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

feat: support setting the path to certs (#4198)

This commit is contained in:
Amir Raminfar
2025-10-19 12:15:50 -07:00
committed by GitHub
parent 074f402d0f
commit 911f785e2c
8 changed files with 54 additions and 12 deletions

View File

@@ -35,7 +35,6 @@ services:
> [!NOTE] Docker Socket Proxy users > [!NOTE] Docker Socket Proxy users
> If you are using a remote agent you **CANNOT** add a socket proxy on top of the agent. Dozzle agents **REPLACE** using a proxy, see [Remote Hosts](/guide/remote-hosts.md) for more info and how to use a socket proxy instead of an agent. > If you are using a remote agent you **CANNOT** add a socket proxy on top of the agent. Dozzle agents **REPLACE** using a proxy, see [Remote Hosts](/guide/remote-hosts.md) for more info and how to use a socket proxy instead of an agent.
The agent will start and listen on port `7007`. You can connect to the agent using the Dozzle UI by providing the agent's IP address and port. The agent will only show the containers that are available on the host where the agent is running. The agent will start and listen on port `7007`. You can connect to the agent using the Dozzle UI by providing the agent's IP address and port. The agent will only show the containers that are available on the host where the agent is running.
> [!TIP] > [!TIP]
@@ -153,7 +152,9 @@ This will restrict the agent to displaying only containers with the label `color
By default, Dozzle uses self-signed certificates for communication between agents. This is a private certificate which is only valid to other Dozzle instances. This is secure and recommended for most use cases. However, if Dozzle is exposed externally and an attacker knows exactly which port the agent is running on, then they can set up their own Dozzle instance and connect to the agent. To prevent this, you can provide your own certificates. By default, Dozzle uses self-signed certificates for communication between agents. This is a private certificate which is only valid to other Dozzle instances. This is secure and recommended for most use cases. However, if Dozzle is exposed externally and an attacker knows exactly which port the agent is running on, then they can set up their own Dozzle instance and connect to the agent. To prevent this, you can provide your own certificates.
To provide custom certificates, you need to mount or use secrets to provide the certificates. Here is an example: To provide custom certificates, you need to mount or use secrets to provide the certificates. By default, Dozzle looks for certificates at `/dozzle_cert.pem` and `/dozzle_key.pem`, but you can customize these paths using the `--cert` and `--key` flags or the `DOZZLE_CERT` and `DOZZLE_KEY` environment variables.
Here is an example using the default paths:
```yml ```yml
services: services:
@@ -176,10 +177,49 @@ secrets:
file: ./key.pem file: ./key.pem
``` ```
Or using custom paths with environment variables:
```yml
services:
agent:
image: amir20/dozzle:latest
command: agent
environment:
- DOZZLE_CERT=/certs/my-cert.pem
- DOZZLE_KEY=/certs/my-key.pem
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./certs:/certs
ports:
- 7007:7007
```
Or using command-line flags:
::: code-group
```sh
docker run -v /var/run/docker.sock:/var/run/docker.sock -v ./certs:/certs -p 7007:7007 amir20/dozzle:latest agent --cert /certs/my-cert.pem --key /certs/my-key.pem
```
```yaml [docker-compose.yml]
services:
agent:
image: amir20/dozzle:latest
command: agent --cert /certs/my-cert.pem --key /certs/my-key.pem
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./certs:/certs
ports:
- 7007:7007
```
:::
> [!TIP] > [!TIP]
> Docker secrets are preferred for providing certificates. They can be created using `docker secret create` command or as the example above using `docker-compose.yml`. The same certificates should be provided to the Dozzle instance connecting to the agent. > Docker secrets are preferred for providing certificates. They can be created using `docker secret create` command or as the example above using `docker-compose.yml`. The same certificates should be provided to the Dozzle instance connecting to the agent.
This will mount the `cert.pem` and `key.pem` files to the agent. The agent will use these certificates for communication. The same certificates should be provided to the Dozzle instance connecting to the agent. This will mount the certificate and key files to the agent. The agent will use these certificates for communication. The same certificates should be provided to the Dozzle instance connecting to the agent.
To generate certificates, you can use the following command: To generate certificates, you can use the following command:

View File

@@ -27,7 +27,7 @@ func (a *AgentCmd) Run(args Args, embeddedCerts embed.FS) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create docker client: %w", err) return fmt.Errorf("failed to create docker client: %w", err)
} }
certs, err := ReadCertificates(embeddedCerts) certs, err := ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to read certificates: %w", err) return fmt.Errorf("failed to read certificates: %w", err)
} }

View File

@@ -14,7 +14,7 @@ type AgentTestCmd struct {
} }
func (at *AgentTestCmd) Run(args Args, embeddedCerts embed.FS) error { func (at *AgentTestCmd) Run(args Args, embeddedCerts embed.FS) error {
certs, err := ReadCertificates(embeddedCerts) certs, err := ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)
if err != nil { if err != nil {
return fmt.Errorf("error reading certificates: %w", err) return fmt.Errorf("error reading certificates: %w", err)
} }

View File

@@ -36,6 +36,8 @@ type Args struct {
TimeoutString string `arg:"--timeout,env:DOZZLE_TIMEOUT" default:"10s" help:"sets the timeout for docker client"` TimeoutString string `arg:"--timeout,env:DOZZLE_TIMEOUT" default:"10s" help:"sets the timeout for docker client"`
Timeout time.Duration `arg:"-"` Timeout time.Duration `arg:"-"`
Namespace []string `arg:"env:DOZZLE_NAMESPACE" help:"sets the namespace to use in k8s"` Namespace []string `arg:"env:DOZZLE_NAMESPACE" help:"sets the namespace to use in k8s"`
CertPath string `arg:"--cert,env:DOZZLE_CERT" default:"dozzle_cert.pem" help:"path to custom TLS certificate"`
KeyPath string `arg:"--key,env:DOZZLE_KEY" default:"dozzle_key.pem" help:"path to custom TLS key"`
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running"` 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"` Generate *GenerateCmd `arg:"subcommand:generate" help:"generates a configuration file for simple auth"`
Agent *AgentCmd `arg:"subcommand:agent" help:"starts the agent"` Agent *AgentCmd `arg:"subcommand:agent" help:"starts the agent"`

View File

@@ -8,13 +8,13 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func ReadCertificates(certs embed.FS) (tls.Certificate, error) { func ReadCertificates(certs embed.FS, certPath, keyPath string) (tls.Certificate, error) {
if pair, err := tls.LoadX509KeyPair("dozzle_cert.pem", "dozzle_key.pem"); err == nil { if pair, err := tls.LoadX509KeyPair(certPath, keyPath); err == nil {
log.Info().Msg("Loaded custom dozzle certificate and key") log.Info().Str("cert", certPath).Str("key", keyPath).Msg("Loaded custom dozzle certificate and key")
return pair, nil return pair, nil
} else { } else {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
log.Fatal().Err(err).Msg("Failed to load custom dozzle certificate and key. Stopping...") log.Fatal().Err(err).Str("cert", certPath).Str("key", keyPath).Msg("Failed to load custom dozzle certificate and key. Stopping...")
} }
} }

View File

@@ -51,7 +51,7 @@ func CreateMultiHostService(embeddedCerts embed.FS, args Args) *docker_support.M
go StartEvent(args, "server", localClient, "") go StartEvent(args, "server", localClient, "")
} }
certs, err := ReadCertificates(embeddedCerts) certs, err := ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not read certificates") log.Fatal().Err(err).Msg("Could not read certificates")
} }

View File

@@ -20,7 +20,7 @@ func (h *HealthcheckCmd) Run(args Args, embeddedCerts embed.FS) error {
return fmt.Errorf("failed to read file: %w", err) return fmt.Errorf("failed to read file: %w", err)
} }
agentAddress := string(data) agentAddress := string(data)
certs, err := ReadCertificates(embeddedCerts) certs, err := ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to read certificates: %w", err) return fmt.Errorf("failed to read certificates: %w", err)
} }

View File

@@ -68,7 +68,7 @@ func main() {
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not create docker client") log.Fatal().Err(err).Msg("Could not create docker client")
} }
certs, err := cli.ReadCertificates(certs) certs, err := cli.ReadCertificates(certs, args.CertPath, args.KeyPath)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Could not read certificates") log.Fatal().Err(err).Msg("Could not read certificates")
} }