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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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."`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -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
20
main.go
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user