Dotege is a tool to automatically generate configuration files from templates based on running docker containers. It also obtains SSL certificates for domains using Let’s Encrypt, and can send a signal (such as HUP) to another container when the template changes.
Out of the box it supports writing a HAProxy configuration file with
appropriate entries for all containers with com.chameth.*
labels.
This enables automatic reverse proxying to any container with the
relevant networks.
Dotege is configured using environment variables:
DOTEGE_CERTIFICATE_DEPLOYMENT
-
Determines how Dotege will deploy certificates. Valid options are:
-
disabled
: Dotege will not request certificates or deploy them to disk. -
combined
: The certificate and private key will be written to one.pem
file. Default. -
splitkeys
: The certificate will be written to a.pem
file, and the private key to a.key
file.
If certificate deployment is disabled, no other options in this section are used.
-
DOTEGE_CERT_DESTINATION
-
The folder where certificates will be placed. Defaults to
/data/certs
. DOTEGE_CERT_GID
-
If specified, certificate files will be
chowned
to this numeric group ID. DOTEGE_CERT_MODE
-
The file mode that should be applied to certificate files. Defaults to
0600
.Take care when configuring this in a YAML file as YAML supports octal integers, resulting in the wrong value being passed:
DOTEGE_CERT_MODE: 0640 # Passes a mode string of "416" to Dotege due to octal->decimal conversion DOTEGE_CERT_MODE: 640 # Works as expected DOTEGE_CERT_MODE: "0640" # Works as expected
DOTEGE_CERT_UID
-
If specified, certificate files will be
chowned
to this numeric user ID. DOTEGE_DNS_PROVIDER
-
The DNS provider to use. Must be one supported by Lego. The DNS provider will also be configured using environmental variables, as documented by the Lego project. Required if certificate deployment is enabled.
DOTEGE_ACME_CACHE_FILE
-
The path to a JSON file to store ACME credentials and certificates. This file will contain the private keys for all certificates generated by Dotege, so must not be accessible to other users or processes. Defaults to
/data/config/certs.json
. DOTEGE_ACME_EMAIL
-
The e-mail address to provide to the ACME service for updates, renewal reminders, etc. Required if certificate deployment is enabled.
DOTEGE_ACME_ENDPOINT
-
The ACME server to request certificates from. Defaults to the Let’s Encrypt production server at https://acme-v02.api.letsencrypt.org/directory. For staging, this can be set to https://acme-staging-v02.api.letsencrypt.org/directory.
DOTEGE_ACME_KEY_TYPE
-
The key type to use for private keys when generating a certificate using ACME. Valid values are:
-
P256
for EC256 -
P384
for EC384 -
2048
for RSA-2048 -
4096
for RSA-4096 -
8192
for RSA-8192The default value is
P384
.
-
DOTEGE_WILDCARD_DOMAINS
-
A space or comma separated list of domains that should use wildcard certificates. Defaults to an empty list.
DOTEGE_DEBUG
-
Enables advanced logging of certain information in Dotege. Comma-separated list of topics to enable logging for. Optional. Valid options are:
-
containers
- containers that are seen to start/stop -
headers
- custom headers (com.chameth.headers
labels) -
hostnames
- mapping of containers to hostnames
-
DOTEGE_PROXYTAG
-
Only containers with a matching
com.chameth.proxytag
label will be processed by Dotege. This allows you to run multiple instances that handle separate containers. If not specified, any container without acom.chameth.proxytag
label will be included. DOTEGE_SIGNAL_CONTAINER
-
The name of a container that should be sent a signal when the template or certificates are changed. No signal is sent if not specified.
DOTEGE_SIGNAL_TYPE
-
The type of signal to send to the
DOTEGE_SIGNAL_CONTAINER
. Defaults toHUP
. DOTEGE_TEMPLATE_DESTINATION
-
Location to write the templated configuration file to. Defaults to
/data/output/haproxy.cfg
. DOTEGE_TEMPLATE_SOURCE
-
Path to a template to use to generate configuration. Defaults to
./templates/haproxy.cfg.tpl
, which is a bundled basic template for generating HAProxy configurations. DOTEGE_USERS
-
A YAML (or JSON) list of users, their password hashes, and their group memberships, to use for ACLs. See Using ACLs below for detailed usage.
Dotege operates by parsing labels applied to docker containers. It understands the following:
com.chameth.auth
-
Specifies the name of an auth group (which must be defined in the DOTEGE_USERS env variable) that users are required to be in to access the container. See Using ACLs below for detailed usage.
com.chameth.headers
-
Specifies response headers to be sent to the client for all requests to the container. Any label with this as a prefix will be used, so multiple headers can be specified as
com.chameth.headers.1
, orcom.chameth.headers-frame-options
, for example. com.chameth.proxy
-
The port on which the container is listening for requests. If
com.chameth.vhost
is specified andcom.chameth.proxy
is not and the container exposes a single non-bound port then Dotege will automatically use that port. That means you do not need to manually label the port for an nginx server, for instance, as the nginx image exposes port 80 (only). com.chameth.proxytag
-
Arbitrary tag to control which containers an instance of Dotege will deal with. If specified, the container will be ignored by any instance of Dotege that does not have the same value passed in using the
DOTEGE_PROXYTAG
env var. Note this should also be set on the container specified in DOTEGE_SIGNAL_CONTAINER if set, or it will be ignored and not restarted. com.chameth.vhost
-
Comma- or space-delimited list of hostnames that the container will handle requests for. Certificates will have the first host as the subject, and any additional hosts will be alternate names. Certificates are only reused if all hostnames match.
version: '3.5'
services:
dotege:
image: ghcr.io/csmith/dotege
restart: always
volumes:
- data:/data/config
- certs:/data/certs
- config:/data/output
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DOTEGE_ACME_EMAIL=email@address
- DOTEGE_DNS_PROVIDER=httpreq
- DOTEGE_SIGNAL_CONTAINER=dotege_haproxy_1
- DOTEGE_SIGNAL_TYPE=USR2
- DOTEGE_WILDCARD_DOMAINS=mydomain.com
- HTTPREQ_ENDPOINT=https://example.com/
- HTTPREQ_USERNAME=user@name
- HTTPREQ_PASSWORD=p@ssw0rd
haproxy:
image: haproxy:2.0.1
restart: always
volumes:
- config:/usr/local/etc/haproxy:ro
- certs:/certs:ro
ports:
- 443:443
- 80:80
networks:
- web
networks:
web:
external: true
volumes:
data:
certs:
config:
This creates an instance of Dotege, configured to use httpreq
to perform DNS
operations in order to generate SSL certificates. You can see the list of
supported providers and their required environment variables in the
Lego docs.
The haproxy instance has read-only access to the config and certs volumes that
will be populated by Dotege, and Dotege will send it the USR2
signal whenever
the config or certs change. With the default haproxy image this will cause it
to reload the configuration.
Container names must be resolvable from the haproxy container with the default template. This means the haproxy container should be on the same network as the containers it’s proxying to. I recommend creating a global 'web' network (or similar) that all web-facing containers sit in.
Dotege, with the default HAProxy template, allows you to specify users in an environment variable and for individual containers to then require a specific group of users using labels.
Dotege expects the DOTEGE_USERS environment variable to contain a list of users, and each user must have a "name" and "password" property, and an optional "groups" property. For example if we want our user list to look like this:
- name: chris
password: hashedPasswordHere
groups: [admins]
- name: bob
password: hashedPasswordHere
Then we’d use the following environment variable:
DOTEGE_USERS="- name: chris\n password: hashedPasswordHere\n groups: [admins]\n- name: bob\n password: hashedPasswordHere"
Alternatively, removing the need for line breaks:
DOTEGE_USERS="[{name: chris, password: hashedPasswordHere, groups: [admins]}, {name: bob, password: hashedPasswordHere}]"
If you are using configuring the container using YAML (e.g. in a docker-compose file), you can use the pipe operator to treat YAML content as a scalar, which is vastly easier to use:
services:
dotege:
environment:
DOTEGE_USERS: |
- name: chris
password: hashedPasswordHere
groups: [admins]
- name: bob
password: hashedPasswordHere
For HAProxy, passwords are hashed using the crypt(3) system call - the easiest
way to generate them is using the mkpassword
utility.
NB: If you are using docker-compose then any $
characters in the hashed password
will need to be escaped by doubling them up (i.e. replace $
with $$
).
To require basic authentication, the container should have the com.chameth.auth
label.
The label should be a space separated list of groups that are allowed access; if it
is blank then all defined users are allowed.
For example:
services:
public:
labels:
com.chameth.vhost: "public.example.com"
private1:
labels:
com.chameth.vhost: "private1.example.com"
com.chameth.auth: ""
private2:
labels:
com.chameth.vhost: "private2.example.com"
com.chameth.auth: "admins"
Of these services, public
won’t require any authentication. private1
will
require any valid user (so from our example above, either "chris" or "bob"),
while private2
will require a user in the "admins" group (so from our example
above only "chris" would be allowed access).
Dotege comes with two templates out of the box - one to create a working HAProxy config, and one to output a list of domains suitable for use with a tool like Dehydrated.
Dotege uses Go’s built in text/template package which provides extensive documentation for the template syntax itself. If you’ve used Smarty, Jinja or other templating systems the syntax should look pretty similar.
Dotege provides the following data to templates:
-
Containers - a map of container IDs to the container’s details:
-
Id - the ID of the container
-
Headers - map of header names to values from
com.chameth.headers
labels -
Labels - map of all label names to values
-
Name - the name of the container
-
Port - the port the container accepts traffic on, or -1 if it couldn’t be determined
-
Ports - all ports exposed by the container
-
ShouldProxy - boolean indicating whether the container has a hostname and port
-
-
Groups - a list of unique group names specified in the
DOTEGE_USERS
key -
Hostnames - a map of known primary hostnames to their details:
-
Alternatives - a map of alternate names for this hostname
-
AuthGroup - the name of the group users must be a member of to access this hostname (if RequiresAuth is true)
-
Containers - all containers that accept traffic for this hostname
-
Headers - map of header names to values from
com.chameth.headers
labels -
Name - the name of the primary hostname
-
RequiresAuth - boolean indicating whether authentication is required
-
-
Users - a list of users defined in the
DOTEGE_USERS
key-
Name - the username of the user
-
Password - the (hashed) password of the user
-
Groups - list of groups the user belongs to
-
Most templates will want to act on the Hostnames
data primarily, as this groups up
containers that accept traffic to the same domains, and avoids having to deal with
containers that aren’t configured for use with Dotege.
If you know in advance you will only use a single DNS provider, you can use build tags to include only support
for that provider in the binary. For example to support only the httpreq
provider you can build with
go build -tags lego_httpreq
. See the legotapas project for more
info.
Contributions are welcome! Please raise an issue if you have any feature requests or spot a bug, or open a pull request if you want to suggest any code changes.
Dotege is licensed under the MIT licence. A full copy of the licence is available in the LICENCE file.
Dotege makes use of a number of third-party libraries. See the go.mod file
for a list of direct dependencies. Users of the docker image will find a copy of the
relevant licence and notice files under the /notices
directory in the image.