Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use both custom certificates and wildcard auto-generated ones? #5216

Closed
pedrolamas opened this issue Nov 22, 2022 · 11 comments
Closed

Comments

@pedrolamas
Copy link

I want to caddy to generate a wildcard certificate *.example.com, and then use that for multiple hosts (like homeassistant.example.com and other.example.com) on port 443.

I also want to have homeassistant-external.example.com on port 21443 so I can use a manually set certificate.

Basically, I want that when I access https://homeassistant.example.com:443 the certificate used is the auto-generated wildcard one, and when I access https://homeassistant-external.example.com:21443 it uses the supplied certificate instead.

The problem is the moment I add the :21443 block, it will always pick up that certificate for both :443 and :21443, and ignore the auto-generated one!

I have this setup working fine under nginx, but I haven’t been able to do it with caddy…

(Note: this is a follow up on https://caddy.community/t/how-to-use-custom-certificates-with-wildcard-generated-ones/17808/1)

docker-compose.yml

version: '3.7'

services:
  caddy:
    build: ./caddy
    restart: unless-stopped
    networks:
      backbone:
        ipv4_address: 10.0.0.12
    ports:
      - "80:80"
      - "443:443"
      - "21443:21443"
    volumes:
      - "./caddy/Caddyfile:/etc/caddy/Caddyfile"
      - "./caddy/data:/data"
      - "./caddy/cf-certs/certificate.pem:/etc/ssl/certs/cf-certificate.pem"
      - "./caddy/cf-certs/origin-pull-ca.pem:/etc/ssl/certs/cf-origin-pull-ca.pem"
      - "./caddy/cf-certs/privatekey.pem:/etc/ssl/private/cf-key.pem"
    env_file:
      - ./common.env
      - ./caddy/secrets.env
    dns: 10.0.0.3

networks:
  backbone:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: backbone
    ipam:
      config:
        - subnet: 10.0.0.0/27

Dockerfile

FROM caddy:builder-alpine AS builder

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

FROM caddy:alpine

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

Caddyfile

*.example.com:443 {
	tls {
		dns cloudflare {env.CADDY_CLOUDFLARE_TOKEN}
	}

	@homeassistant host homeassistant.example.com
	handle @homeassistant {
		encode zstd gzip
		reverse_proxy homeassistant:8123
	}

	@other host other.example.com
	handle @other {
		encode zstd gzip
		reverse_proxy other
	}
}

:21443 {
	 tls /etc/ssl/certs/cf-certificate.pem /etc/ssl/private/cf-key.pem {
		 client_auth {
			 mode require_and_verify
			 trusted_ca_cert_file /etc/ssl/certs/cf-origin-pull-ca.pem
		 }
	 }

	 @homeassistant host homeassistant-external.example.com
	 handle @homeassistant {
		 encode zstd gzip
		 reverse_proxy homeassistant:8123
	 }
}

Log entries

{"level":"info","ts":1669049465.481152,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
{"level":"warn","ts":1669049465.4910066,"msg":"Caddyfile input is not formatted; run the 'caddy fmt' command to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
{"level":"info","ts":1669049465.4954476,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1669049465.4981682,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x4000440310"}
{"level":"info","ts":1669049465.50363,"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}
{"level":"info","ts":1669049465.5037131,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1669049465.51021,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1669049465.5101776,"logger":"tls","msg":"cleaning storage unit","description":"FileStorage:/data/caddy"}
{"level":"info","ts":1669049465.510438,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details."}
{"level":"debug","ts":1669049465.5105987,"logger":"http","msg":"starting server loop","address":"[::]:443","tls":true,"http3":true}
{"level":"info","ts":1669049465.5106263,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"debug","ts":1669049465.5107412,"logger":"http","msg":"starting server loop","address":"[::]:80","tls":false,"http3":false}
{"level":"info","ts":1669049465.5107656,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":1669049465.5107765,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["*.example.com"]}
{"level":"debug","ts":1669049465.5117052,"logger":"tls","msg":"loading managed certificate","domain":"*.example.com","expiration":1676811013,"issuer_key":"acme-v02.api.letsencrypt.org-directory","storage":"FileStorage:/data/caddy"}
{"level":"debug","ts":1669049465.5124776,"logger":"tls.cache","msg":"added certificate to cache","subjects":["*.example.com"],"expiration":1676811013,"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"73ca35f4146d93539ede788e63f664049395110f58b0fbd301b215124fb07479","cache_size":1,"cache_capacity":10000}
{"level":"debug","ts":1669049465.5125697,"logger":"events","msg":"event","name":"cached_managed_cert","id":"48c0b6ea-a061-4272-b97d-0564946e6fac","origin":"tls","data":{"sans":["*.example.com"]}}
{"level":"info","ts":1669049465.5134115,"logger":"tls","msg":"finished cleaning storage units"}
{"level":"info","ts":1669049465.5136263,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1669049465.5136645,"msg":"serving initial configuration"}
@mholt
Copy link
Member

mholt commented Dec 5, 2022

Thanks for opening an issue, I or someone will look at this soon!

@pedrolamas
Copy link
Author

For the record, I managed to bypass this issue by running two separate Caddy Docker containers, one for the internal stuff (using the auto-generated wildcard certificate), and the other for the external (with the Cloudflare certificate)

@amy1337
Copy link

amy1337 commented May 10, 2024

Thanks for opening an issue, I or someone will look at this soon!

Any news on this? I current plan to host services on port 443 with a, lets say letsencrypt wildcard cert, and other services (also on port 443) with my own certs so hosting 2 caddy instances sadly isn't an option.

@mholt
Copy link
Member

mholt commented May 10, 2024

@amy1337 This issue is pretty old by now, could you try with the latest Caddy version (ideally the latest 2.8 beta) and show us your config and corresponding curl -v commands that demonstrate what is not working as expected.

@vehlwn
Copy link

vehlwn commented Sep 7, 2024

I think I have a similar problem: tls directive from one site block leaks to another blocks. I want to use wildcard certificate but my DNS hosting doesn't support plugins for auto renewing, so I requested certificates through certbot and copied them to caddy folder.

My config is:

{
    auto_https disable_certs
}

service1.example.com {
    tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem
    respond "ok1"
}

service2.example.com {
    # no tls option
    respond "ok2"
}

When I curl either https://service1.example.com or service2 they both use wildcard certificate from the tls option in service1 block. Is this behavior intentional? If yes then why tls directive in not global?

PS: caddy 2.8.4 on Arch Linux

PPS: Docs page for tls directive never mentions this implicit behavior.

@polarathene
Copy link

polarathene commented Sep 7, 2024

both use wildcard certificate from the tls option in service1 block. Is this behavior intentional?

I don't think it is intentional. You should get a better idea with caddy adapt to view the JSON that Caddyfile is converted to.

There is a WIP feature PR that is meant to get the behaviour you describe from a wildcard site address becoming the priority over provisioning new certs for subdomains that the wildcard could be used for.

In that PR they state if you have a single domain you don't want to use wildcard with, you would not use the feature. But since you've relied on the tls directive that is externally provisioned cert rather than managed by Caddy, the behaviour you get is different AFAIK, so you've triggered a bug I assume (at least with the Caddyfile logic), and I don't think we have tls acme to explicitly configure the default cert provisioning logic 🤔 (which would probably resolve the linked PR use-case too where a user wants some site blocks to ignore the wildcard and provision a new cert).


EDIT: As pointed out below, I misunderstood the auto_https mode.. disable_certs instructed Caddy not to provision certs for a site address, so it found and used the one you manually loaded from an external source that was a valid match.

@francislavoie
Copy link
Member

francislavoie commented Sep 7, 2024

@vehlwn I think in your case, you want auto_https ignore_loaded_certs, not disable_certs. See https://caddyserver.com/docs/caddyfile/options#tls-options

The reason the Caddyfile's tls was designed as a directive in sites is because it's convenient to be able to associate the site addresses (domains) with the TLS automation policies (i.e. how to get a cert for the domains of that site) or TLS connection policies (if you need client auth, requiring clients to pass a certificate). If it was global then you'd have to make that association yourself (i.e. duplicate your domain in the config). The TLS layer is "global" though (it's an "app", as @polarathene said you can adapt your config to JSON to get an idea of how it actually looks with caddy adapt -p). The auto_https modes are there to provide escape hatches for that automatic wiring behaviour.

@vehlwn
Copy link

vehlwn commented Sep 8, 2024

ignore_loaded_certs manages certificates automatically. I don't want caddy to issue different certificates for each domain because I don't want all my domains to be publicly available for hackers to scrap them. See https://crt.sh/?q=archlinux.org for example.

In nginx ssl_certificate option can be used outside any server block so multiple ssl servers can inherit it.

@francislavoie
Copy link
Member

I don't understand what your complaint is about then. Just use tls <cert> <key> as you already were.

@polarathene
Copy link

polarathene commented Sep 8, 2024

I don't want caddy to issue different certificates for each domain because I don't want all my domains to be publicly available for hackers to scrap them.

That is why you use the wildcard certificate.

In nginx ssl_certificate option can be used outside any server block so multiple ssl servers can inherit it.

So your actual complaint is not about the wildcard cert being used, but why each site block has tls directive instead of a global directive?

This was explained by looking at the JSON output.

  • Continue to rely on the behaviour you have already? (or as suggested be more explicit with the tls directive in each block, you can use a import <snippet-name-here> to make it easier to manage)
  • You can configure global default SNI (or was it SNI fallback?)... one of those will allow using that SNI to match a certificate for a site block that has say :443 without any FQDN if I recall.
  • Alternatively take the Caddy docs advice for handling wildcard as a site address, and in the site block for it use handle directive for each subdomain.

@francislavoie
Copy link
Member

I'll close this as inactive. I'm not sure there's anything actionable here.

@francislavoie francislavoie closed this as not planned Won't fix, can't repro, duplicate, stale Sep 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants