diff --git a/.env.example b/.env.example index 6c20d3c..3150014 100644 --- a/.env.example +++ b/.env.example @@ -45,6 +45,22 @@ TRAEFIK_ACME_MAIL= # When certificates are displayed and are emitted by # "Fake LE Intermediate X1", # the process went well and the envvar can be reset to empty to get valid certificates. TRAEFIK_ACME_CASERVER= +# Enable the Traefik ACME (Automatic Certificate Management Environment) for automatic SSL certificate management. +TRAEFIK_SERVICES_TLS_CONFIG="tls.certresolver=letsencrypt" +# Enable Traefik to use local certificates. +#TRAEFIK_SERVICES_TLS_CONFIG="tls=true" +# You also need to provide a config file in ./config/traefik/dynamic/certs.yml +# Example: +# cat ./config/traefik/dynamic/certs.yml +# tls: +# certificates: +# - certFile: /certs/opencloud.test.crt +# keyFile: /certs/opencloud.test.key +# stores: +# - default +# +# The certificates need to copied into ./certs/, the absolute path inside the container is /certs/. +# You can also use TRAEFIK_CERTS_DIR=/path/on/host to set the path to the certificates directory. ## OpenCloud Settings ## diff --git a/.gitignore b/.gitignore index d2b38ec..33c9639 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,16 @@ .env # exclude the apps folder -/config/opencloud/apps +/config/opencloud/apps/* +!/config/opencloud/apps/.gitkeep # exclude custom compose files /custom + +# exclude certificates +/certs/* +!/certs/.gitkeep + +# exclude the certificates config folder +/config/traefik/dynamic/* +!/config/traefik/dynamic/.gitkeep diff --git a/README.md b/README.md index 2d2bc26..9cba3e3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repository provides Docker Compose configurations for deploying OpenCloud i OpenCloud Compose offers a modular approach to deploying OpenCloud with several configuration options: -- **Standard deployment** with Traefik reverse proxy and Let's Encrypt certificates +- **Standard deployment** with Traefik reverse proxy and Let's Encrypt certificates or certificates from files - **External proxy** support for environments with existing reverse proxies (like Nginx, Caddy, etc.) - **Collabora Online** integration for document editing - **Keycloak and LDAP** integration for centralized identity management @@ -226,6 +226,77 @@ If you're using **Nginx Proxy Manager (NPM)**, you **should NOT** activate **"Bl Otherwise, the desktop app authentication will return **error 403 Forbidden**. +## SSL Certificate Support + +OpenCloud Compose supports adding SSL certificates for public domains and development environments. This feature enables you to use the "Let's Encrypt ACME challenge" to generate certificates for your public domains as well as using your own certificates. + +### Use Let's Encrypt with ACME Challenge + +1. **Enable Let's Encrypt**: + - Set `TRAEFIK_LETSENCRYPT_EMAIL` to your email address for the ACME challenge + - Set `TRAEFIK_SERVICES_TLS_CONFIG="tls.certresolver=letsencrypt"` to use Let's Encrypt (default value) + + ```bash + # In your .env file + TRAEFIK_LETSENCRYPT_EMAIL=devops@your-domain.tld + TRAEFIK_SERVICES_TLS_CONFIG="tls.certresolver=letsencrypt" + ``` + +### Use Certificates from the `certs/` directory + +1. **Place your certificates**: + - Copy your certificate files (`.crt`, `.pem`, `.key`) to the `certs/` directory + - The directory structure is flexible - organize as needed for your setup + +2. **Configure Traefik dynamic configuration**: + - Place Traefik dynamic configuration files in `config/traefik/dynamic/` + + Example `config/traefik/dynamic/certs.yml`: + ```yaml + tls: + certificates: + - certFile: /certs/opencloud.test.crt + keyFile: /certs/opencloud.test.key + stores: + - default + - certFile: /certs/wildcard.example.com.crt + keyFile: /certs/wildcard.example.com.key + stores: + - default + ``` + +3. **Configure environment variables**: + - Set `TRAEFIK_SERVICES_TLS_CONFIG="tls=true"` to use your local certificates + + ```bash + # In your .env file + TRAEFIK_SERVICES_TLS_CONFIG="tls=true" + ``` + +The certificate directory and configuration directories are now available and automatically mounted in the containers: +- `certs/` → `/certs/` (inside the Traefik container) +- `config/traefik/dynamic/` → dynamic configuration loading + +> [!TIP] +> +> **Local development or testing with mkcert** +> For local development, you can use `mkcert` to generate self-signed certificates for your local domains. This allows you to test SSL/TLS configurations without needing a public domain or Let's Encrypt. It also brings the advantage that you don't have to accept self-signed certificates in your browser all the time. +> ```bash +> # Install mkcert (if not already installed) +> # macOS: brew install mkcert +> # Linux: apt install mkcert or similar +> # Windows: choco install mkcert or download from GitHub +> +> # Install the local CA +> mkcert -install +> +> # Generate certificates for your local domains +> mkcert -cert-file certs/opencloud.test.crt -key-file certs/opencloud.test.key "*.opencloud.test" opencloud.test +> ``` + +> [!IMPORTANT] +> The contents of the `certs/` directory and configuration directories are ignored by git to prevent accidentally committing sensitive certificate files. + ## Configuration ### Environment Variables @@ -238,23 +309,26 @@ The configuration is managed through environment variables in the `.env` file: Key variables: -| Variable | Description | Default | -|---------------------------|----------------------------------------------|---------------------------| -| `COMPOSE_FILE` | Colon-separated list of compose files to use | (commented out) | -| `OC_DOMAIN` | OpenCloud domain | cloud.opencloud.test | -| `OC_DOCKER_TAG` | OpenCloud image tag | latest | -| `OC_CONFIG_DIR` | Config directory path | (Docker volume) | -| `OC_DATA_DIR` | Data directory path | (Docker volume) | -| `INSECURE` | Skip certificate validation | true | -| `COLLABORA_DOMAIN` | Collabora domain | collabora.opencloud.test | -| `WOPISERVER_DOMAIN` | WOPI server domain | wopiserver.opencloud.test | -| `TIKA_IMAGE` | Apache Tika image tag | apache/tika:latest-full | -| `KEYCLOAK_DOMAIN` | Keycloak domain | keycloak.opencloud.test | -| `KEYCLOAK_ADMIN` | Keycloak admin username | kcadmin | -| `KEYCLOAK_ADMIN_PASSWORD` | Keycloak admin password | admin | -| `LDAP_BIND_PASSWORD` | LDAP password for the bind user | admin | -| `KC_DB_USERNAME` | Database user for keycloak | keycloak | -| `KC_DB_PASSWORD` | Database password for keycloak | keycloak | +| Variable | Description | Default | +|------------------------------------|-------------------------------------------------------|------------------------------| +| `COMPOSE_FILE` | Colon-separated list of compose files to use | (commented out) | +| `OC_DOMAIN` | OpenCloud domain | cloud.opencloud.test | +| `OC_DOCKER_TAG` | OpenCloud image tag | latest | +| `OC_CONFIG_DIR` | Config directory path | (Docker volume) | +| `OC_DATA_DIR` | Data directory path | (Docker volume) | +| `INSECURE` | Skip certificate validation | true | +| `COLLABORA_DOMAIN` | Collabora domain | collabora.opencloud.test | +| `WOPISERVER_DOMAIN` | WOPI server domain | wopiserver.opencloud.test | +| `TIKA_IMAGE` | Apache Tika image tag | apache/tika:latest-full | +| `KEYCLOAK_DOMAIN` | Keycloak domain | keycloak.opencloud.test | +| `KEYCLOAK_ADMIN` | Keycloak admin username | kcadmin | +| `KEYCLOAK_ADMIN_PASSWORD` | Keycloak admin password | admin | +| `LDAP_BIND_PASSWORD` | LDAP password for the bind user | admin | +| `KC_DB_USERNAME` | Database user for keycloak | keycloak | +| `KC_DB_PASSWORD` | Database password for keycloak | keycloak | +| `TRAEFIK_LETSENCRYPT_EMAIL` | Email Address for the Let's Encrypt ACME challenge | example@example.org | +| `TRAEFIK_SERVICES_TLS_CONFIG` | Tell traefik and the services which TLS config to use | tls.certresolver=letsencrypt | +| `TRAEFIK_CERTS_DIR` | Directory for custom certificates. | ./certs | See `.env.example` for all available options and their documentation. diff --git a/certs/.gitkeep b/certs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/opencloud/apps/.gitkeep b/config/opencloud/apps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/traefik/docker-entrypoint-override.sh b/config/traefik/docker-entrypoint-override.sh new file mode 100644 index 0000000..cbf85d9 --- /dev/null +++ b/config/traefik/docker-entrypoint-override.sh @@ -0,0 +1,72 @@ +set -e + +printenv +# Function to add arguments to the command +add_arg() { + TRAEFIK_CMD="$TRAEFIK_CMD $1" +} + +# Initialize the base command +TRAEFIK_CMD="traefik" + +# Base Traefik arguments (from your existing configuration) +add_arg "--log.level=${TRAEFIK_LOG_LEVEL:-ERROR}" +# enable dashboard +add_arg "--api.dashboard=true" +# define entrypoints +add_arg "--entryPoints.http.address=:80" +add_arg "--entryPoints.http.http.redirections.entryPoint.to=https" +add_arg "--entryPoints.http.http.redirections.entryPoint.scheme=https" +add_arg "--entryPoints.https.address=:443" +# change default timeouts for long-running requests +# this is needed for webdav clients that do not support the TUS protocol +add_arg "--entryPoints.https.transport.respondingTimeouts.readTimeout=12h" +add_arg "--entryPoints.https.transport.respondingTimeouts.writeTimeout=12h" +add_arg "--entryPoints.https.transport.respondingTimeouts.idleTimeout=3m" +# docker provider (get configuration from container labels) +add_arg "--providers.docker.endpoint=unix:///var/run/docker.sock" +add_arg "--providers.docker.exposedByDefault=false" +# access log +add_arg "--accessLog=true" +add_arg "--accessLog.format=json" +add_arg "--accessLog.fields.headers.names.X-Request-Id=keep" + +# Add Let's Encrypt configuration if enabled +if [ "${TRAEFIK_SERVICES_TLS_CONFIG}" = "tls.certresolver=letsencrypt" ]; then + echo "Configuring Traefik with Let's Encrypt..." + add_arg "--certificatesResolvers.letsencrypt.acme.email=${TRAEFIK_ACME_MAIL:-example@example.org}" + add_arg "--certificatesResolvers.letsencrypt.acme.storage=/certs/acme.json" + add_arg "--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http" + add_arg "--certificatesResolvers.letsencrypt.acme.caserver=${TRAEFIK_ACME_CASERVER:-https://acme-v02.api.letsencrypt.org/directory}" +fi + +# Add local certificate configuration if enabled +if [ "${TRAEFIK_SERVICES_TLS_CONFIG}" = "tls=true" ]; then + echo "Configuring Traefik with local certificates..." + add_arg "--providers.file.directory=/etc/traefik/dynamic" + add_arg "--providers.file.watch=true" +fi + +# Warning if neither certificate method is enabled +if [ "${TRAEFIK_SERVICES_TLS_CONFIG}" != "tls=true" ] && [ "${TRAEFIK_SERVICES_TLS_CONFIG}" != "tls.certresolver=letsencrypt" ]; then + echo "WARNING: Neither Let's Encrypt nor local certificates are enabled." + echo "HTTPS will not work properly without certificate configuration." +fi + +# Add any custom arguments from environment variable +if [ -n "${TRAEFIK_CUSTOM_ARGS}" ]; then + echo "Adding custom Traefik arguments: ${TRAEFIK_CUSTOM_ARGS}" + TRAEFIK_CMD="$TRAEFIK_CMD $TRAEFIK_CUSTOM_ARGS" +fi + +# Add any additional arguments passed to the script +for arg in "$@"; do + add_arg "$arg" +done + +# Print the final command for debugging +echo "Starting Traefik with command:" +echo "$TRAEFIK_CMD" + +# Execute Traefik +exec $TRAEFIK_CMD diff --git a/config/traefik/dynamic/.gitkeep b/config/traefik/dynamic/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/traefik/collabora.yml b/traefik/collabora.yml index 0325497..74182ee 100644 --- a/traefik/collabora.yml +++ b/traefik/collabora.yml @@ -11,7 +11,7 @@ services: - "traefik.enable=true" - "traefik.http.routers.collaboration.entrypoints=https" - "traefik.http.routers.collaboration.rule=Host(`${WOPISERVER_DOMAIN:-wopiserver.opencloud.test}`)" - - "traefik.http.routers.collaboration.tls.certresolver=letsencrypt" + - "traefik.http.routers.collaboration.${TRAEFIK_SERVICES_TLS_CONFIG}" - "traefik.http.routers.collaboration.service=collaboration" - "traefik.http.services.collaboration.loadbalancer.server.port=9300" collabora: @@ -19,6 +19,6 @@ services: - "traefik.enable=true" - "traefik.http.routers.collabora.entrypoints=https" - "traefik.http.routers.collabora.rule=Host(`${COLLABORA_DOMAIN:-collabora.opencloud.test}`)" - - "traefik.http.routers.collabora.tls.certresolver=letsencrypt" + - "traefik.http.routers.collabora.${TRAEFIK_SERVICES_TLS_CONFIG}" - "traefik.http.routers.collabora.service=collabora" - "traefik.http.services.collabora.loadbalancer.server.port=9980" diff --git a/traefik/ldap-keycloak.yml b/traefik/ldap-keycloak.yml index 38bd9b0..1905e8e 100644 --- a/traefik/ldap-keycloak.yml +++ b/traefik/ldap-keycloak.yml @@ -10,6 +10,6 @@ services: - "traefik.enable=true" - "traefik.http.routers.keycloak.entrypoints=https" - "traefik.http.routers.keycloak.rule=Host(`${KEYCLOAK_DOMAIN:-keycloak.opencloud.test}`)" - - "traefik.http.routers.keycloak.tls.certresolver=letsencrypt" + - "traefik.http.routers.keycloak.${TRAEFIK_SERVICES_TLS_CONFIG}" - "traefik.http.routers.keycloak.service=keycloak" - "traefik.http.services.keycloak.loadbalancer.server.port=8080" diff --git a/traefik/opencloud.yml b/traefik/opencloud.yml index 9716e90..d566427 100644 --- a/traefik/opencloud.yml +++ b/traefik/opencloud.yml @@ -5,9 +5,9 @@ services: - "traefik.enable=true" - "traefik.http.routers.opencloud.entrypoints=https" - "traefik.http.routers.opencloud.rule=Host(`${OC_DOMAIN:-cloud.opencloud.test}`)" - - "traefik.http.routers.opencloud.tls.certresolver=letsencrypt" - "traefik.http.routers.opencloud.service=opencloud" - "traefik.http.services.opencloud.loadbalancer.server.port=9200" + - "traefik.http.routers.opencloud.${TRAEFIK_SERVICES_TLS_CONFIG}" traefik: image: traefik:v3.3.1 # release notes: https://github.com/traefik/traefik/releases @@ -15,38 +15,19 @@ services: opencloud-net: aliases: - ${OC_DOMAIN:-cloud.opencloud.test} - command: - - "--log.level=${TRAEFIK_LOG_LEVEL:-ERROR}" - # letsencrypt configuration - - "--certificatesResolvers.letsencrypt.acme.email=${TRAEFIK_ACME_MAIL:-example@example.org}" - - "--certificatesResolvers.letsencrypt.acme.storage=/certs/acme.json" - - "--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http" - - "--certificatesResolvers.letsencrypt.acme.caserver=${TRAEFIK_ACME_CASERVER:-https://acme-v02.api.letsencrypt.org/directory}" - # enable dashboard - - "--api.dashboard=true" - # define entrypoints - - "--entryPoints.http.address=:80" - - "--entryPoints.http.http.redirections.entryPoint.to=https" - - "--entryPoints.http.http.redirections.entryPoint.scheme=https" - - "--entryPoints.https.address=:443" - # change default timeouts for long-running requests - # this is needed for webdav clients that do not support the TUS protocol - - "--entryPoints.https.transport.respondingTimeouts.readTimeout=12h" - - "--entryPoints.https.transport.respondingTimeouts.writeTimeout=12h" - - "--entryPoints.https.transport.respondingTimeouts.idleTimeout=3m" - # docker provider (get configuration from container labels) - - "--providers.docker.endpoint=unix:///var/run/docker.sock" - - "--providers.docker.exposedByDefault=false" - # access log - - "--accessLog=true" - - "--accessLog.format=json" - - "--accessLog.fields.headers.names.X-Request-Id=keep" + entrypoint: [ "/bin/sh", "/opt/traefik/bin/docker-entrypoint-override.sh"] + environment: + - "TRAEFIK_SERVICES_TLS_CONFIG=${TRAEFIK_SERVICES_TLS_CONFIG:-tls.certresolver=letsencrypt}" + - "TRAEFIK_ACME_EMAIL=${TRAEFIK_ACME_MAIL:-example@example.org}" + - "TRAEFIK_ACME_CASERVER=${TRAEFIK_ACME_CASERVER:-https://acme-v02.api.letsencrypt.org/directory}" ports: - "80:80" - "443:443" volumes: - "${DOCKER_SOCKET_PATH:-/var/run/docker.sock}:/var/run/docker.sock:ro" - - "certs:/certs" + - "./config/traefik/docker-entrypoint-override.sh:/opt/traefik/bin/docker-entrypoint-override.sh" + - "${TRAEFIK_CERTS_DIR:-./certs}:/certs" + - "./config/traefik/dynamic:/etc/traefik/dynamic" labels: - "traefik.enable=${TRAEFIK_DASHBOARD:-false}" # defaults to admin:admin @@ -54,11 +35,8 @@ services: - "traefik.http.routers.traefik.entrypoints=https" - "traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DOMAIN:-traefik.opencloud.test}`)" - "traefik.http.routers.traefik.middlewares=traefik-auth" - - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" + - "traefik.http.routers.traefik.${TRAEFIK_SERVICES_TLS_CONFIG}" - "traefik.http.routers.traefik.service=api@internal" logging: driver: ${LOG_DRIVER:-local} restart: always - -volumes: - certs: