mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-24 06:28:42 +01:00
feat: adds generate subcommand to auto generate users.yml (#2939)
This commit is contained in:
2
docs/components.d.ts
vendored
2
docs/components.d.ts
vendored
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// Generated by unplugin-vue-components
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
BrowserWindow: typeof import('./.vitepress/theme/components/BrowserWindow.vue')['default']
|
BrowserWindow: typeof import('./.vitepress/theme/components/BrowserWindow.vue')['default']
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ users:
|
|||||||
email: me@email.net
|
email: me@email.net
|
||||||
```
|
```
|
||||||
|
|
||||||
Dozzle uses `email` to generate avatars using [Gravatar](https://gravatar.com/). It is optional.
|
> [!TIP]
|
||||||
|
> This file can be generated with `docker run amir20/dozzle generate` with v6.6.x. See [below](#generating-users-yml) for more details.
|
||||||
|
|
||||||
The password is hashed using `sha256` which can be generated with `echo -n 'secret-password' | shasum -a 256` or `echo -n 'secret-password' | sha256sum` on linux.
|
Dozzle uses `email` to generate avatars using [Gravatar](https://gravatar.com/). It is optional. The password is hashed using `sha256` which can be generated with `echo -n 'secret-password' | shasum -a 256` or `echo -n 'secret-password' | sha256sum` on linux.
|
||||||
|
|
||||||
You will need to mount this file for Dozzle to find it. Here is an example:
|
You will need to mount this file for Dozzle to find it. Here is an example:
|
||||||
|
|
||||||
@@ -64,6 +65,16 @@ users:
|
|||||||
|
|
||||||
Dozzle uses [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) to generate tokens for authentication. This token is saved in a cookie.
|
Dozzle uses [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) to generate tokens for authentication. This token is saved in a cookie.
|
||||||
|
|
||||||
|
### Generating users.yml
|
||||||
|
|
||||||
|
Starting with version `v6.6.x`, Dozzle has a builtin `generate` sub-command which can be used to create a `users.yml`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run amir20/dozzle generate admin --password password --email test@email.net --name "John Doe" > users.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, `admin` is the username. Email and name are optional but recommended to display accurate avatars. `docker run amir20/dozzle generate --help` displays all options.
|
||||||
|
|
||||||
## Forward Proxy
|
## Forward Proxy
|
||||||
|
|
||||||
Dozzle can be configured to read proxy headers by setting `--auth-provider` to `forward-proxy`.
|
Dozzle can be configured to read proxy headers by setting `--auth-provider` to `forward-proxy`.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -16,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username" yaml:"-"`
|
||||||
Email string `json:"email" yaml:"email"`
|
Email string `json:"email" yaml:"email"`
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
Password string `json:"-" yaml:"password"`
|
Password string `json:"-" yaml:"password"`
|
||||||
@@ -56,6 +57,24 @@ func ReadUsersFromFile(path string) (UserDatabase, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateUsers(user User, hashPassword bool) *bytes.Buffer {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if hashPassword {
|
||||||
|
user.Password = sha256sum(user.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
users := UserDatabase{
|
||||||
|
Users: map[string]*User{
|
||||||
|
user.Username: &user,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml.NewEncoder(buffer).Encode(users)
|
||||||
|
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
func decodeUsersFromFile(path string) (UserDatabase, error) {
|
func decodeUsersFromFile(path string) (UserDatabase, error) {
|
||||||
users := UserDatabase{}
|
users := UserDatabase{}
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
|
|||||||
44
main.go
44
main.go
@@ -33,8 +33,6 @@ type args struct {
|
|||||||
Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`
|
Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`
|
||||||
Hostname string `arg:"env:DOZZLE_HOSTNAME" help:"sets the hostname for display. This is useful with multiple Dozzle instances."`
|
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."`
|
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"`
|
|
||||||
AuthProvider string `arg:"--auth-provider,env:DOZZLE_AUTH_PROVIDER" default:"none" help:"sets the auth provider to use. Currently only forward-proxy is supported."`
|
AuthProvider string `arg:"--auth-provider,env:DOZZLE_AUTH_PROVIDER" default:"none" help:"sets the auth provider to use. Currently only forward-proxy is supported."`
|
||||||
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."`
|
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."`
|
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."`
|
||||||
@@ -43,14 +41,23 @@ type args struct {
|
|||||||
EnableActions bool `arg:"--enable-actions,env:DOZZLE_ENABLE_ACTIONS" default:"false" help:"enables essential actions on containers from the web interface."`
|
EnableActions bool `arg:"--enable-actions,env:DOZZLE_ENABLE_ACTIONS" default:"false" help:"enables essential actions on containers from the web interface."`
|
||||||
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."`
|
||||||
Filter map[string][]string `arg:"-"`
|
Filter map[string][]string `arg:"-"`
|
||||||
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running."`
|
|
||||||
RemoteHost []string `arg:"env:DOZZLE_REMOTE_HOST,--remote-host,separate" help:"list of hosts to connect remotely"`
|
RemoteHost []string `arg:"env:DOZZLE_REMOTE_HOST,--remote-host,separate" help:"list of hosts to connect remotely"`
|
||||||
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"`
|
||||||
|
|
||||||
|
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running"`
|
||||||
|
Generate *GenerateCmd `arg:"subcommand:generate" help:"generates a configuration file for simple auth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthcheckCmd struct {
|
type HealthcheckCmd struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GenerateCmd struct {
|
||||||
|
Username string `arg:"positional"`
|
||||||
|
Password string `arg:"--password, -p" help:"sets the password for the user"`
|
||||||
|
Name string `arg:"--name, -n" help:"sets the display name for the user"`
|
||||||
|
Email string `arg:"--email, -e" help:"sets the email for the user"`
|
||||||
|
}
|
||||||
|
|
||||||
func (args) Version() string {
|
func (args) Version() string {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
@@ -59,12 +66,32 @@ func (args) Version() string {
|
|||||||
var content embed.FS
|
var content embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
args := parseArgs()
|
args, subcommand := parseArgs()
|
||||||
validateEnvVars()
|
validateEnvVars()
|
||||||
if args.Healthcheck != nil {
|
if subcommand != nil {
|
||||||
|
switch subcommand.(type) {
|
||||||
|
case *HealthcheckCmd:
|
||||||
if err := healthcheck.HttpRequest(args.Addr, args.Base); err != nil {
|
if err := healthcheck.HttpRequest(args.Addr, args.Base); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *GenerateCmd:
|
||||||
|
if args.Generate.Username == "" || args.Generate.Password == "" {
|
||||||
|
log.Fatal("Username and password are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := auth.GenerateUsers(auth.User{
|
||||||
|
Username: args.Generate.Username,
|
||||||
|
Password: args.Generate.Password,
|
||||||
|
Name: args.Generate.Name,
|
||||||
|
Email: args.Generate.Email,
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
if _, err := os.Stdout.Write(buffer.Bytes()); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +284,7 @@ func createLocalClient(args args, localClientFactory func(map[string][]string) (
|
|||||||
return nil, errors.New("could not connect to local Docker Engine")
|
return nil, errors.New("could not connect to local Docker Engine")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs() args {
|
func parseArgs() (args, interface{}) {
|
||||||
var args args
|
var args args
|
||||||
parser := arg.MustParse(&args)
|
parser := arg.MustParse(&args)
|
||||||
|
|
||||||
@@ -275,10 +302,7 @@ func parseArgs() args {
|
|||||||
args.Filter[key] = append(args.Filter[key], val)
|
args.Filter[key] = append(args.Filter[key], val)
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Username != "" || args.Password != "" {
|
return args, parser.Subcommand()
|
||||||
log.Fatal("Using --username and --password is removed on v6.x. See https://github.com/amir20/dozzle/issues/2630 for details.")
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureLogger(level string) {
|
func configureLogger(level string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user