Skip to content

Latest commit

 

History

History
392 lines (306 loc) · 31.2 KB

README.md

File metadata and controls

392 lines (306 loc) · 31.2 KB

Caddy Docker Custom Builds

GitHub release (latest SemVer) GitHub build status License

Caddy takes a modular approach to building Docker images, allowing users to include only the modules they need. This repository aims to provide flexibility and convenience to run Caddy with specific combinations of modules by providing pre-built images according to the needs and preferences of the users.

All custom images are updated automatically when a new version of Caddy is released using the official Caddy Docker image. This is done by using GitHub Actions to build and push the images for all Caddy supported platforms to Docker Hub, GitHub Packages and Quay container registries. In addition, since the update cycle of many modules is faster than Caddy's, all custom images are periodically re-built with the latest version of their respective modules on the first day of every month. Those who are already running Caddy's latest version can force the update by re-creating the container (i.e. running docker compose up --force-recreate if using Docker Compose).

All commits and tags are signed with a GPG key to ensure their integrity and authenticity, and 2FA is enabled in the accounts involved in the management of this repository and the container registries.

Builds

If you are looking for a specific custom build not available yet in this repository, please open a new Issue with your request. To make sure no broken or unsafe builds are created, the requested modules should be properly maintained and listed in the Caddy's download page. Additional information and instructions can be found by clicking on the name of the Caddy images and modules listed below.

Caddy Images:

Modules:

Usage

Since all images from this repository are built off the official Caddy Docker image, the same volumes and/or bind mounts, ports mapping, environment variables, etc. can be used with this container. Please refer to the official Caddy Docker image and docs for more information on using Caddy.

Docker builds for all Caddy supported platforms are available at the following container registries:

  • Docker Hub > docker pull serfriz/<caddy-build-name>:latest
  • GitHub Packages > docker pull ghcr.io/serfriz/<caddy-build-name>:latest
  • Quay > docker pull quay.io/serfriz/<caddy-build-name>:latest

To pull a specific build, replace <caddy-build-name> with the desired one. For example, to pull the caddy-cloudflare build from Docker Hub, use docker pull serfriz/caddy-cloudflare:latest.

Tags

The following tags are available for all images:

  • latest
  • <version> (eg: 2.7.4, including: 2.7, 2, etc.)

Container Creation

Simply create the container using the docker run command, or a docker-compose.yml file including the necessary environment variables depending on the modules used. The following blocks contain examples for both methods using <caddy-build-name> as the image name (replace it with the desired Caddy build name), and including all environment variables required by the modules listed above (some may not apply to your specific build).

Docker Run

docker run --rm -it \
  --name caddy \  # feel free to choose your own container name
  --restart unless-stopped \  # run container unless stopped by user (optional)
  -p 80:80 \  # HTTP port
  -p 443:443 \  # HTTPS port
  -p 443:443/udp \  # HTTP/3 port (optional)
  -v caddy-data:/data \  # volume mount for certificates data
  -v caddy-config:/config \  # volume mount for configuration data
  -v $PWD/Caddyfile:/etc/caddy/Caddyfile \  # to use your own Caddyfile
  -v $PWD/log:/var/log \  # bind mount for the log directory (optional)
  -v $PWD/srv:/srv \  # bind mount to serve static sites or files (optional)
  -e CLOUDFLARE_API_TOKEN=<token-value> \  # Cloudflare API token (if applicable)
  -e DUCKDNS_API_TOKEN=<token-value> \  # DuckDNS API token (if applicable)
  -e CROWDSEC_API_KEY=<key-value> \  # CrowdSec API key (if applicable)
  -e GANDI_BEARER_TOKEN=<token-value> \  # Gandi API token (if applicable)
  -e NETCUP_CUSTOMER_NUMBER=<number-value> \  # Netcup customer number (if applicable)
  -e NETCUP_API_KEY=<key-value> \  # Netcup API key (if applicable)
  -e NETCUP_API_PASSWORD=<password-value> \  # Netcup API password (if applicable)
  -e PORKBUN_API_KEY=<key-value> \  # Porkbun API key (if applicable)
  -e PORKBUN_API_SECRET_KEY=<secret-key-value> \  # Porkbun API secret key (if applicable)
  -e OVH_ENDPOINT=<endpoint-value> \  # OVH endpoint (if applicable)
  -e OVH_APPLICATION_KEY=<application-value> \  # OVH application key (if applicable)
  -e OVH_APPLICATION_SECRET=<secret-value> \  # OVH application secret (if applicable)
  -e OVH_CONSUMER_KEY=<consumer-key-value> \  # OVH consumer key (if applicable)
  serfriz/<caddy-build-name>:latest  # replace with the desired Caddy build name

The volume and bind mounts can be adjusted to meet to your needs, $PWD is used to reference the current working directory, but you can replace it with your preferred path. The environment variables are only required if the modules used in the build require them.

The default Caddyfile that is included inside the Docker container is just a placeholder to serve a static Caddy welcome page with some useful instructions. So you will most likely want to mount your own $PWD/Caddyfile to configure Caddy according to your needs (the file must already exist in the specified path before creating the container).

The restart policy can be adjusted to your needs. The policy unless-stopped ensures the container is always running (even at boot) unless it is explicitly stopped by the user.

Docker Compose

version: "3.7"
services:
  caddy:
    image: serfriz/<caddy-build-name>:latest  # replace with the desired Caddy build name
    container_name: caddy  # feel free to choose your own container name
    restart: "unless-stopped"  # run container unless stopped by user (optional) 
    ports:
      - "80:80"  # HTTP port
      - "443:443"  # HTTPS port
      - "443:443/udp"  # HTTP/3 port (optional)
    volumes:
      - caddy-data:/data  # volume mount for certificates data
      - caddy-config:/config  # volume mount for configuration data
      - $PWD/Caddyfile:/etc/caddy/Caddyfile  # to use your own Caddyfile
      - $PWD/log:/var/log  # bind mount for the log directory (optional)
      - $PWD/srv:/srv  # bind mount to serve static sites or files (optional)
    environment:
      - CLOUDFLARE_API_TOKEN=<token-value>  # Cloudflare API token (if applicable)
      - DUCKDNS_API_TOKEN=<token-value>  # DuckDNS API token (if applicable)
      - CROWDSEC_API_KEY=<key-value>  # CrowdSec API key (if applicable)
      - GANDI_BEARER_TOKEN=<token-value>  # Gandi API token (if applicable)
      - NETCUP_CUSTOMER_NUMBER=<number-value>  # Netcup customer number (if applicable)
      - NETCUP_API_KEY=<key-value>  # Netcup API key (if applicable)
      - NETCUP_API_PASSWORD=<password-value>  # Netcup API password (if applicable)
      - PORKBUN_API_KEY=<key-value>  # Porkbun API key (if applicable)
      - PORKBUN_API_SECRET_KEY=<secret-key-value>  # Porkbun API secret key (if applicable)
      - OVH_ENDPOINT=<endpoint-value>  # OVH endpoint (if applicable)
      - OVH_APPLICATION_KEY=<application-value>  # OVH application key (if applicable)
      - OVH_APPLICATION_SECRET=<secret-value>  # OVH application secret (if applicable)
      - OVH_CONSUMER_KEY=<consumer-key-value>  # OVH consumer key (if applicable)
volumes:
  caddy-data:
    external: true
  caddy-config:

Defining the data volume as external ensures that docker compose down does not delete the volume, but you may need to create it first using docker volume create caddy-data. This doesn't apply to bind mounts if you opt to use them instead of volumes.

Graceful Reloads

Caddy does not require a full restart when the Caddyfile is modified. Caddy comes with a caddy reload command which can be used to reload its configuration with zero downtime.

When running Caddy in Docker, the recommended way to trigger a config reload is by executing the caddy reload command in the running container. First, you'll need to determine your container ID or name. Then, pass the container ID to docker exec. The working directory is set to /etc/caddy so Caddy can find your Caddyfile without additional arguments.

caddy_container_id=$(docker ps | grep caddy | awk '{print $1;}')  # use your container name if different from 'caddy'
docker exec -w /etc/caddy $caddy_container_id caddy reload

It is possible to create an alias for the caddy reload command to make it more convenient to use by adding the following line to your ~/.bashrc or ~/.zshrc file:

alias caddy-reload="docker exec -w /etc/caddy $(docker ps | grep caddy | awk '{print $1;}') caddy reload"

Once you have added the alias to the appropriate file, you will need to source it for the changes to take effect. You can do this by running source ~/.bashrc or source ~/.zshrc in your terminal. After this, you will be able to use the caddy-reload alias in your terminal sessions.

Configuration

This section aims to provide some basic information on how to configure Caddy with the modules included in the custom builds, but it is not intended to be a comprehensive guide. All the examples are based on the official Caddyfile syntax and the modules' documentation.

DNS Modules

To make use of the different modules that provide DNS-01 ACME validation support at the server level, set the global acme_dns directive in your Caddyfile using your DNS provider's name and the respective environment variable for the API token. The example shows the use case for Cloudflare DNS with the rest of the DNS providers commented out.

{
  acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} #  for Cloudflare
  # acme_dns duckdns {env.DUCKDNS_API_TOKEN} #  for DuckDNS
  # acme_dns gandi {env.GANDI_BEARER_TOKEN} #  for Gandi
  # acme_dns netcup {  # for Netcup
  #   customer_number {env.NETCUP_CUSTOMER_NUMBER}
  #   api_key {env.NETCUP_API_KEY}
  #   api_password {env.NETCUP_API_PASSWORD}
  # }
  # acme_dns porkbun {  # for Porkbun
  #   api_key {env.PORKBUN_API_KEY}
  #   api_secret_key {env.PORKBUN_API_SECRET_KEY}
  # }
  # acme_dns ovh {  # for OVH
  #   endpoint {env.OVH_ENDPOINT}
  #   application_key {env.OVH_APPLICATION_KEY}
  #   application_secret {env.OVH_APPLICATION_SECRET}
  #   consumer_key {env.OVH_CONSUMER_KEY}
  # }
  # Please refer to the respective Caddy DNS plugin page for other DNS providers
}

Alternatively, you can use the tls directive at each site. See the caddy-dns/cloudflare module for additional details.

my.domain.tld {
  tls {
    dns cloudflare {env.CLOUDFLARE_API_TOKEN}  # for Cloudflare
    # dns duckdns {env.DUCKDNS_API_TOKEN}  # for DuckDNS
    # dns gandi {env.GANDI_BEARER_TOKEN}  # for Gandi
    # dns netcup {  # for Netcup
    #   customer_number {env.NETCUP_CUSTOMER_NUMBER}
    #   api_key {env.NETCUP_API_KEY}
    #   api_password {env.NETCUP_API_PASSWORD}
    # }
    # dns porkbun {  # for Porkbun
    #   api_key {env.PORKBUN_API_KEY}
    #   api_secret_key {env.PORKBUN_API_SECRET_KEY}
    # }
    # dns ovh {  # for OVH
    #   endpoint {env.OVH_ENDPOINT}
    #   application_key {env.OVH_APPLICATION_KEY}
    #   application_secret {env.OVH_APPLICATION_SECRET}
    #   consumer_key {env.OVH_CONSUMER_KEY}
    # }
    # Please refer to the respective Caddy DNS plugin page for other DNS providers
  }
}

Creating a Cloudflare API Token

You can generate a Cloudflare API token via the Cloudflare web dashboard through the following steps:

  1. Login to your Cloudflare Dashboard
  2. Go to Account Profile > API Tokens
  3. Click "Create token" (Use the "Create Custom Token" option)
  4. Grant the following permissions:
    • Zone > Zone > Read
    • Zone > DNS > Edit
  5. Copy the token and use it as the CLOUDFLARE_API_TOKEN environment variable.

Creating a DuckDNS API Token

To generate a DuckDNS API token, login to your DuckDNS account, copy the token, and use it as the DUCKDNS_API_TOKEN environment variable. You can recreate the token by clicking on the three vertical lines in the top right corner next to your logged in email, and selecting the recreate token option.

Creating a Netcup API Token

To generate a Netcup API token follow the steps from the Netcup API docs. Use the NETCUP_CUSTOMER_NUMBER, NETCUP_API_KEY and NETCUP_API_PASSWORD environment variables in the Docker Compose/Run and Caddyfile configuration.

Creating API Tokens for Other DNS Providers

The process to generate API tokens for the rest of the DNS providers is similar to the Cloudflare, DuckDNS and Netcup ones. Please refer to the respective DNS provider's documentation for additional details.

Cloudflare IPs

To restrict access to your server only to Cloudflare's IP ranges, add the trusted_proxies directive to the global options, under servers, in your Caddyfile. For additional details, refer to trusted_proxies/cloudflare documentation and WeidiDeng/caddy-cloudflare-ip repository.

{
  servers {
    trusted_proxies cloudflare {
      interval 12h
      timeout 15s
    }
  }
}

Dynamic DNS

To keep your DNS records updated with the public IP address of your instance, add the dynamic_dns directive to the global options in your Caddyfile. This module regularly queries a service for your public IP address and updates the DNS records via your DNS provider's API whenever it changes. For additional details and advanced configuration examples refer to mholt/caddy-dynamicdns repository. The example shows the use case for Cloudflare DNS with the rest of the DNS providers commented out.

{
  dynamic_dns {
    provider cloudflare {env.CLOUDFLARE_API_TOKEN}  # for Cloudflare
    # provider duckdns {env.DUCKDNS_API_TOKEN}  # for DuckDNS
    # provider gandi {env.GANDI_BEARER_TOKEN}  # for Gandi
    # provider netcup {  # for Netcup
    #   customer_number {env.NETCUP_CUSTOMER_NUMBER}
    #   api_key {env.NETCUP_API_KEY}
    #   api_password {env.NETCUP_API_PASSWORD}
    # }
    # provider porkbun {  # for Porkbun
    #   api_key {env.PORKBUN_API_KEY}
    #   api_secret_key {env.PORKBUN_API_SECRET_KEY}
    # }
    # provider ovh {  # for OVH
    #   endpoint {env.OVH_ENDPOINT}
    #   application_key {env.OVH_APPLICATION_KEY}
    #   application_secret {env.OVH_APPLICATION_SECRET}
    #   consumer_key {env.OVH_CONSUMER_KEY}
    # }
    # Please refer to the respective Caddy DNS plugin page for other DNS providers
    domains {
      domain.tld
    }
  }
}

Using the option dynamic_domains, it can also be configured to scan through the domains configured in the Caddyfile and try to manage those DNS records.

CrowdSec Bouncer

CrowdSec is a free and open source security automation tool that uses local logs and a set of scenarios to infer malicious intent. In addition to operating locally, an optional community integration is also available, through which crowd-sourced IP reputation lists are distributed.

To make use of the CrowdSec Bouncer module, set the global crowdsec directive in your Caddyfile, and include it in every site you want to protect. For advanced usage, refer to the hslatman/caddy-crowdsec-bouncer repository.

{
  debug  # makes Caddy logs more detailed (optional)
  order crowdsec first  # forces the CrowdSec directive to be executed first
  crowdsec {
    api_url http://localhost:8080  # it should point to your CrowdSec API (it can be a remote URL)
    api_key {env.CROWDSEC_API_KEY}
  }
}

my.domain.tld {
  crowdsec
  log {
    output file /var/log/access.log  # the path should match the bind mount of the log directory
  }
}

Creating a CrowdSec API Key

To register the Caddy CrowdSec Bouncer to your API, you need to run the command below on the server where the CrowdSec API is installed, and use the generated API key CROWDSEC_API_KEY as environment variable when creating the Caddy container.

sudo cscli bouncers add caddy-bouncer

For additional details, refer to the CrowdSec documentation.

Rate Limit

The rate_limit HTTP handler module lets you define rate limit zones, which have a unique name of your choosing. If a rate limit is exceeded, an HTTP error with status 429 will be returned. This error can be handled using the conventional error handling routes in your config.

Additional information and Caddyfile configuration examples can be found in the mholt/caddy-ratelimit repository.

Caddy Security

This plugin implements different authentication methods: Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML, including MFA/2FA with App Authenticators and Yubico (formerly caddy-auth-portal). It is also an authorization plugin for HTTP request authorization based on JWT/PASETO tokens (formerly caddy-authorize, caddy-auth-jwt), and manages credentials for various integrations.

Please, refer to the official greenpau/caddy-security documentation for additional details. Some configuration examples can also be found in the authp/authp.github.io repository.

Docker Proxy

The plugin scans Docker metadata, looking for labels indicating that the service or container should be served by Caddy. Then, it generates an in-memory Caddyfile with site entries and proxies pointing to each Docker service by their DNS name or container IP. Every time a Docker object changes, the plugin updates the Caddyfile and triggers Caddy to gracefully reload with zero downtime.

Additional information and Caddyfile configuration examples can be found in the lucaslorentz/caddy-docker-proxy repository.

Sablier

Sablier is a free and open-source software that can scale your workloads on demand following different strategies. Your workloads can be a docker container, a kubernetes deployment and more (see providers for the full list). This plugin provides an integration with Caddy.

Additional information and Caddyfile configuration examples can be found in the acouvreur/sablier documentation.

GeoIP Filter

Allows Caddy to filter traffic based on the client's IP address location. This module needs access to the Maxmind GeoLite2 database which can be downloaded for free after creating an account. Additional information is available on Maxmind official website. You will specifically need the GeoLite2-Country.mmdb file, or the GeoLite2-City.mmdb if you're matching on subdivisions and metro codes.

Information and examples about the usage of this module can be found on the on the Caddy website's plugin page and the porech/caddy-maxmind-geolocation repository.

Events Exec

It can be used to execute commands on the system based on specific events, such as when a certificate is renewed. This is configured in the Caddyfile using the standard events directive along with the events.handlers.exec module. Additional information and examples can be found in the mholt/caddy-events-exec repository. Please be mindful of any security implications of the commands you run and how you configure this module.

Contributing

Feel free to contribute, request additional Caddy images with your preferred modules, and make things better by opening an Issue or Pull Request.

License

Software under GPL-3.0 ensures users' freedom to use, modify, and distribute it while keeping the source code accessible. It promotes transparency, collaboration, and knowledge sharing. Users agree to comply with the GPL-3.0 license terms and provide the same freedom to others.