Custom directory for certificates?

1. The problem I’m having:

currently, because of the way my Caddyfile is setup, caddy grabs certificates from letsencrypt and zero-ssl … which is fine, except then depending on which authority is used, the actual certificate files are placed in one of two directories. this is awkward if one needs to symlink or periodically copy those certificates to other places. (e.g. prosody). i am wanting to have all my certs in one directory so my weekly copying of cert files to their relevant locations doesn’t bork if the cert is somehow issued by a different authority than last month.

2. Error messages and/or full log output:

no error logs for this

3. Caddy version:

caddy version
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

because the customised downloader sometimes acts up, and at one point when i tried to use xcaddy it borked (don’t remember the problem now), i have been building caddy myself. my custom caddy build includes these modules (looking at this list and it is entirely possible i have more than i need to have):

// plug in Caddy modules here
_ "github.com/caddyserver/caddy/v2/modules/standard"
_ "github.com/caddy-dns/digitalocean"
_ "github.com/caddyserver/replace-response"
_ "github.com/caddyserver/transform-encoder"
_ "github.com/caddyserver/nginx-adapter"
_ "github.com/caddyserver/cache-handler"
_ "github.com/aksdb/caddy-cgi/v2"

a. System environment:

debian 12, systemd

b. Command:

my systemd script:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile --pidfile /var/lib/caddy/caddy.pid
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force --pidfile /var/lib/caddy/caddy.pid
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

c. Service/unit/compose file:

d. My complete Caddy config:

i know this is supposed to be the entire Caddyfile, but mine is long so I copied the first few and last few lines here. the middle looks much the same as the last few

{
	email **redacted**
	acme_dns digitalocean **redacted**
	log {
		output file /var/log/caddy/caddy.log {
			roll_keep_for 10d
			roll_size 50MiB
			roll_local_time
			roll_key 10
		}
		level info
		format console {
			time_format rfc3339
			time_local
		}
	}
	order cgi before respond
}

* {
	tls internal
	import common.caddy2
}

https://jupiter.fyrf.li {
	import common.caddy2
	root * /u/local
	file_server browse
	respond @blocked "Access denied" 403
	# reverse_proxy localhost:8080
	# php_fastcgi localhost:9000
}

...
...
...

https://notes.fyrf.li {
	import common.caddy2
	# reverse_proxy :5230
	root * /u/standardnotes/packages/web
	file_server
}

https://archives.fyrf.li {
	import common.caddy2
	reverse_proxy :8030
}

https://gaming.fyrf.li {
	import common.caddy2
	reverse_proxy :10088
}

https://test.fyrf.li {
	import common.caddy2
	reverse_proxy :10110
}

https://rss.fyrf.li {
	import common.caddy2
	reverse_proxy :8011
	log {
		output file /home/camille/logs/freshrss.log
	}
}

the common.caddy2 file that is being imported looks like this is my custom error handling. it defines a few custom matchers (@blocked included) and then:

respond @blocked_referrers "Traffic blocked" 444
respond @bad_robots "Forbidden" 444
respond @scraper "Traffic blocked" 444

handle_errors {
    @404 expression `{err.status_code} == 404`
	@4xx expression `{err.status_code} in [403, 444, 410]`
	@5xx expression `{err.status_code} >= 500 && {err.status_code} < 600`

        handle @404 {
            redir https://404.fyrf.li
        }
	handle @4xx {
	    respond "Nope. Not a chance!"
	    redir https://example.com
	}
	handle @5xx {
	    respond "whoops ... something's really wrong"
	}
	handle {
	    respond "dunno what just happened ... but it was bad"
	}
}

i am fully aware that that * line is wonky and needs fixing. that’s another issue for another topic.

5. Links to relevant resources:

i am looking specifically at this page on the tls directive. when i started this topic, i noted the suggested topics and found this topic but that doesn’t quite help since it specifically refers to the json config which i don’t use (and i am not 100% confident in figuring out how to translate that).

You could probably improve your copying script to recursively go through all directories under a base directory and search for ‘.crt’ files.

Additionally, you could combine it with some openssl syntax to only copy certificates that have their issue date most recently to the current date, preventing expired or old certificates to be copied when both zerossl and letsencrypt locations have certificates for the same domain name.

I have recently wrote a script that returns certificates and their end dates because I consume that in monitoring, maybe you can adjust that to copy certificates instead?

3 Likes

that sounds like the perfect solution. i will take a look at doing it like you do in a few hours after i get some sleep. thanks!

1 Like

Our recommendation is to use events to trigger copying the certs to where you need them to go. This is because Caddy can choose to use either LE or ZeroSSL, and that choice means that a different filepath is used to store the certs. Using the event lets you watch for when a cert is renewed, and you get the correct path for that domain’s cert at that point in time.

2 Likes

oooh. i did not know this was possible. i’m going to have to dig into this. thank you!

so, the documentation does not go into any detail at all about this, so it’s back to just scripting it out on my own then. thanks anyway. something to keep an eye on.