From acbdea25e9420ea05eed2df94536fe3d30ba623b Mon Sep 17 00:00:00 2001 From: "Justin J. Novack" Date: Wed, 23 Apr 2025 16:41:29 -0400 Subject: [PATCH 1/4] docs(examples): rewrite traefik example for v3 Addresses #380, #396, and #419 --- examples/traefik/README.md | 91 ++++++- examples/traefik/acme.json | 0 examples/traefik/docker-compose-swarm.yml | 34 --- examples/traefik/run-swarm.sh | 42 --- examples/traefik/swarm.yml | 299 ++++++++++++++++++++++ 5 files changed, 379 insertions(+), 87 deletions(-) delete mode 100644 examples/traefik/acme.json delete mode 100644 examples/traefik/docker-compose-swarm.yml delete mode 100755 examples/traefik/run-swarm.sh create mode 100644 examples/traefik/swarm.yml diff --git a/examples/traefik/README.md b/examples/traefik/README.md index 9412bca2..81e6372f 100644 --- a/examples/traefik/README.md +++ b/examples/traefik/README.md @@ -1,22 +1,91 @@ -# Traefik example +# Single URL Docker Registry UI for Docker Swarm -Host the docker registry ui behind [traefik](http://traefik.io) with Docker Swarm mode. +Swarm-compliant UI with public registry PULLs and authenticated registry PUSHes with Traefik v3. -## How to run +## Features -Open a terminal console and type +* Unauthenticated PULLs +* Authenticated PUSH/DELETEs +* Single URL (registry and ui on the same domain) +* Let's Encrypt enabled +* Authenticated Traefik Dashboard -```bash -bash run-swarm.sh +## Introduction + +This configuration is designed to be a "single-serving" quickstart for homelab or development environment. +It is built to be modular enough to easily break apart for larger or more production environments for the +more advanced scenarios. + +It serves both a container registry and Joxit's Docker-Registry-UI on the same URL over HTTPS that can be +secured with certificates from Let's Encrypt. The registry allows for public PULLs and authenticated PUSHes. + +Additionally, the **traefik** dashboard UI is published (although you do not need it during production) on +port 8443 because [traefik hardcodes](https://doc.traefik.io/traefik/operations/api/#dashboard) the paths +`/dashboard` and `/api`, plus it is just good practice to logically separate your administrative endpoints. + +## Prerequisites + +### Internet-Facing + +As committed, this uses Let's Encrypt for certificates, so port `:443` must be forwarded to your swarm to +use the TLS challenge. + +[!WARNING] +By default, you are set to use Let's Encrypt production certificate environment. During testing, you are +advised to use the staging environment so you are not locked out from generating certificates due to a +misconfiguration. + +### `.env` File + +The `.env` file makes it easy to perform all the replacements necessary. + +[!TIP] +You may opt to find and replace the variables in the yaml file rather than relying on environment variables. + +```shell +# Name of the stack within docker swarm +STACK=registry +# Name of the domain to register with traefik +DOMAIN=contoso.com +# Leave blank if not using Let's Encrypt +RESOLVER=letsencrypt +# Email address for Let's Encrypt +EMAIL=postmaster@contoso.com +``` + +## Uppies + +[!WARNING] +Ensure that you have loaded the variables (e.g. `source .env` ) before bringing up the stack. + +```shell +❯ docker stack deploy -c <(cat swarm.yml) $STACK +Creating network registry_frontend +Creating service registry_traefik +Creating service registry_redis +Creating service registry_registry +Creating service registry_ui +Creating service registry_error-pages ``` -## Authentication +### Accounts + +#### dashboard + +Traefik's dashboard account is: + +* Username: `admin` +* Password: `hunter2` + +#### registry -The registry is protected via __Basic authentication__ but feel free to use whatever you like. -In this sample, credentials are: **admin / admin**. +The registry account is: -To generate a new password for basic auth, run the command: `htpasswd -nb username password`. +* Username: `user` +* Password: `hunter2` ## Contributors -Thank you [@onizet](https://github.com/onizet) for this example. \ No newline at end of file +* [@jnovack](https://github.com/jnovack) +* [@agahkarakuzu](https://github.com/agahkarakuzu) +* [@onizet](https://github.com/onizet) diff --git a/examples/traefik/acme.json b/examples/traefik/acme.json deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/traefik/docker-compose-swarm.yml b/examples/traefik/docker-compose-swarm.yml deleted file mode 100644 index cbe1da00..00000000 --- a/examples/traefik/docker-compose-swarm.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '3.1' -services: - registry: - image: registry:2.7 - volumes: - - /opt/docker-registry:/var/lib/registry - environment: - - REGISTRY_HTTP_SECRET=my_registry_secret - - REGISTRY_STORAGE_DELETE_ENABLED=true - deploy: - placement: - constraints: [node.role == manager] - - ui: - image: joxit/docker-registry-ui:latest - environment: - - DELETE_IMAGES=true - - REGISTRY_TITLE=My Private Docker Registry - - NGINX_PROXY_PASS_URL=http://docker-registry_registry:5000 - - SINGLE_REGISTRY=true - depends_on: ['registry'] - networks: ['proxy', 'default'] - deploy: - labels: - traefik.backend: 'registry.mydomain.com' - traefik.frontend.rule: 'Host:registry.mydomain.com' - traefik.enable: 'true' - traefik.port: 80 - traefik.docker.network: 'traefik-net' - traefik.frontend.auth.basic: 'admin:$apr1$XXrpwZre$ItZSXpoeB6bdPLCGT7eXG0' - traefik.frontend.passHostHeader: 'true' - -networks: - proxy: {external: {name: 'traefik-net'}} \ No newline at end of file diff --git a/examples/traefik/run-swarm.sh b/examples/traefik/run-swarm.sh deleted file mode 100755 index 66c87239..00000000 --- a/examples/traefik/run-swarm.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -if ! [[ `docker network ls | grep "traefik-net"` ]] &>/dev/null; then - echo "Setup traefik network" - docker network create --driver=overlay --attachable traefik-net -fi - - -if ! [[ `docker service ls | grep "traefik2"` ]] &>/dev/null; then - dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - - # ensure acme.json wich will contains the letsencrypt certificates - touch "$dir"/acme.json && chmod 600 "$dir"/acme.json - - docker service create --name traefik2 --detach=false \ - --constraint node.role==manager \ - --update-parallelism 1 --update-delay 10s \ - --mode global \ - --publish 80:80 \ - --publish 443:443 \ - --read-only \ - --mount type=bind,source="$(pwd)"/acme.json,target=/etc/traefik/acme.json \ - --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ - --network traefik-net \ - traefik:1.7.4-alpine \ - --entrypoints='Name:http Address::80 Redirect.EntryPoint:https' \ - --entrypoints='Name:https Address::443 TLS' \ - --defaultentrypoints=http,https \ - --acme \ - --acme.storage=/etc/traefik/acme.json \ - --acme.entryPoint=https \ - --acme.httpChallenge.entryPoint=http \ - --acme.email=contact@mydomain.com \ - --docker \ - --docker.swarmMode \ - --docker.domain=mydomain.com \ - --docker.exposedByDefault=false \ - --docker.watch \ - --api -fi - -docker stack deploy --compose-file docker-compose-swarm.yml docker-registry \ No newline at end of file diff --git a/examples/traefik/swarm.yml b/examples/traefik/swarm.yml new file mode 100644 index 00000000..5e9df6f5 --- /dev/null +++ b/examples/traefik/swarm.yml @@ -0,0 +1,299 @@ +services: + traefik: + image: traefik:3 + ports: + - target: 80 + published: 80 + protocol: tcp + - target: 443 + published: 443 + protocol: tcp + - target: 8443 + published: 8443 + protocol: tcp + command: + ### Provider - Docker Swarm + # Enable Swarm mode in traefik + - --providers.swarm + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.swarm.exposedbydefault=false + # Connect to the socket, so that traefik can read labels from Docker services + - --providers.swarm.endpoint=unix:///var/run/docker.sock + + ### Entrypoints + # Create an entrypoint "http" listening on port 80 + - --entrypoints.http.address=:80 + # Create an entrypoint "https" listening on port 443 + - --entrypoints.https.address=:443 + # Create an entrypoint "dashboard" listening on port 8443 + - --entrypoints.dashboard.address=:8443 + + ### TLS Certificates, Stores, and Options REQUIRE on-disk configuration (OPTIONAL Configuration) + # Enable the File Provider (this is the path INSIDE the container) + # - --providers.file.directory=/opt/traefik/ + # Tell traefik to watch for changes + # - --providers.file.watch=true + + ### Let's Encrypt Resolver (Dynamic Certificate Option) + # Create the certificate resolver "letsencrypt" for Let's Encrypt, uses the environment variable EMAIL + - --certificatesresolvers.letsencrypt.acme.email=${EMAIL} + # Store the Let's Encrypt certificates in the mounted volume + - --certificatesresolvers.letsencrypt.acme.storage=/certificates/acme.json + # Use the TLS Challenge for Let's Encrypt + - --certificatesresolvers.letsencrypt.acme.tlschallenge=true + # Let's Encrypt will refuse to issue certs if you make an unreasonably large amount of requests + # ** ENABLE this during testing, REMOVE during production ** + # - --certificatesresolvers.letsencrypt.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory + + ### Operations + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + - --log.level=INFO + # Enable the Dashboard and API + - --api + - --api.insecure=true + - --api.dashboard=true + + deploy: + replicas: 1 + resources: + reservations: + cpus: '0.05' + memory: 32M + limits: + cpus: '0.5' + memory: 128M + placement: + constraints: + # Always run it on a manager + - node.role == manager + # Set to always run on the node with the certificates volume (must add label to node) + # - node.labels.traefik == true + labels: + ### Initial Setup + # Use the traefik network (declared below) + - "traefik.swarm.network=${STACK}_frontend" + # Enable Traefik for this service, to make it available in the public network + - "traefik.enable=true" + # Define the port inside of the Docker service to use + - "traefik.http.services.traefik.loadbalancer.server.port=8080" + + ### catch-all https-redirect middleware to redirect all HTTP to HTTPS + # Redirect to the https:// scheme (note this is NOT the 'https' entrypoint name) + - "traefik.http.middlewares.redirect-to-https.redirectScheme.scheme=https" + # Permanent redirect, status code 308 (preserves the method, e.g GET/POST) + - "traefik.http.middlewares.redirect-to-https.redirectScheme.permanent=true" + # Match any host (including none) + - "traefik.http.routers.http-catchall.rule=HostRegexp(`.+`)" + # Listen on the http entrypoint + - "traefik.http.routers.http-catchall.entryPoints=http" + # Set this rule to have a very high priority (default when unset = 25) + - "traefik.http.routers.http-catchall.priority=1000" + # Send to the middleware 'redirect-to-https' + - "traefik.http.routers.http-catchall.middlewares=redirect-to-https" + + ### Dashboard configuration + # traefik-https router matches the domain... + - "traefik.http.routers.traefik-https.rule=Host(`registry.${DOMAIN}`)" + # ... but only the 'dashboard' entrypoint! + - "traefik.http.routers.traefik-https.entrypoints=dashboard" + # Enable HTTP Basic auth, using the middleware created below + - "traefik.http.routers.traefik-https.middlewares=dashboard-basic-auth" + # Use the special Traefik service api@internal with the web UI/Dashboard + - "traefik.http.routers.traefik-https.service=api@internal" + # Enable TLS + - "traefik.http.routers.traefik-https.tls=true" + # Set the certificate resolver, set value to nothing if using a static certificate + - "traefik.http.routers.traefik-https.tls.certresolver=${RESOLVER}" + + ### Middlewares + ## 'compress' middleware - https://doc.traefik.io/traefik/middlewares/http/compress/ + # enable compression + - "traefik.http.middlewares.compress.compress=true" + ## 'ratelimit' middleware - https://doc.traefik.io/traefik/middlewares/http/ratelimit/ + # set average requests to 20/second + - "traefik.http.middlewares.ratelimit.ratelimit.average=20" + # allow bursting to 50/second when average is not met + - "traefik.http.middlewares.ratelimit.ratelimit.burst=50" + ## 'buffering' middleware - https://doc.traefik.io/traefik/middlewares/http/buffering/ + # permit body sizes up to 10GB (for handling registry blob data layers) + - "traefik.http.middlewares.buffering.buffering.maxRequestBodyBytes=10240000000" # 10GB + + ### BasicAuth middleware + # BasicAuth credentials (interpolation warning, $ must be $$) password = hunter2 + - "traefik.http.middlewares.dashboard-basic-auth.basicauth.users=admin:$$2y$$05$$8S4d0UTrSxlq5wlQPJzUoeSlat5N5U2zRcFQ7sGu10Tzw21p1TfUu" + + volumes: + # Add Docker as a mounted volume, so that Traefik can read the labels of other services + - /var/run/docker.sock:/var/run/docker.sock:ro + # Mount the volume to store the certificates for lets encrypt + - certficates:/certificates + # OPTIONAL: Dynamic configuration for TLS (certificates, stores and options) defaults (limitation: cannot be loaded at runtime) + # - /opt/traefik/:/opt/traefik + + networks: + - frontend + + ### Redis Container + redis: + image: redis:alpine + networks: + - frontend + command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru + deploy: + replicas: 1 + resources: + reservations: + memory: 32M + limits: + cpus: '0.5' + memory: 512M + + ### Docker's Distribution / Registry Container + registry: + image: registry:latest + networks: + - frontend + volumes: + - /opt/registry:/var/lib/registry + environment: + REGISTRY_STORAGE_DELETE_ENABLED: "yes" + REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: redis + REGISTRY_REDIS_OPTIONS_ADDRS: "[redis:6379]" + deploy: + replicas: 1 + resources: + limits: + cpus: '0.25' + memory: 1024M + labels: + ### Initial Setup + # Use the traefik network (declared below) + - "traefik.swarm.network=${STACK}_frontend" + # Enable Traefik for this service, to make it available in the public network + - "traefik.enable=true" + # Define the port inside of the Docker service to use + - "traefik.http.services.registry.loadbalancer.server.port=5000" + + ### Router for GET requests without authentication + # 'registry' accepts GET requests on the URL beginning with /v2, allows public `docker pull` + - "traefik.http.routers.registry.rule=Host(`registry.${DOMAIN}`) && PathPrefix(`/v2`) && Method(`GET`)" + # Runs on the 'https' endpoint + - "traefik.http.routers.registry.entryPoints=https" + # Enable TLS + - "traefik.http.routers.registry.tls=true" + # Set the certificate resolver, set value to nothing if using a static certificate + - "traefik.http.routers.registry.tls.certresolver=${RESOLVER}" + # Use the 'compress' and 'ratelimit' middlewares + - "traefik.http.routers.registry.middlewares=compress@swarm,ratelimit@swarm,error-pages-middleware" + + ### Router for POST/PUT/DELETE requests with BasicAuth + # 'registry-authd' accepts POST/PUT/DELETE requests on the URL beginning with /v2, allows only authenticated `docker push` + - "traefik.http.routers.registry-authd.rule=(Host(`registry.${DOMAIN}`) && PathPrefix(`/v2`)) && (Method(`POST`) || Method(`PUT`) || Method(`DELETE`))" + # Runs on the 'https' entrypoint + - "traefik.http.routers.registry-authd.entryPoints=https" + # Enable TLS + - "traefik.http.routers.registry-authd.tls=true" + # Set the certificate resolver, set value to nothing if using a static certificate + - "traefik.http.routers.registry-authd.tls.certresolver=${RESOLVER}" + # Use the 'compress', 'ratelimit' and 'registry-basic-auth', defined below + - "traefik.http.routers.registry-authd.middlewares=compress@swarm,ratelimit@swarm,registry-basic-auth,error-pages-middleware" + + # CORS for UI when on different domain (NOTE: You do NOT need CORS for this setup where everything is the same domain) + # - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://registry.${DOMAIN}" + # - "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=HEAD,GET,OPTIONS,DELETE" + # - "traefik.http.middlewares.cors.headers.accesscontrolallowcredentials=true" + # - "traefik.http.middlewares.cors.headers.accesscontrolallowheaders=Authorization,Accept,Cache-Control" + # - "traefik.http.middlewares.cors.headers.accesscontrolexposeheaders=Docker-Content-Digest" + + ### BasicAuth middleware + # BasicAuth credentials (interpolation warning, $ must be $$) password = hunter2 + - "traefik.http.middlewares.registry-basic-auth.basicauth.users=user:$$2y$$05$$8S4d0UTrSxlq5wlQPJzUoeSlat5N5U2zRcFQ7sGu10Tzw21p1TfUu" + + ### Joxit's Docker Registry UI + ui: + image: joxit/docker-registry-ui:latest + environment: + - DELETE_IMAGES=true + - REGISTRY_URL=https://registry.${DOMAIN} + - NGINX_PROXY_PASS_URL=http://registry:5000 + - SINGLE_REGISTRY=true + networks: + - frontend + deploy: + replicas: 1 # You can adjust the replica count as needed for scaling + resources: + limits: + cpus: '0.5' + memory: 1024M + labels: + ### Initial Setup + # Use the traefik network (declared below) + - "traefik.swarm.network=${STACK}_frontend" + # Enable Traefik for this service, to make it available in the public network + - "traefik.enable=true" + # Define the port inside of the Docker service to use + - "traefik.http.services.ui.loadbalancer.server.port=80" + + ### Router for all requests + # 'ui' accepts all requests for the domain + - "traefik.http.routers.ui.rule=Host(`registry.${DOMAIN}`)" + # Runs on the 'https' endpoint + - "traefik.http.routers.ui.entrypoints=https" + # Uses the error-pages-middleware (will use itself if it encouters an error) + - "traefik.http.routers.ui.middlewares=error-pages-middleware" + # Enable TLS + - "traefik.http.routers.ui.tls=true" + # Set the certificate resolver, set value to nothing if using a static certificate + - "traefik.http.routers.ui.tls.certresolver=${RESOLVER}" + + # Custom error pages (COMPLETELY Optional) + error-pages: + image: ghcr.io/tarampampam/error-pages:3 # using the latest tag is highly discouraged + networks: + - frontend + environment: + TEMPLATE_NAME: connection # set the error pages template + deploy: + replicas: 1 + resources: + limits: + cpus: '0.1' + memory: 32M + labels: + ### Initial Setup + # Use the traefik network (declared below) + - "traefik.swarm.network=${STACK}_frontend" + # Enable Traefik for this service, to make it available in the public network + - "traefik.enable=true" + # Define the port inside of the Docker service to use + - "traefik.http.services.error-pages-service.loadbalancer.server.port=8080" + + ### Configure the Router for all requests + # use as "fallback" for any NON-registered services + - "traefik.http.routers.error-pages-router.rule=HostRegexp(`.+`)" + # set priority to the lowest priority + - "traefik.http.routers.error-pages-router.priority=1" + # Runs on the 'https' endpoint + - "traefik.http.routers.error-pages-router.entrypoints=https" + # Uses the error-pages-middleware (will use itself if it encouters an error) + - "traefik.http.routers.error-pages-router.middlewares=error-pages-middleware" + + ### Configure the middleware + # Use only on HTTP status codes 400-599 + - "traefik.http.middlewares.error-pages-middleware.errors.status=400-599" + # Set the service to send to + - "traefik.http.middlewares.error-pages-middleware.errors.service=error-pages-service" + # Set the url to forward the error to + - "traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html" + +volumes: + # Create a volume to store the certificates + certficates: + +networks: + # Optionally use an externally-managed already-created overlay network "traefik" + frontend: + driver: overlay From 75f4785147d8c652acf74c1d143a6233efdac385 Mon Sep 17 00:00:00 2001 From: "Justin J. Novack" Date: Fri, 2 May 2025 16:40:58 -0400 Subject: [PATCH 2/4] docs(examples): fix github markdown and add mermaid diagram --- examples/traefik/README.md | 45 ++++++++++++++++++++++++++++++++------ examples/traefik/swarm.yml | 2 ++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/examples/traefik/README.md b/examples/traefik/README.md index 81e6372f..d148dc1e 100644 --- a/examples/traefik/README.md +++ b/examples/traefik/README.md @@ -30,8 +30,8 @@ port 8443 because [traefik hardcodes](https://doc.traefik.io/traefik/operations/ As committed, this uses Let's Encrypt for certificates, so port `:443` must be forwarded to your swarm to use the TLS challenge. -[!WARNING] -By default, you are set to use Let's Encrypt production certificate environment. During testing, you are +> [!WARNING] +> By default, you are set to use Let's Encrypt production certificate environment. During testing, you are advised to use the staging environment so you are not locked out from generating certificates due to a misconfiguration. @@ -39,8 +39,8 @@ misconfiguration. The `.env` file makes it easy to perform all the replacements necessary. -[!TIP] -You may opt to find and replace the variables in the yaml file rather than relying on environment variables. +> [!TIP] +> You may opt to find and replace the variables in the yaml file rather than relying on environment variables. ```shell # Name of the stack within docker swarm @@ -55,11 +55,13 @@ EMAIL=postmaster@contoso.com ## Uppies -[!WARNING] -Ensure that you have loaded the variables (e.g. `source .env` ) before bringing up the stack. +> [!WARNING] +> If using environment variables, ensure that you have loaded them +> (e.g. `source .env` ) before bringing up the stack. Alternatively +> you can use `envsubst` to interpolate them at runtime. ```shell -❯ docker stack deploy -c <(cat swarm.yml) $STACK +❯ docker stack deploy -c swarm.yml $STACK Creating network registry_frontend Creating service registry_traefik Creating service registry_redis @@ -84,6 +86,35 @@ The registry account is: * Username: `user` * Password: `hunter2` +## Mermaid Diagram + +```mermaid +flowchart LR + subgraph containers["containers"] + registry_ui["registry_ui"] + registry["registry"] + end + subgraph traefik_proxy["traefik_proxy"] + traefik["traefik"] + basic_auth["basic_auth"] + end + web1>"registry.contoso.com"] --> traefik + web2>"registry.contoso.com/v2"] -- GET --> traefik + web2 -- POST --> traefik + traefik --> registry_ui & basic_auth & registry + basic_auth --> registry + basic_auth@{ shape: delay} + style basic_auth stroke-width:2px,stroke-dasharray: 2 + linkStyle 0 stroke:#2962FF + linkStyle 1 stroke:#00C853 + linkStyle 2 stroke:#FF6D00 + linkStyle 3 stroke:#2962FF + linkStyle 4 stroke:#FF6D00 + linkStyle 5 stroke:#00C853 + linkStyle 6 stroke:#FF6D00 + +``` + ## Contributors * [@jnovack](https://github.com/jnovack) diff --git a/examples/traefik/swarm.yml b/examples/traefik/swarm.yml index 5e9df6f5..417a11d4 100644 --- a/examples/traefik/swarm.yml +++ b/examples/traefik/swarm.yml @@ -48,6 +48,8 @@ services: ### Operations # Enable the access log, with HTTP requests - --accesslog + # Turn on anonymous usage to help developers + - --global.sendAnonymousUsage # Enable the Traefik log, for configurations and errors - --log - --log.level=INFO From 6692250ca47d263f45184c05b14e2909861ceeb9 Mon Sep 17 00:00:00 2001 From: "Justin J. Novack" Date: Tue, 27 May 2025 15:29:31 -0400 Subject: [PATCH 3/4] docs(examples): fix for proper docker login authentication properly 401'd the docker login check. Reference: https://mirilittleme.medium.com/how-docker-login-works-under-the-hood-42225601843c --- examples/traefik/swarm.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/traefik/swarm.yml b/examples/traefik/swarm.yml index 417a11d4..76e6f32f 100644 --- a/examples/traefik/swarm.yml +++ b/examples/traefik/swarm.yml @@ -192,8 +192,8 @@ services: - "traefik.http.routers.registry.middlewares=compress@swarm,ratelimit@swarm,error-pages-middleware" ### Router for POST/PUT/DELETE requests with BasicAuth - # 'registry-authd' accepts POST/PUT/DELETE requests on the URL beginning with /v2, allows only authenticated `docker push` - - "traefik.http.routers.registry-authd.rule=(Host(`registry.${DOMAIN}`) && PathPrefix(`/v2`)) && (Method(`POST`) || Method(`PUT`) || Method(`DELETE`))" + # 'registry-authd' accepts POST/PUT/DELETE requests on the URL beginning with /v2, allows only authenticated `docker push`; additionally make sure you 401 the /v2/ request for `docker login` + - "traefik.http.routers.registry-authd.rule=(Host(`registry.${DOMAIN}`) && PathPrefix(`/v2`)) && (Method(`POST`) || Method(`PUT`) || Method(`DELETE`)) || (Host(`registry.${DOMAIN}`) && Path(`/v2/`) && Method(`GET`))" # Runs on the 'https' entrypoint - "traefik.http.routers.registry-authd.entryPoints=https" # Enable TLS From 1fb41bd62eb2f6238e09f758efb1c1d2b404c6c4 Mon Sep 17 00:00:00 2001 From: "Justin J. Novack" Date: Fri, 6 Jun 2025 09:18:19 -0400 Subject: [PATCH 4/4] chore: clean up traefik example --- examples/traefik/swarm.yml | 55 ++------------------------------------ 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/examples/traefik/swarm.yml b/examples/traefik/swarm.yml index 76e6f32f..4d6396f4 100644 --- a/examples/traefik/swarm.yml +++ b/examples/traefik/swarm.yml @@ -28,12 +28,6 @@ services: # Create an entrypoint "dashboard" listening on port 8443 - --entrypoints.dashboard.address=:8443 - ### TLS Certificates, Stores, and Options REQUIRE on-disk configuration (OPTIONAL Configuration) - # Enable the File Provider (this is the path INSIDE the container) - # - --providers.file.directory=/opt/traefik/ - # Tell traefik to watch for changes - # - --providers.file.watch=true - ### Let's Encrypt Resolver (Dynamic Certificate Option) # Create the certificate resolver "letsencrypt" for Let's Encrypt, uses the environment variable EMAIL - --certificatesresolvers.letsencrypt.acme.email=${EMAIL} @@ -132,8 +126,6 @@ services: - /var/run/docker.sock:/var/run/docker.sock:ro # Mount the volume to store the certificates for lets encrypt - certficates:/certificates - # OPTIONAL: Dynamic configuration for TLS (certificates, stores and options) defaults (limitation: cannot be loaded at runtime) - # - /opt/traefik/:/opt/traefik networks: - frontend @@ -189,7 +181,7 @@ services: # Set the certificate resolver, set value to nothing if using a static certificate - "traefik.http.routers.registry.tls.certresolver=${RESOLVER}" # Use the 'compress' and 'ratelimit' middlewares - - "traefik.http.routers.registry.middlewares=compress@swarm,ratelimit@swarm,error-pages-middleware" + - "traefik.http.routers.registry.middlewares=compress@swarm,ratelimit@swarm" ### Router for POST/PUT/DELETE requests with BasicAuth # 'registry-authd' accepts POST/PUT/DELETE requests on the URL beginning with /v2, allows only authenticated `docker push`; additionally make sure you 401 the /v2/ request for `docker login` @@ -201,7 +193,7 @@ services: # Set the certificate resolver, set value to nothing if using a static certificate - "traefik.http.routers.registry-authd.tls.certresolver=${RESOLVER}" # Use the 'compress', 'ratelimit' and 'registry-basic-auth', defined below - - "traefik.http.routers.registry-authd.middlewares=compress@swarm,ratelimit@swarm,registry-basic-auth,error-pages-middleware" + - "traefik.http.routers.registry-authd.middlewares=compress@swarm,ratelimit@swarm,registry-basic-auth" # CORS for UI when on different domain (NOTE: You do NOT need CORS for this setup where everything is the same domain) # - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=https://registry.${DOMAIN}" @@ -220,7 +212,6 @@ services: environment: - DELETE_IMAGES=true - REGISTRY_URL=https://registry.${DOMAIN} - - NGINX_PROXY_PASS_URL=http://registry:5000 - SINGLE_REGISTRY=true networks: - frontend @@ -244,53 +235,11 @@ services: - "traefik.http.routers.ui.rule=Host(`registry.${DOMAIN}`)" # Runs on the 'https' endpoint - "traefik.http.routers.ui.entrypoints=https" - # Uses the error-pages-middleware (will use itself if it encouters an error) - - "traefik.http.routers.ui.middlewares=error-pages-middleware" # Enable TLS - "traefik.http.routers.ui.tls=true" # Set the certificate resolver, set value to nothing if using a static certificate - "traefik.http.routers.ui.tls.certresolver=${RESOLVER}" - # Custom error pages (COMPLETELY Optional) - error-pages: - image: ghcr.io/tarampampam/error-pages:3 # using the latest tag is highly discouraged - networks: - - frontend - environment: - TEMPLATE_NAME: connection # set the error pages template - deploy: - replicas: 1 - resources: - limits: - cpus: '0.1' - memory: 32M - labels: - ### Initial Setup - # Use the traefik network (declared below) - - "traefik.swarm.network=${STACK}_frontend" - # Enable Traefik for this service, to make it available in the public network - - "traefik.enable=true" - # Define the port inside of the Docker service to use - - "traefik.http.services.error-pages-service.loadbalancer.server.port=8080" - - ### Configure the Router for all requests - # use as "fallback" for any NON-registered services - - "traefik.http.routers.error-pages-router.rule=HostRegexp(`.+`)" - # set priority to the lowest priority - - "traefik.http.routers.error-pages-router.priority=1" - # Runs on the 'https' endpoint - - "traefik.http.routers.error-pages-router.entrypoints=https" - # Uses the error-pages-middleware (will use itself if it encouters an error) - - "traefik.http.routers.error-pages-router.middlewares=error-pages-middleware" - - ### Configure the middleware - # Use only on HTTP status codes 400-599 - - "traefik.http.middlewares.error-pages-middleware.errors.status=400-599" - # Set the service to send to - - "traefik.http.middlewares.error-pages-middleware.errors.service=error-pages-service" - # Set the url to forward the error to - - "traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html" - volumes: # Create a volume to store the certificates certficates: