mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 21:33:18 +01:00
feat: add support for custom headers for forward-proxy auth (#2642)
This commit is contained in:
@@ -178,6 +178,39 @@ notifier:
|
|||||||
|
|
||||||
Valid SSL keys are required because Authelia only supports SSL.
|
Valid SSL keys are required because Authelia only supports SSL.
|
||||||
|
|
||||||
|
### Setting up Dozzle with Cloudflare Zero Trust
|
||||||
|
|
||||||
|
Cloudflare Zero Trust is a service for authenticated access to selfhosted
|
||||||
|
software. This section defines how Dozzle can be setup to use Cloudflare Zero
|
||||||
|
Trust for authentication.
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
|
```yaml [docker-compose.yml]
|
||||||
|
version: "3.3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
dozzle:
|
||||||
|
image: amir20/dozzle:latest
|
||||||
|
networks:
|
||||||
|
- net
|
||||||
|
environment:
|
||||||
|
DOZZLE_AUTH_PROVIDER: forward-proxy
|
||||||
|
DOZZLE_AUTH_HEADER_USER: Cf-Access-Authenticated-User-Email
|
||||||
|
DOZZLE_AUTH_HEADER_EMAIL: Cf-Access-Authenticated-User-Email
|
||||||
|
DOZZLE_AUTH_HEADER_NAME: Cf-Access-Authenticated-User-Email
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
expose:
|
||||||
|
- 8080
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
After running the Dozzle container, configure the Application in Cloudflare Zero
|
||||||
|
Trust dashboard by following the
|
||||||
|
[guide](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/)
|
||||||
|
here.
|
||||||
|
|
||||||
## File Based User Management
|
## File Based User Management
|
||||||
|
|
||||||
Dozzle supports multi-user authentication by setting `--auth-provider` to `simple`. In this mode, Dozzle will try to read `/data/users.yml`. The content of the file looks like
|
Dozzle supports multi-user authentication by setting `--auth-provider` to `simple`. In this mode, Dozzle will try to read `/data/users.yml`. The content of the file looks like
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ type contextKey string
|
|||||||
const remoteUser contextKey = "remoteUser"
|
const remoteUser contextKey = "remoteUser"
|
||||||
|
|
||||||
type proxyAuthContext struct {
|
type proxyAuthContext struct {
|
||||||
|
headerUser string
|
||||||
|
headerEmail string
|
||||||
|
headerName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashEmail(email string) string {
|
func hashEmail(email string) string {
|
||||||
@@ -25,14 +28,18 @@ func hashEmail(email string) string {
|
|||||||
return hex.EncodeToString(hash[:])
|
return hex.EncodeToString(hash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewForwardProxyAuth() *proxyAuthContext {
|
func NewForwardProxyAuth(user, email, name string) *proxyAuthContext {
|
||||||
return &proxyAuthContext{}
|
return &proxyAuthContext{
|
||||||
|
headerUser: user,
|
||||||
|
headerEmail: email,
|
||||||
|
headerName: name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler {
|
func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Header.Get("Remote-User") != "" {
|
if r.Header.Get(p.headerUser) != "" {
|
||||||
user := newUser(r.Header.Get("Remote-User"), r.Header.Get("Remote-Email"), r.Header.Get("Remote-Name"))
|
user := newUser(r.Header.Get(p.headerUser), r.Header.Get(p.headerEmail), r.Header.Get(p.headerName))
|
||||||
ctx := context.WithValue(r.Context(), remoteUser, user)
|
ctx := context.WithValue(r.Context(), remoteUser, user)
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func Test_createRoutes_proxy_missing_headers(t *testing.T) {
|
|||||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/",
|
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/",
|
||||||
Authorization: Authorization{
|
Authorization: Authorization{
|
||||||
Provider: FORWARD_PROXY,
|
Provider: FORWARD_PROXY,
|
||||||
Authorizer: auth.NewForwardProxyAuth(),
|
Authorizer: auth.NewForwardProxyAuth("Remote-User", "Remote-Email", "Remote-Name"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
req, err := http.NewRequest("GET", "/", nil)
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
@@ -39,7 +39,7 @@ func Test_createRoutes_proxy_happy(t *testing.T) {
|
|||||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/",
|
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/",
|
||||||
Authorization: Authorization{
|
Authorization: Authorization{
|
||||||
Provider: FORWARD_PROXY,
|
Provider: FORWARD_PROXY,
|
||||||
Authorizer: auth.NewForwardProxyAuth(),
|
Authorizer: auth.NewForwardProxyAuth("Remote-User", "Remote-Email", "Remote-Name"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
req, err := http.NewRequest("GET", "/", nil)
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
|
|||||||
5
main.go
5
main.go
@@ -44,6 +44,9 @@ type args struct {
|
|||||||
Level string `arg:"env:DOZZLE_LEVEL" default:"info" help:"set Dozzle log level. Use debug for more logging."`
|
Level string `arg:"env:DOZZLE_LEVEL" default:"info" help:"set Dozzle log level. Use debug for more logging."`
|
||||||
Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username for auth."`
|
Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username for auth."`
|
||||||
Password string `arg:"env:DOZZLE_PASSWORD" help:"sets password for auth"`
|
Password string `arg:"env:DOZZLE_PASSWORD" help:"sets password for auth"`
|
||||||
|
AuthHeaderUser string `arg:"env:DOZZLE_AUTH_HEADER_USER" default:"Remote-User" help:"sets the HTTP Header to use for username in Forward Proxy configuration."`
|
||||||
|
AuthHeaderEmail string `arg:"env:DOZZLE_AUTH_HEADER_EMAIL" default:"Remote-Email" help:"sets the HTTP Header to use for email in Forward Proxy configuration."`
|
||||||
|
AuthHeaderName string `arg:"env:DOZZLE_AUTH_HEADER_NAME" default:"Remote-Name" help:"sets the HTTP Header to use for name in Forward Proxy configuration."`
|
||||||
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS" help:"disables anonymous analytics"`
|
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS" help:"disables anonymous analytics"`
|
||||||
WaitForDockerSeconds int `arg:"--wait-for-docker-seconds,env:DOZZLE_WAIT_FOR_DOCKER_SECONDS" help:"wait for docker to be available for at most this many seconds before starting the server."`
|
WaitForDockerSeconds int `arg:"--wait-for-docker-seconds,env:DOZZLE_WAIT_FOR_DOCKER_SECONDS" help:"wait for docker to be available for at most this many seconds before starting the server."`
|
||||||
FilterStrings []string `arg:"env:DOZZLE_FILTER,--filter,separate" help:"filters docker containers using Docker syntax."`
|
FilterStrings []string `arg:"env:DOZZLE_FILTER,--filter,separate" help:"filters docker containers using Docker syntax."`
|
||||||
@@ -168,7 +171,7 @@ func createServer(args args, clients map[string]web.DockerClient) *http.Server {
|
|||||||
var authorizer web.Authorizer
|
var authorizer web.Authorizer
|
||||||
if args.AuthProvider == "forward-proxy" {
|
if args.AuthProvider == "forward-proxy" {
|
||||||
provider = web.FORWARD_PROXY
|
provider = web.FORWARD_PROXY
|
||||||
authorizer = auth.NewForwardProxyAuth()
|
authorizer = auth.NewForwardProxyAuth(args.AuthHeaderUser, args.AuthHeaderEmail, args.AuthHeaderName)
|
||||||
} else if args.AuthProvider == "simple" {
|
} else if args.AuthProvider == "simple" {
|
||||||
provider = web.SIMPLE
|
provider = web.SIMPLE
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user