1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-21 13:23:07 +01:00

feat: supports auth ttl to override session with DOZZLE_AUTH_TTL env. (#3313)

This commit is contained in:
Amir Raminfar
2024-10-06 06:31:40 -07:00
committed by GitHub
parent 423e4e11a3
commit 567337649e
7 changed files with 71 additions and 6 deletions

View File

@@ -62,6 +62,34 @@ users:
Dozzle uses [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) to generate tokens for authentication. This token is saved in a cookie.
### Extending Authentication Cookie Lifetime
By default, Dozzle uses session cookies which expire when the browser is closed. You can extend the lifetime of the cookie by setting `--auth-ttl` to a duration. Here is an example:
::: code-group
```sh [cli]
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/dozzle/data:/data -p 8080:8080 amir20/dozzle --auth-provider simple --auth-ttl 48h
```
```yaml [docker-compose.yml]
services:
dozzle:
image: amir20/dozzle:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /path/to/dozzle/data:/data
ports:
- 8080:8080
environment:
DOZZLE_AUTH_PROVIDER: simple
DOZZLE_AUTH_TTL: 48h
```
:::
Note that only the duration is supported. You can only use `s`, `m`, `h` for seconds, minutes and hours respectively.
## Generating users.yml
Dozzle has a builtin `generate` command to generate `users.yml`. Here is an example:

View File

@@ -12,11 +12,12 @@ import (
type simpleAuthContext struct {
UserDatabase UserDatabase
tokenAuth *jwtauth.JWTAuth
ttl time.Duration
}
var ErrInvalidCredentials = errors.New("invalid credentials")
func NewSimpleAuth(userDatabase UserDatabase) *simpleAuthContext {
func NewSimpleAuth(userDatabase UserDatabase, ttl time.Duration) *simpleAuthContext {
h := sha256.New()
for _, user := range userDatabase.Users {
h.Write([]byte(user.Password))
@@ -27,6 +28,7 @@ func NewSimpleAuth(userDatabase UserDatabase) *simpleAuthContext {
return &simpleAuthContext{
UserDatabase: userDatabase,
tokenAuth: tokenAuth,
ttl: ttl,
}
}
@@ -36,7 +38,14 @@ func (a *simpleAuthContext) CreateToken(username, password string) (string, erro
return "", ErrInvalidCredentials
}
_, tokenString, err := a.tokenAuth.Encode(map[string]interface{}{"username": user.Username, "email": user.Email, "name": user.Name, "timestamp": time.Now()})
claims := map[string]interface{}{"username": user.Username, "email": user.Email, "name": user.Name}
jwtauth.SetIssuedNow(claims)
if a.ttl > 0 {
jwtauth.SetExpiryIn(claims, a.ttl)
}
_, tokenString, err := a.tokenAuth.Encode(claims)
if err != nil {
return "", err
}

View File

@@ -15,6 +15,7 @@ type Args struct {
Hostname string `arg:"env:DOZZLE_HOSTNAME" help:"sets the hostname for display. This is useful with multiple Dozzle instances."`
Level string `arg:"env:DOZZLE_LEVEL" default:"info" help:"set Dozzle log level. Use debug for more logging."`
AuthProvider string `arg:"--auth-provider,env:DOZZLE_AUTH_PROVIDER" default:"none" help:"sets the auth provider to use. Currently only forward-proxy is supported."`
AuthTTL string `arg:"--auth-ttl,env:DOZZLE_AUTH_TTL" default:"session" help:"sets the TTL for the auth token. Accepts duration values like 12h. Valid time units are s, m, h"`
AuthHeaderUser string `arg:"--auth-header-user,env:DOZZLE_AUTH_HEADER_USER" default:"Remote-User" help:"sets the HTTP Header to use for username in Forward Proxy configuration."`
AuthHeaderEmail string `arg:"--auth-header-email,env:DOZZLE_AUTH_HEADER_EMAIL" default:"Remote-Email" help:"sets the HTTP Header to use for email in Forward Proxy configuration."`
AuthHeaderName string `arg:"--auth-header-name,env:DOZZLE_AUTH_HEADER_NAME" default:"Remote-Name" help:"sets the HTTP Header to use for name in Forward Proxy configuration."`

View File

@@ -12,12 +12,18 @@ func (h *handler) createToken(w http.ResponseWriter, r *http.Request) {
pass := r.PostFormValue("password")
if token, err := h.config.Authorization.Authorizer.CreateToken(user, pass); err == nil {
expires := time.Time{}
if h.config.Authorization.TTL > 0 {
expires = time.Now().Add(h.config.Authorization.TTL)
}
http.SetCookie(w, &http.Cookie{
Name: "jwt",
Value: token,
HttpOnly: true,
Path: "/",
SameSite: http.SameSiteLaxMode,
Expires: expires,
})
log.Info().Str("user", user).Msg("Token created")
w.WriteHeader(http.StatusOK)

View File

@@ -7,6 +7,7 @@ import (
"net/http"
"net/http/httptest"
"strings"
"time"
"testing"
@@ -32,7 +33,7 @@ func Test_createRoutes_simple_redirect(t *testing.T) {
Password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
},
},
}),
}, time.Second*100),
},
})
req, err := http.NewRequest("GET", "/", nil)
@@ -57,7 +58,7 @@ func Test_createRoutes_simple_valid_token(t *testing.T) {
Password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
},
},
}),
}, time.Second*100),
},
})
@@ -102,7 +103,7 @@ func Test_createRoutes_simple_bad_password(t *testing.T) {
Password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
},
},
}),
}, time.Second*100),
},
})

View File

@@ -2,6 +2,7 @@ package web
import (
"io/fs"
"time"
"net/http"
"strings"
@@ -36,6 +37,7 @@ type Config struct {
type Authorization struct {
Provider AuthProvider
Authorizer Authorizer
TTL time.Duration
}
type Authorizer interface {

20
main.go
View File

@@ -229,7 +229,24 @@ func createServer(args cli.Args, multiHostService *docker_support.MultiHostServi
}
log.Debug().Int("users", len(db.Users)).Msg("Loaded users")
authorizer = auth.NewSimpleAuth(db)
ttl := time.Duration(0)
if args.AuthTTL != "session" {
ttl, err = time.ParseDuration(args.AuthTTL)
if err != nil {
log.Fatal().Err(err).Msg("Could not parse auth ttl")
}
}
authorizer = auth.NewSimpleAuth(db, ttl)
}
authTTL := time.Duration(0)
if args.AuthTTL != "session" {
ttl, err := time.ParseDuration(args.AuthTTL)
if err != nil {
log.Fatal().Err(err).Msg("Could not parse auth ttl")
}
authTTL = ttl
}
config := web.Config{
@@ -242,6 +259,7 @@ func createServer(args cli.Args, multiHostService *docker_support.MultiHostServi
Authorization: web.Authorization{
Provider: provider,
Authorizer: authorizer,
TTL: authTTL,
},
EnableActions: args.EnableActions,
}