diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md index aed819b6..c97d10aa 100644 --- a/docs/guide/authentication.md +++ b/docs/guide/authentication.md @@ -178,6 +178,39 @@ notifier: 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 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 diff --git a/internal/auth/proxy.go b/internal/auth/proxy.go index a34d55f8..e6875dfe 100644 --- a/internal/auth/proxy.go +++ b/internal/auth/proxy.go @@ -15,6 +15,9 @@ type contextKey string const remoteUser contextKey = "remoteUser" type proxyAuthContext struct { + headerUser string + headerEmail string + headerName string } func hashEmail(email string) string { @@ -25,14 +28,18 @@ func hashEmail(email string) string { return hex.EncodeToString(hash[:]) } -func NewForwardProxyAuth() *proxyAuthContext { - return &proxyAuthContext{} +func NewForwardProxyAuth(user, email, name string) *proxyAuthContext { + return &proxyAuthContext{ + headerUser: user, + headerEmail: email, + headerName: name, + } } func (p *proxyAuthContext) AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Remote-User") != "" { - user := newUser(r.Header.Get("Remote-User"), r.Header.Get("Remote-Email"), r.Header.Get("Remote-Name")) + if r.Header.Get(p.headerUser) != "" { + user := newUser(r.Header.Get(p.headerUser), r.Header.Get(p.headerEmail), r.Header.Get(p.headerName)) ctx := context.WithValue(r.Context(), remoteUser, user) next.ServeHTTP(w, r.WithContext(ctx)) } else { diff --git a/internal/web/routes_proxy_test.go b/internal/web/routes_proxy_test.go index 8ffd7d98..a62f6c47 100644 --- a/internal/web/routes_proxy_test.go +++ b/internal/web/routes_proxy_test.go @@ -20,7 +20,7 @@ func Test_createRoutes_proxy_missing_headers(t *testing.T) { handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/", Authorization: Authorization{ Provider: FORWARD_PROXY, - Authorizer: auth.NewForwardProxyAuth(), + Authorizer: auth.NewForwardProxyAuth("Remote-User", "Remote-Email", "Remote-Name"), }, }) 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: "/", Authorization: Authorization{ Provider: FORWARD_PROXY, - Authorizer: auth.NewForwardProxyAuth(), + Authorizer: auth.NewForwardProxyAuth("Remote-User", "Remote-Email", "Remote-Name"), }, }) req, err := http.NewRequest("GET", "/", nil) diff --git a/main.go b/main.go index 3b45152f..59196b8a 100644 --- a/main.go +++ b/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."` Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username 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"` 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."` @@ -168,7 +171,7 @@ func createServer(args args, clients map[string]web.DockerClient) *http.Server { var authorizer web.Authorizer if args.AuthProvider == "forward-proxy" { provider = web.FORWARD_PROXY - authorizer = auth.NewForwardProxyAuth() + authorizer = auth.NewForwardProxyAuth(args.AuthHeaderUser, args.AuthHeaderEmail, args.AuthHeaderName) } else if args.AuthProvider == "simple" { provider = web.SIMPLE