Wildcard certificates for subdomains with Cloudflare

1. Caddy version (caddy version):

2.1.1

2. How I run Caddy:

Using a custom container image, based on the official one, but I have added the cloudflare dns module.

3. The problem I’m having:

I am trying to get Caddy to serve a subdomain with wildcard certificates for a domain which is being manged by Cloudflare.
So far I have a working solution for serving a route for *.mydomain.com. Caddy successfully acquires a wildcard certificate from Lets Encrypt and is therefore able to do a dns challenge via Cloudflare.
Unfortunately I am not able to get a wildcard certificate for *.sub.mydomain.com though. Cloudflare tells me that my api key does not have the required permissions to list the zones.

server    | 2020/08/14 18:05:28 [INFO][cache:0x27ab140] Started certificate maintenance routine
server    | {"level":"info","ts":1597428328.9633636,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
server    | {"level":"info","ts":1597428328.9637709,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
server    | {"level":"info","ts":1597428328.9696124,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["gitlab.differentdomain.com","gitlab-registry.differentdomain.com","*.sub.mydomain.com","*.mydomain.com"]}
server    | 2020/08/14 18:05:29 [INFO][cache:0x28a4900] Stopped certificate maintenance routine
server    | {"level":"info","ts":1597428329.005108,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
server    | 2020/08/14 18:05:29 [INFO][*.sub.mydomain.com] Obtain certificate; acquiring lock...
server    | 2020/08/14 18:05:29 [INFO][*.sub.mydomain.com] Obtain: Lock acquired; proceeding...
server    | 2020/08/14 18:05:29 [INFO][*.sub.mydomain.com] Waiting on rate limiter...
server    | 2020/08/14 18:05:29 [INFO][*.sub.mydomain.com] Done waiting
server    | 2020/08/14 18:05:29 [INFO] [*.sub.mydomain.com] acme: Obtaining bundled SAN certificate given a CSR
server    | {"level":"info","ts":1597428329.458439,"logger":"admin","msg":"stopped previous server"}
server    | 2020/08/14 18:05:29 [INFO] [*.sub.mydomain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/6536168811
server    | 2020/08/14 18:05:29 [INFO] [*.sub.mydomain.com] acme: use dns-01 solver
server    | 2020/08/14 18:05:29 [INFO] [*.sub.mydomain.com] acme: Preparing to solve DNS-01
server    | 2020/08/14 18:05:30 [INFO] [*.sub.mydomain.com] acme: Cleaning DNS-01 challenge
server    | 2020/08/14 18:05:30 [WARN] [*.sub.mydomain.com] acme: cleaning up failed: no memory of presenting a DNS record for sub.mydomain.com
server    | 2020/08/14 18:05:30 [INFO] Deactivating auth: https://acme-v02.api.letsencrypt.org/acme/authz-v3/6536168811
server    | 2020/08/14 18:05:30 [ERROR] error: one or more domains had a problem:
server    | [*.sub.mydomain.com] [*.sub.mydomain.com] acme: error presenting token: got error status: HTTP 403: [{Code:0 Message:Actor 'com.cloudflare.api.token.88af6bb45674657gfg34534dfd345345' requires permission 'com.cloudflare.api.account.zone.read' to list zones}]
server    |  (challenge=dns-01 remaining=[])
server    | 2020/08/14 18:05:32 [ERROR] attempt 1: [*.sub.mydomain.com] Obtain: [*.sub.mydomain.com] error: one or more domains had a problem:
server    | [*.sub.mydomain.com] [*.sub.mydomain.com] acme: error presenting token: got error status: HTTP 403: [{Code:0 Message:Actor 'com.cloudflare.api.token.88af6bb45674657gfg34534dfd345345' requires permission 'com.cloudflare.api.account.zone.read' to list zones}]

This is the request I am sending to Caddy’s API (actually its nearly identical to the request I am using for *.domain.com):

curl caddyhost:2019/config/apps/http/servers/srv0/routes/ -X POST -H "Content-Type: application/json" -d '{"@id":"wildcard-sub-mydomain-com","handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"reverse_proxy","headers":{"request":{"set":{"Host":["{http.request.host}"],"X-Real-Ip":["{http.request.remote.host}"]}}},"upstreams":[{"dial":"10.0.0.10:443"}]}]}]}],"match":[{"host":["*.sub.mydomain.com"]}],"terminal":true}'

Since I am able to get a wildcard certificate for *.mydomain.com, but not for *.subdomain.com, I am wondering what I may have forgotten to configure.

This is my current configuration:

{
    "admin": {
        "listen": "0.0.0.0:2019"
    },
    "apps": {
        "http": {
            "servers": {
                "srv0": {
                    "listen": [
                        ":443"
                    ],
                    "routes": [
                        {
                            "@id": "wildcard-mydomain-com",
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "handler": "reverse_proxy",
                                                    "headers": {
                                                        "request": {
                                                            "set": {
                                                                "Host": [
                                                                    "{http.request.host}"
                                                                ],
                                                                "X-Real-Ip": [
                                                                    "{http.request.remote.host}"
                                                                ]
                                                            }
                                                        }
                                                    },
                                                    "upstreams": [
                                                        {
                                                            "dial": "10.0.0.5:443"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "match": [
                                {
                                    "host": [
                                        "*.mydomain.com"
                                    ]
                                }
                            ],
                            "terminal": true
                        },
                        {
                            "@id": "wildcard-sub-mydomain-com",
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "handler": "reverse_proxy",
                                                    "headers": {
                                                        "request": {
                                                            "set": {
                                                                "Host": [
                                                                    "{http.request.host}"
                                                                ],
                                                                "X-Real-Ip": [
                                                                    "{http.request.remote.host}"
                                                                ]
                                                            }
                                                        }
                                                    },
                                                    "upstreams": [
                                                        {
                                                            "dial": "10.0.0.10:443"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "match": [
                                {
                                    "host": [
                                        "*.sub.mydomain.com"
                                    ]
                                }
                            ],
                            "terminal": true
                        }
                    ]
                }
            }
        },
        "tls": {
            "automation": {
                "policies": [
                    {
                        "issuer": {
                            "challenges": {
                                "dns": {
                                    "provider": {
                                        "api_token": "MY_CLOUDFLARE_API_TOKEN",
                                        "name": "cloudflare"
                                    }
                                }
                            },
                            "module": "acme"
                        }
                    }
                ]
            }
        }
    }
}

When this error happens, it’s almost always because people accidentally use an API Key instead of an API Token: GitHub - libdns/cloudflare: Cloudflare provider implementation for libdns

(I wish Cloudflare would rename them…)

Yes, I’ve seen those threads in the forum. But this actually is not the cause to my problem, since the token works for *.mydomain.com.

Please take a look at my configuration. Both routes (*.mydomain.com and *.sub.mydomain.com) use the same token.

I’ve not looked into the sourcecode, but is it possible that caddy sends requests to the cloudflare api using my subdomain (sub.mydomain.com) as a domain filter? This would explain the authentication issue.

Hmm, what if you use the latest code from source? We’re going to tag a new release next week but if you want to test it before then, that would be helpful!

Do you upload a container image for the release candidate? It would make it much easier for me to test the code. I would have to change only a few things in my cd-pipeline then and wouldn’t have to build caddy and the container from scratch.

We have CI artifacts for the tests: caddytls: Configure custom DNS resolvers for DNS challenge (close #2476) · caddyserver/caddy@744d04c · GitHub

Not sure about containers. @hairyhenderson would know

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