NET::ERR_CERT_AUTHORITY_INVALID when running Traefik Docker container in front of Caddy, PHP, MySQL Docker container

1. The problem I’m having:

I’m trying to set up a Traefik Docker container to act as a proxy for several other Docker containers running Caddy, MySQL, PHP so that each of the containers behind Traefik can host a website / webapplication. Here is a quick drawing:

Without Traefik, everything works fine, I assume it’s because both Traefik and Caddy try to issue an certificate for my domain vanill.at as seen in the

docker logs -f 1876714962c4

below.

When running the Traefik container, I can only access Traefik’s dashboard at

monitor.vanill.at but not the website located inside

/srv/lcmp/www/public

which is a Shopware 6 installation but could be any static index.html file.
At this point I have no clue what to try or where to even begin to resolve this issue. since I’m fairly new to all this.

The folder structure looks like this

  • /srv/
  • /srv/traefik.toml
  • /srv/traefik_dynamic.toml
  • /srv/docker-compose.yml
  • /srv/letsencrypt
    ++ /srv/letsencrypt/acme.json (I did a chmod 600 to it)

++ /srv/lcmp
++ /srv/lcmp/docker-compose.yml
++ /srv/lcmp/caddy_docker
+++ /srv/lcmp/caddy_docker/Caddyfile
+++ /srv/lcmp/caddy_docker/Dockerfile

++ /srv/lcmp/php_docker
+++ /srv/lcmp/php_docker/Dockerfile

++ /srv/lcmp/www
+++ /srv/lcmp/www/public

2. Error messages and/or full log output:

curl -vL
*   Trying 185.164.4.42:80...
* Connected to vanill.at (185.164.4.42) port 80 (#0)
> GET / HTTP/1.1
> Host: vanill.at
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://vanill.at/
< Server: Caddy
< Date: Thu, 21 Mar 2024 19:24:12 GMT
< Content-Length: 0
<
* Closing connection 0
* Clear auth, redirects to port from 80 to 443Issue another request to this URL: 'https://vanill.at/'
*   Trying 185.164.4.42:443...
* Connected to vanill.at (185.164.4.42) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS header, Unknown (21):
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: self-signed certificate
* Closing connection 1
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Error in Browser

NET::ERR_CERT_AUTHORITY_INVALID

Output of docker logs -f 1876714962c4 which is the Traefik Docker container

time="2024-03-21T19:20:58Z" level=error msg="Unable to obtain ACME certificate for domains \"vanill.at\": unable to generate a certificate for the domains [vanill.at]: acme: error: 429 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:rateLimited :: Error creating new order :: too many certificates (5) already issued for this exact set of domains in the last 168 hours: vanill.at, retry after 2024-03-22T21:13:49Z: see https://letsencrypt.org/docs/duplicate-certificate-limit/" rule="Host(`vanill.at`)" providerName=lets-encrypt.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory" routerName=caddy@docker

3. Caddy version:

docker exec -it lcmp-caddy-1 caddy version
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=

4. How I installed and ran Caddy:

see docker-compose.yml and Dockerfile

a. System environment:

A virtual server running AlmaLinux 9, Dockerfile Version 3.9
vanill.at is connected to the vServer using DNS A recoreds which works fine

b. Command:

To start the Docker containers running Caddy, PHP, MySQL
docker compose up -d → output

CONTAINER ID   IMAGE                          COMMAND                  CREATED          STATUS          PORTS                                                                      NAMES
8855d15ac968   lcmp-caddy                     "caddy run --config …"   34 minutes ago   Up 34 minutes   80/tcp, 443/tcp, 2019/tcp, 443/udp                                         lcmp-caddy-1
578520a4c5d3   phpmyadmin/phpmyadmin:latest   "/docker-entrypoint.…"   34 minutes ago   Up 34 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp                                      lcmp-phpmyadmin-1
0a81677b87f0   lcmp-php                       "docker-php-entrypoi…"   34 minutes ago   Up 34 minutes   9000/tcp                                                                   lcmp-php-1
c1c35caf9124   mysql:8.0                      "docker-entrypoint.s…"   34 minutes ago   Up 34 minutes   3306/tcp, 33060/tcp                                                        lcmp-mysql-1
1876714962c4   traefik:v2.11                  "/entrypoint.sh --ap…"   4 hours ago      Up 4 hours      0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   srv-traefik-1

To create the network web that should be exposed to the internet
docker network create web → successful

docker network inspect web

[
    {
        "Name": "web",
        "Id": "29d19a955745e51fd0e84921fe6c09b2b895e06761b91b3b72354f39a68d0dca",
        "Created": "2024-03-21T13:34:58.556968868+01:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.224.0/20",
                    "Gateway": "192.168.224.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0a81677b87f02e51c46970b5a7a12824af5610906973b7761cce89f4663ddac9": {
                "Name": "lcmp-php-1",
                "EndpointID": "81485d2d3cafe191c7bb6bbfd2c5d1b05b2095a23cceba604d4504036da952aa",
                "MacAddress": "02:42:c0:a8:e0:03",
                "IPv4Address": "192.168.224.3/20",
                "IPv6Address": ""
            },
            "1876714962c426e45039dec88719792d58f2d59262664e623c2ce97f6b32508d": {
                "Name": "srv-traefik-1",
                "EndpointID": "d7eee022bf04f5fb3d0ce8cddf56e82da8c97fc4a4af6ca2e9f05449f45d2825",
                "MacAddress": "02:42:c0:a8:e0:02",
                "IPv4Address": "192.168.224.2/20",
                "IPv6Address": ""
            },
            "8855d15ac968632c329eec7315e157015a17267d21d18ed72370d650a4fca3e7": {
                "Name": "lcmp-caddy-1",
                "EndpointID": "f67a48ccc02a7310c64b5afbe962d5c42a07e64badf08956b73ddb0f78cfc583",
                "MacAddress": "02:42:c0:a8:e0:04",
                "IPv4Address": "192.168.224.4/20",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

c. Service/unit/compose file:

docker-compose.yml for running the Traefik Docker container

version: '3.9'

services:
  traefik:
    image: traefik:v2.11
    ports:
      - 80:80
      - 443:443
    networks:
      - web
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - letsencrypt:/letsencrypt
      - $PWD/traefik.toml:/traefik.toml
      - $PWD/traefik_dynamic.toml:/traefik_dynamic.toml
      - /var/log:/var/log
    command:
      - --api.dashboard=true
      - --log.level=INFO
      - --log.filepath=/var/log/traefik.log
      - --accesslog=true
      - --accesslog.filepath=/var/log/traefik-access.log
      - --providers.docker.network=web
      - --providers.docker.exposedByDefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entryPoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      # remove next line when using Traefik v2
      #- --entrypoints.websecure.asDefault=true
      - --entrypoints.websecure.http.tls.certresolver=myresolver
      - --certificatesresolvers.myresolver.acme.email=mail@vanill.at
      - --certificatesresolvers.myresolver.acme.tlschallenge=true
      - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
    labels:
      - traefik.enable=true
      #- traefik.http.routers.mydashboard.rule=Host(`monitor.vanill.at`)
      #- traefik.http.routers.mydashboard.service=api@internal
      #- traefik.http.routers.mydashboard.middlewares=myauth
      #- traefik.http.middlewares.myauth.basicauth.users=<redacted>:<redacted>


networks:
  web:
    external: true

volumes:
  letsencrypt:
    name: letsencrypt

The traefik.toml

[entryPoints]
  [entryPoints.web]
    address = ":80"
    [entryPoints.web.http.redirections.entryPoint]
      to = "websecure"
      scheme = "https"

  [entryPoints.websecure]
    address = ":443"

[api]
  dashboard = true`Preformatted text`

[certificatesResolvers.lets-encrypt.acme]
  email = "mail@vanill.at"
  storage = "acme.json"
  [certificatesResolvers.lets-encrypt.acme.tlsChallenge]

[providers.docker]
  watch = true
  network = "web"

[providers.file]
  filename = "traefik_dynamic.toml"

The traefik_dynamic.toml

[http.middlewares.simpleAuth.basicAuth]
  users = [
    "<redacted>:<redacted>"
  ]


[http.routers.api]
  rule = "Host(`monitor.vanill.at`)"
  entrypoints = ["websecure"]
  middlewares = ["simpleAuth"]
  service = "api@internal"
  [http.routers.api.tls]
    certResolver = "lets-encrypt"

The Dockerfile for PHP

FROM php:8.2-fpm-alpine

# Downloading install-php-extensions script and making it executable
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

# Making install-php-extensions script executable
RUN chmod +x /usr/local/bin/install-php-extensions

# Installing PHP extensions using install-php-extensions script
RUN install-php-extensions \
    mysqli \
    pdo \
    pdo_mysql \
    gd \
    zip \
    intl \
    xml \
    curl \
    dom \
    fileinfo \
    iconv \
    json \
    libxml \
    mbstring \
    openssl \
    pcre \
    phar \
    simplexml \
    zlib

# Setting PHP directives
RUN echo 'memory_limit = 512M' > /usr/local/etc/php/conf.d/memory-limit.ini
RUN echo 'max_execution_time = 300' > /usr/local/etc/php/conf.d/max-execution-time.ini
RUN echo 'max_input_vars = 10000' > /usr/local/etc/php/conf.d/max-input-vars.ini
RUN echo 'max_input_time = 300' > /usr/local/etc/php/conf.d/max-input-time.ini
RUN echo 'opcache.revalidate_freq=0' > /usr/local/etc/php/conf.d/opcache-revalidate-freq.ini
RUN echo 'opcache.validate_timestamps=0' > /usr/local/etc/php/conf.d/opcache-validate-timestamps.ini
RUN echo 'opcache.max_accelerated_files=7963' > /usr/local/etc/php/conf.d/opcache-max-accelerated-files.ini
RUN echo 'opcache.memory_consumption=256' > /usr/local/etc/php/conf.d/opcache-memory-consumption.ini
RUN echo 'opcache.interned_strings_buffer=16' > /usr/local/etc/php/conf.d/opcache-interned-strings-buffer.ini
RUN echo 'opcache.fast_shutdown=1' > /usr/local/etc/php/conf.d/opcache-fast-shutdown.ini

# Cleaning up
RUN rm -rf /var/cache/apk/*

Dockerfile for Caddy

# Use the official Caddy Docker image
FROM caddy:latest

# Update package index and upgrade installed packages
RUN apk update && apk upgrade

# Copy Caddyfile to configure Caddy server
COPY Caddyfile /etc/caddy/Caddyfile

docker-compose.yml for the Docker container that should be behind Traefik


networks:
  web:
    external: true
  internal:
    external: false

services:
  # PHP Service
  php:
    build: './php_docker/'
    volumes:
      - ./www/:/var/www/html/
    labels:
      - traefik.enable=true
      #- traefik.http.routers.php.rule=Host(`vanill.at`)
      #- traefik.http.routers.php.tls=true
      #- traefik.http.routers.php.tls.certresolver=lets-encrypt
      #- traefik.port=9000
    networks:
      - internal
      - web

  # Caddy Service
  caddy:
    build: './caddy_docker/'
    depends_on:
      - php
    restart: unless-stopped
    #ports:
      #- "80:80"
      #- "443:443"
      #- "443:443/udp"
    volumes:
      - ./www/:/var/www/html/
      - ./caddy_docker/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    labels:
      - traefik.enable=true
      - traefik.tcp.routers.caddy.entrypoints.websecure
      - traefik.tcp.routers.caddy.rule=HostSNI(`*`)
      - traefik.tcp.services.caddy.loadbalancer.server.port=80
      - traefik.http.routers.caddy.rule=Host(`vanill.at`)
      - traefik.http.routers.caddy.tls=true
      - traefik.http.routers.caddy.tls.certresolver=lets-encrypt
      - traefik.port=443
      #- traefik.port=80
      - traefik.port=80
    networks:
      - internal
      - web

  # MySQL Service
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: <redacted>
    volumes:
      - mysqldata:/var/lib/mysql
    networks:
      - internal
    labels:
      - traefik.enable=false

  # phpMyAdmin Service
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8080:80
    environment:
      PMA_HOST: mysql
    networks:
      - internal
    depends_on:
      - mysql

# Volumes
volumes:
  mysqldata:
  caddy_data:
  caddy_config:

d. My complete Caddy config:

        encode gzip zstd
        root * /var/www/html/public
        php_fastcgi php:9000
        file_server
        header {
                -server
                -Link
                -X-Powered-By

                # disable FLoC tracking
                Permissions-Policy interest-cohort=()

                # enable HSTS
                Strict-Transport-Security max-age=31536000;

                # disable clients from sniffing the media type
                X-Content-Type-Options nosniff

                # clickjacking protection
                X-Frame-Options DENY

                # keep referrer data off of HTTP connections
                Referrer-Policy no-referrer-when-downgrade
        }
}

www.vanill.at {
        redir https://vanill.at{uri}
}

5. Links to relevant resources:

Here is the guide that I used to set it up

I followed this exact tutorial and got the WordPress site to run but I do not want to run this exact configuration.

Here is an image of my Traefik Dashboard at monitor.vanill.at

I highly appreciate any help or hints!

Hi @chukZ presently I see both Ports 80 & 443 are Closed.

$ nmap -Pn -p80,443 vanill.at
Starting Nmap 7.80 ( https://nmap.org ) at 2024-03-21 23:56 UTC
Nmap scan report for vanill.at (185.164.4.42)
Host is up (0.17s latency).
rDNS record for 185.164.4.42: nezgif.myvserver.online

PORT    STATE  SERVICE
80/tcp  closed http
443/tcp closed https

Nmap done: 1 IP address (1 host up) scanned in 0.47 seconds

You don’t need to use Traefik, you can use GitHub - lucaslorentz/caddy-docker-proxy: Caddy as a reverse proxy for Docker instead which is a plugin that lets Caddy auto-configure itself via Docker labels (labels mapping to Caddyfile config).

If you put a proxy in front of another Caddy instance, your Caddy instances should serve HTTP only, not HTTPS. Trying to proxy over HTTPS adds a lot of complexity for next to no benefit, and adds overhead. Just set your site address on those Caddy instances to :80, then proxy to that port from whatever server you choose to put in front.

Looks like you’ve been rate limited by Let’s Encrypt, there’s not much you can do about that. But if you used Caddy, then it would try issuing with ZeroSSL instead and you’d probably get a valid certificate. Caddy has a much more robust ACME implementation than Traefik’s, so that’s another reason I recommend using it as your proxy.

1 Like

Thanks a lot, I’ll be using this Caddy reverse proxy instead.
Now I’m getting a redirect loop

This is the docker-compose.yml for the Caddy reverse proxy

version: "3.7"
services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    ports:
      - 80:80
      - 443:443
    environment:
      - CADDY_INGRESS_NETWORKS=caddy
    networks:
      - caddy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - caddy_data:/data
    restart: unless-stopped

networks:
  caddy:
    external: true

volumes:
  caddy_data: {}

This is my updated docker-compose.yml for setting up the Caddy, PHP, MySQL, phpMyAdmin container

version: "3.9"

networks:
  caddy:
    external: true
  internal:
    external: false

services:
  # PHP Service
  php:
    build: './php_docker/'
    volumes:
      - ./www/:/var/www/html/
    #labels:
      #- traefik.enable=true
      #- traefik.http.routers.php.rule=Host(`vanill.at`)
      #- traefik.http.routers.php.tls=true
      #- traefik.http.routers.php.tls.certresolver=lets-encrypt
      #- traefik.port=9000
   # networks:
      #- internal
      #- caddy

  # Caddy Service
  caddy:
    build: './caddy_docker/'
    depends_on:
      - php
    restart: unless-stopped
    #ports:
      #- "80:80"
      #- "443:443"
      #- "443:443/udp"
    volumes:
      - ./www/:/var/www/html/
      - ./caddy_docker/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    labels:
      caddy: vanill.at
      caddy.reverse_proxy: "{{upstreams 80}}"
      #- traefik.enable=true
    networks:
      #- internal
      - caddy

  # MySQL Service
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: <redacted>
    volumes:
      - mysqldata:/var/lib/mysql
    networks:
      - internal
    #labels:
    #  - traefik.enable=false

  # phpMyAdmin Service
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8080:80
    environment:
      PMA_HOST: mysql
    networks:
      - internal
    depends_on:
      - mysql

# Volumes
volumes:
  mysqldata:
  caddy_data:
  caddy_config:

The whoami service in /srv/whoami works fine, here is the output

*   Trying 185.164.4.42:80...
* Connected to monitor.vanill.at (185.164.4.42) port 80 (#0)
> GET / HTTP/1.1
> Host: monitor.vanill.at
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://monitor.vanill.at/
< Server: Caddy
< Date: Fri, 22 Mar 2024 18:31:42 GMT
< Content-Length: 0
<
* Closing connection 0
* Clear auth, redirects to port from 80 to 443Issue another request to this URL: 'https://monitor.vanill.at/'
*   Trying 185.164.4.42:443...
* Connected to monitor.vanill.at (185.164.4.42) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=monitor.vanill.at
*  start date: Mar 22 00:00:00 2024 GMT
*  expire date: Jun 20 23:59:59 2024 GMT
*  subjectAltName: host "monitor.vanill.at" matched cert's "monitor.vanill.at"
*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL ECC Domain Secure Site CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* Using Stream ID: 1 (easy handle 0x55a6c09fd550)
* TLSv1.2 (OUT), TLS header, Unknown (23):
> GET / HTTP/2
> Host: monitor.vanill.at
> user-agent: curl/7.76.1
> accept: */*
>
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Unknown (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< content-type: text/plain; charset=utf-8
< date: Fri, 22 Mar 2024 18:31:42 GMT
< server: Caddy
< content-length: 277
<
* TLSv1.2 (IN), TLS header, Unknown (23):
Hostname: 164cef9db65a
IP: 127.0.0.1
IP: 172.28.0.4
RemoteAddr: 172.28.0.2:43324
GET / HTTP/1.1
Host: monitor.vanill.at
User-Agent: curl/7.76.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.28.0.1
X-Forwarded-Host: monitor.vanill.at
X-Forwarded-Proto: https

* Connection #1 to host monitor.vanill.at left intact

All other files and configs are unchanged

This is the output of curl -vL vanill.at

...

* TLSv1.2 (IN), TLS header, Unknown (23):
< HTTP/2 308
< alt-svc: h3=":443"; ma=2592000
< date: Fri, 22 Mar 2024 18:32:53 GMT
< location: https://vanill.at/
< server: Caddy
< server: Caddy
< content-length: 0
<
* Connection #1 to host vanill.at left intact
* Maximum (50) redirects followed
curl: (47) Maximum (50) redirects followed

Again, I highly appreciate any tips or info! Have a nice weekend!

Looks like Caddy is proxying to another Caddy instance, but a redirect to HTTPS is being served. This causes an infinite redirect loop. You should only listen on port 80 on your upstream Caddy, don’t specify a hostname in your site address. Let your front Caddy instance handle HTTPS and HTTP->HTTPS redirects.

1 Like

Thank you so much for this hint!

I got it to work now, at least with curl -lV vanill.at but not with www.vanill.at

There is a new error now though probably not related to Caddy:

Some content is still loaded through http:// so I get a mixed content error in the browser. I guess I’ve got to modify the Shopware settings now

This is my current Caddyfile in /srv/lcmp/caddy_docker

vanill.at:80 {
        encode gzip zstd
        root * /var/www/html/public
        php_fastcgi php:9000
        file_server
        header {
                -server
                -Link
                -X-Powered-By

                # disable FLoC tracking
                Permissions-Policy interest-cohort=()

                # enable HSTS
                Strict-Transport-Security max-age=31536000;

                # disable clients from sniffing the media type
                X-Content-Type-Options nosniff

                # clickjacking protection
                X-Frame-Options DENY

                # keep referrer data off of HTTP connections
                Referrer-Policy no-referrer-when-downgrade
        }
}

www.vanill.at:80 {
        redir vanill.at{uri}
}

I got it to work by adding: 80 to the first site name, hopefully this is correct now.

This is the current docker-compose.yml in /srv/lcmp/

version: "3.9"

networks:
  caddy:
    external: true
  internal: {}

services:
  php:
    build: './php_docker/'
    volumes:
      - ./www/:/var/www/html/
    networks:
      - internal
      - caddy

  caddy:
    build: './caddy_docker/'
    depends_on:
      - php
    restart: unless-stopped
    volumes:
      - ./www/:/var/www/html/
      - ./caddy_docker/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    labels:
      caddy: vanill.at
      caddy.reverse_proxy: "{{upstreams 80}}"
    networks:
      - internal
      - caddy

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: <redacted>
    volumes:
      - mysqldata:/var/lib/mysql
    networks:
      - internal

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8080:80
    environment:
      PMA_HOST: mysql
    networks:
      - internal
    depends_on:
      - mysql

volumes:
  mysqldata:
  caddy_data:
  caddy_config:

There is a new folder now /lcmp3 with Caddy, MySQL, PHP, phpMyAdmin that should serve the /www directory containing a simple index.html

Here is the docker-compose.yml of that folder

version: "3.9"

networks:
  caddy:
    external: true
  internal: {}

services:
  php:
    build: './php_docker/'
    volumes:
      - ./www/:/var/www/html/
    networks:
      - internal
      - caddy

  caddy:
    build: './caddy_docker/'
    depends_on:
      - php
    restart: unless-stopped
    volumes:
      - ./www/:/var/www/html/
      - ./caddy_docker/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    labels:
      caddy: monitor.vanill.at
      caddy.reverse_proxy: "{{upstreams 80}}"
      #- traefik.enable=true
      #- traefik.http.routers.caddy.rule=Host(`vanill.at`)
      #- traefik.http.routers.caddy.tls=true
      #- traefik.http.routers.caddy.tls.certresolver=lets-encrypt
      #- traefik.port=80
    networks:
      - internal
      - caddy

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: PyjbNnbmlh4kpRqltYpT
    volumes:
      - mysqldata:/var/lib/mysql
    networks:
      - internal

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8081:80
    environment:
      PMA_HOST: mysql
    networks:
      - internal
    depends_on:
      - mysql

volumes:
  mysqldata:
  caddy_data:
  caddy_config:

And here the Caddyfile in /srv/lcmp3/caddy_docker

monitor.vanill.at:80 {
        encode gzip zstd
        root * /var/www/html
        php_fastcgi php:9000
        file_server
        header {
                -server
                -Link
                -X-Powered-By

                # disable FLoC tracking
                Permissions-Policy interest-cohort=()

                # enable HSTS
                Strict-Transport-Security max-age=31536000;

                # disable clients from sniffing the media type
                X-Content-Type-Options nosniff

                # clickjacking protection
                X-Frame-Options DENY

                # keep referrer data off of HTTP connections
                Referrer-Policy no-referrer-when-downgrade
        }
}

Here is the Docker logs of lcmp3-caddy-1

docker logs -f lcmp3-caddy-1

{"level":"info","ts":1711192195.499441,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
{"level":"info","ts":1711192195.5022416,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//[::1]:2019","//127.0.0.1:2019","//localhost:2019"]}
{"level":"warn","ts":1711192195.5023842,"logger":"http.auto_https","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv0","http_port":80}
{"level":"info","ts":1711192195.5026696,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000222a00"}
{"level":"info","ts":1711192195.5030944,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"info","ts":1711192195.503434,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1711192195.5035205,"msg":"serving initial configuration"}
{"level":"warn","ts":1711192195.50424,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"791a25d4-99bb-4310-b9ec-4d22fd3cce66","try_again":1711278595.5042362,"try_again_in":86399.999999321}
{"level":"info","ts":1711192195.5043185,"logger":"tls","msg":"finished cleaning storage units"}

Here is curl -lV monitor.vanill.at

*   Trying 185.164.4.42:80...
* Connected to monitor.vanill.at (185.164.4.42) port 80 (#0)
> GET / HTTP/1.1
> Host: monitor.vanill.at
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://monitor.vanill.at/
< Server: Caddy
< Date: Sat, 23 Mar 2024 14:14:33 GMT
< Content-Length: 0
<
* Closing connection 0
* Clear auth, redirects to port from 80 to 443Issue another request to this URL: 'https://monitor.vanill.at/'
*   Trying 185.164.4.42:443...
* Connected to monitor.vanill.at (185.164.4.42) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=monitor.vanill.at
*  start date: Mar 22 00:00:00 2024 GMT
*  expire date: Jun 20 23:59:59 2024 GMT
*  subjectAltName: host "monitor.vanill.at" matched cert's "monitor.vanill.at"
*  issuer: C=AT; O=ZeroSSL; CN=ZeroSSL ECC Domain Secure Site CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* Using Stream ID: 1 (easy handle 0x560a4b35f550)
* TLSv1.2 (OUT), TLS header, Unknown (23):
> GET / HTTP/2
> Host: monitor.vanill.at
> user-agent: curl/7.76.1
> accept: */*
>
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Unknown (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.2 (IN), TLS header, Unknown (23):
< HTTP/2 503
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Sat, 23 Mar 2024 14:14:33 GMT
<
* Connection #1 to host monitor.vanill.at left intact
  • Without running the Caddy proxy in front of the other containers, there is no Mixed Content error, is there anything wrong with the current config of /lcmp?
  • Why do I get the 503 error in the /lcmp3?

I’m really grateful for the help that you’ve provided so far! Thank you so much!

You don’t need the hostname here at all. Just make it :80. Your front Caddy instance handles the hostname & TLS issuance.

Your front-Caddy should be configured with any domains to redirect, not your upstream Caddy. You can do that with labels, just add another site with the redirect. Also your redir should include the scheme, i.e. https://vanill.at{uri}

2 Likes

Thanks a lot! It works perfectly fine now, just go to figure out why I get the mixed content issue in Shopware but that’s not for here I guess

1 Like

Hello again,

the Caddy proxy plus one Caddy, PHP, MySQL, phpMyAdmin setup works fine now. Thanks for the hint with the Caddy reverse proxy plugin. I’ve also resolved the issue with mixed content by modifying the .env.local for Shopware to trust the proxy. My current issue is that whenever I spin up another Caddy, PHP, MySQL, phpMyAdmin “box” behind the reverse proxy (in my case shop4.vanill.at and shop3.vanill.at), shop4’s PHP service responses to requests for shop3.vanill.at, I get a 404 of course since in shop4 there is only a simple index.html (for testing purposes)

I didn’t modify the docker-compose.yml’s of each “box” behind the reverse proxy, that might be the issue but I do not know how to properly separate them.

Monitoring lcmp4-php-1 and lcmp3-php-1 reveals that sometimes lcmp4-php-1 responses and sometimes lcmp3-php-1 responses
when accessing shop3.vanill.at.

Can you help me properly seperate the “boxes”?

This is the docker-compose.yml for the reverse proxy

version: "3.9"
services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      - CADDY_INGRESS_NETWORKS=caddy
    networks:
      - caddy
    #  - caddy2
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - caddy_data:/data
    restart: unless-stopped
    #labels:
    #  caddy: vanill.at
    #  caddy.reverse.proxy: "{{upstreams}}"
    #  caddy.tls.ca: https://acme-staging-v02.api.letsencrypt.org/directory
networks:
  caddy:
    external: true
  #caddy2:
  #  external: true
  #caddy3:
  #  external: true


volumes:
  caddy_data: {}

This is the docker-compose.yml in lcmp3 and lcmp4

version: '3.9'
networks:
  caddy:
    external: true
  internal: {}

services:
  php:
    build: ./php_docker/
    volumes:
      - './www/:/var/www/html/'
    networks:
      #- internal
      - caddy
    #labels:
    #  caddy: vanill.at
    #  caddy.reverse_proxy: '{{upstreams}}'
  caddy:
    build: ./caddy_docker/
    depends_on:
      - php
    #ports:
    #  - 80:80
    #  - "443:443"
    #  - "443:443/udp"
    restart: unless-stopped
    volumes:
      - './www/:/var/www/html/'
      - './caddy_docker/Caddyfile:/etc/caddy/Caddyfile'
      - 'caddy_data:/data'
      - 'caddy_config:/config'
    labels:
      #- traefik.enable=true
      #- traefik.http.routers.caddy.rule=Host(`vanill.at`)
      #- traefik.http.routers.caddy.tls=true
      #- traefik.http.routers.caddy.tls.certresolver=lets-encrypt
      #- traefik.docker.network=caddy
      caddy: shop3.vanill.at
      caddy.reverse_proxy: '{{upstreams}}'
      #caddy.tls.ca: https://acme-staging-v02.api.letsencrypt.org/directory
    networks:
      #- internal
      - caddy
  mysql:
    image: 'mysql:8.0'
    environment:
      MYSQL_ROOT_PASSWORD: <redacted>
    volumes:
      - 'mysqldata:/var/lib/mysql'
    networks:
      - internal
  phpmyadmin:
    image: 'phpmyadmin/phpmyadmin:latest'
    ports:
      - '8085:80'
    environment:
      PMA_HOST: mysql
    networks:
      - internal
    depends_on:
      - mysql
volumes:
  mysqldata: {}
  caddy_data: {}
  caddy_config: {}

If they’re all in networks seen by caddy-docker-proxy and have the same service names, Caddy will probably try to load balance between them. Is that what you mean?

You’d need to use different service names for each app’s php containers to separate them.

2 Likes

Again, thanks a lot, works now!