Wildcard Cert and Multiple Subdomain

1. Caddy version (caddy version):

v2.4.0-beta.1

2. How I run Caddy:

docker

a. System environment:

Fedora 33
Docker

b. Command:

paste command here

c. Service/unit/compose file:

paste full file contents here

d. My complete Caddyfile or JSON config:

{
email email@example.com
#acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

(dnsauth) {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
log {
output file /var/log/caddy/access.log
}
}

*.{$MY_DOMAIN}:443 {
import dnsauth

@cloud {
host cloud.{$MY_DOMAIN}
}
reverse_proxy @cloud nextcloud-app:80 {
header Strict-Transport-Security max-age=31536000;
redir /.well-known/carddav /remote.php/carddav 301
redir /.well-known/caldav /remote.php/caldav 301
}

@whoami {
host whoami.{$MY_DOMAIN}
}
reverse_proxy @whoami whoami:80
}

3. The problem I’m having:

I’m trying to use wildcard certs for multiple subdomains and not sure if im doing it correctly, for testing I included Nextcloud and whoami but I’ll like to add more later. I think im getting the cert correctly but the additional options for Nextcloud aren’t working when I add them, can I add headers and redir under reverse proxy?

4. Error messages and/or full log output:

caddy | run: adapting config using caddyfile: parsing caddyfile tokens for ‘reverse_proxy’: /etc/caddy/Caddyfile:22 - Error during parsing: unrecognized subdirective header

5. What I already tried:

6. Links to relevant resources:

The problem is here, header and redir are not a subdirectives for reverse_proxy, they are their own directives. Move those outside of the reverse_proxy block (i.e. take them out of the { }).

It should probably look more like this, with handle blocks to wrap each host:

@cloud host cloud.{$MY_DOMAIN}
handle @cloud {
	header Strict-Transport-Security max-age=31536000;
	redir /.well-known/carddav /remote.php/carddav 301
	redir /.well-known/caldav /remote.php/caldav 301
	reverse_proxy nextcloud-app:80
}

@whoami host whoami.{$MY_DOMAIN}
handle @whoami {
	reverse_proxy whoami:80
}

This gives me this error:

caddy | run: adapting config using caddyfile: matcher is defined more than once: host

is the host option supposed to be used once? Thanks!

What’s your full Caddyfile at this point? I couldn’t say without seeing what you have.

Also please remember to use ``` on the lines before and after your config and logs to use code formatting to preserve whitespace!

This is the Caddyfile:

{
  email myemail@email.com 
  #acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

(dnsauth) {
  tls {
    dns cloudflare {env.CLOUDFLARE_API_TOKEN}
  }
  log {
    output file /var/log/caddy/access.log
  }
}

*.{$MY_DOMAIN}:443 {
  import dnsauth
  
  @cloud host cloud.{$MY_DOMAIN}
  handle @cloud {
    reverse_proxy nextcloud-app:80 
    header Strict-Transport-Security max-age=31536000;
    redir /.well-known/carddav /remote.php/carddav 301
    redir /.well-known/caldav /remote.php/caldav 301
  }
  
  @whoami host whoami.{$MY_DOMAIN}
  handle @whoami {
    reverse_proxy @whoami whoami:80
  }
}

I don’t get an error when I try to adapt that config with caddy adapt. Are you sure you’re running the right version of Caddy?

If I comment @whoami block it will work fine, so just one host per Caddyfile? don’t understand why…

Caddy version:

docker exec -ti caddy sh -c "caddy version"
v2.4.0-beta.1 => /src/caddy

Works fine for me, like I said (I omitted the cloudflare DNS plugin because I don’t have it installed right now, but that should be irrelevant here):

$ cat Caddyfile 
{
  email myemail@email.com 
}

(dnsauth) {
  log {
    output file /var/log/caddy/access.log
  }
}

*.{$MY_DOMAIN}:443 {
  import dnsauth
  
  @cloud host cloud.{$MY_DOMAIN}
  handle @cloud {
    reverse_proxy nextcloud-app:80 
    header Strict-Transport-Security max-age=31536000;
    redir /.well-known/carddav /remote.php/carddav 301
    redir /.well-known/caldav /remote.php/caldav 301
  }
  
  @whoami host whoami.{$MY_DOMAIN}
  handle @whoami {
    reverse_proxy @whoami whoami:80
  }
}

$ MY_DOMAIN=example.com caddy adapt --pretty
2021/03/04 03:36:30.768	INFO	using adjacent Caddyfile
{
  "logging": {
    "logs": {
      "default": {
        "exclude": [
          "http.log.access.log0"
        ]
      },
      "log0": {
        "writer": {
          "filename": "/var/log/caddy/access.log",
          "output": "file"
        },
        "include": [
          "http.log.access.log0"
        ]
      }
    }
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [
            ":443"
          ],
          "routes": [
            {
              "match": [
                {
                  "host": [
                    "*.example.com"
                  ]
                }
              ],
              "handle": [
                {
                  "handler": "subroute",
                  "routes": [
                    {
                      "group": "group2",
                      "handle": [
                        {
                          "handler": "subroute",
                          "routes": [
                            {
                              "handle": [
                                {
                                  "handler": "headers",
                                  "response": {
                                    "set": {
                                      "Strict-Transport-Security": [
                                        "max-age=31536000;"
                                      ]
                                    }
                                  }
                                }
                              ]
                            },
                            {
                              "handle": [
                                {
                                  "handler": "static_response",
                                  "headers": {
                                    "Location": [
                                      "/remote.php/carddav"
                                    ]
                                  },
                                  "status_code": 301
                                }
                              ],
                              "match": [
                                {
                                  "path": [
                                    "/.well-known/carddav"
                                  ]
                                }
                              ]
                            },
                            {
                              "handle": [
                                {
                                  "handler": "static_response",
                                  "headers": {
                                    "Location": [
                                      "/remote.php/caldav"
                                    ]
                                  },
                                  "status_code": 301
                                }
                              ],
                              "match": [
                                {
                                  "path": [
                                    "/.well-known/caldav"
                                  ]
                                }
                              ]
                            },
                            {
                              "handle": [
                                {
                                  "handler": "reverse_proxy",
                                  "upstreams": [
                                    {
                                      "dial": "nextcloud-app:80"
                                    }
                                  ]
                                }
                              ]
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "host": [
                            "cloud.example.com"
                          ]
                        }
                      ]
                    },
                    {
                      "group": "group2",
                      "handle": [
                        {
                          "handler": "subroute",
                          "routes": [
                            {
                              "handle": [
                                {
                                  "handler": "reverse_proxy",
                                  "upstreams": [
                                    {
                                      "dial": "whoami:80"
                                    }
                                  ]
                                }
                              ],
                              "match": [
                                {
                                  "host": [
                                    "whoami.example.com"
                                  ]
                                }
                              ]
                            }
                          ]
                        }
                      ],
                      "match": [
                        {
                          "host": [
                            "whoami.example.com"
                          ]
                        }
                      ]
                    }
                  ]
                }
              ],
              "terminal": true
            }
          ],
          "logs": {
            "logger_names": {
              "*.example.com": "log0"
            }
          }
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "subjects": [
              "*.example.com"
            ],
            "issuers": [
              {
                "email": "myemail@email.com",
                "module": "acme"
              },
              {
                "email": "myemail@email.com",
                "module": "zerossl"
              }
            ]
          }
        ]
      }
    }
  }
}

Are you sure you have your MY_DOMAIN environment variable set? I’m not sure what else to suggest, something is clearly incorrect in your environment.

Yes, it is set correctly. I even used the domain directly but same error. Is this the only way to configure wildcard cert with multiple subdomains? been trying to configure for a couple of days now, maybe I chose the wrong approach.

Like I said, works for me.

Please show all your steps, similarly to how I did. Run cat <path to your Caddyfile> then run caddy adapt --pretty --config <path to your Caddyfile> and show us the output. If you’re omitting any information at all, then we cannot help.

CaddyFile:

{
  email myemail@mail.com 
  #acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

(dnsauth) {
  tls {
    dns cloudflare {env.CLOUDFLARE_API_TOKEN}
  }
  log {
    output file /var/log/caddy/access.log
  }
}

*.{$MY_DOMAIN}:443 {
  import dnsauth
  
  @cloud host cloud
  handle @cloud {
    reverse_proxy nextcloud-app:80 
    header Strict-Transport-Security max-age=31536000;
    redir /.well-known/carddav /remote.php/carddav 301
    redir /.well-known/caldav /remote.php/caldav 301
  }
  
  @whoami host whoami
  handle @whoami {
    reverse_proxy @whoami whoami:80
  }
}

docker-compose:

version: "3.7"
services:

  caddy:
    # image: caddy
    build: ./dns-dockerfile
    container_name: caddy
    hostname: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - MY_DOMAIN
      - CLOUDFLARE_API_TOKEN
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./data:/data
      - ./config:/config

networks:
  default:
    external:
      name: $DOCKER_MY_NETWORK

dns-dockerfile Dockerfile:

FROM caddy:2.0.0-builder AS builder

RUN caddy-builder \
    github.com/caddy-dns/cloudflare

FROM caddy:2.0.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

docker version:

docker exec -ti caddy sh -c "caddy version"
v2.4.0-beta.1 => /src/caddy
{
	"logging": {
		"logs": {
			"default": {
				"exclude": [
					"http.log.access.log0"
				]
			},
			"log0": {
				"writer": {
					"filename": "/var/log/caddy/access.log",
					"output": "file"
				},
				"include": [
					"http.log.access.log0"
				]
			}
		}
	},
	"apps": {
		"http": {
			"servers": {
				"srv0": {
					"listen": [
						":443"
					],
					"routes": [
						{
							"match": [
								{
									"host": [
										"*.example.com"
									]
								}
							],
							"handle": [
								{
									"handler": "subroute",
									"routes": [
										{
											"handle": [
												{
													"handler": "subroute",
													"routes": [
														{
															"handle": [
																{
																	"handler": "headers",
																	"response": {
																	"set": {
																	"Strict-Transport-Security": [
																	"max-age=31536000;"
																	]
																	}
																	}
																}
															]
														},
														{
															"handle": [
																{
																	"handler": "static_response",
																	"headers": {
																	"Location": [
																	"/remote.php/carddav"
																	]
																	},
																	"status_code": 301
																}
															],
															"match": [
																{
																	"path": [
																	"/.well-known/carddav"
																	]
																}
															]
														},
														{
															"handle": [
																{
																	"handler": "static_response",
																	"headers": {
																	"Location": [
																	"/remote.php/caldav"
																	]
																	},
																	"status_code": 301
																}
															],
															"match": [
																{
																	"path": [
																	"/.well-known/caldav"
																	]
																}
															]
														},
														{
															"handle": [
																{
																	"handler": "reverse_proxy",
																	"upstreams": [
																	{
																	"dial": "nextcloud-app:80"
																	}
																	]
																}
															]
														}
													]
												}
											],
											"match": [
												{}
											]
										}
									]
								}
							],
							"terminal": true
						}
					],
					"tls_connection_policies": [
						{
							"match": {
								"sni": [
									"*.example.com"
								]
							}
						},
						{}
					],
					"logs": {
						"logger_names": {
							"*.example.com": "log0"
						}
					}
				}
			}
		},
		"tls": {
			"automation": {
				"policies": [
					{
						"subjects": [
							"*.example.com"
						],
						"issuer": {
							"challenges": {
								"dns": {
									"provider": {
										"api_token": "{env.CLOUDFLARE_API_TOKEN}",
										"name": "cloudflare"
									}
								}
							},
							"email": "mymail@mail.com",
							"module": "acme"
						}
					},
					{
						"issuer": {
							"email": "mymail@mail.com",
							"module": "acme"
						}
					}
				]
			}
		}
	}
}

This is all for caddy I believe. thanks.

The JSON output doesn’t look right. The matchers are missing. I think you’re actually running Caddy v2.0.0, not v2.4.0-beta.1

Please update your Dockerfile to the latest instructions on Docker Hub and rebuild your container with docker-compose build.

FROM caddy:2.3.0-builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare

FROM caddy:2.3.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

that was it. didn’t notice the version there since it was printing 2.4 beta 1 in docker. Thanks for your help!

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.