Secure way to store API keys in config

I know Caddy can read env variables but they don’t feel secure with systemd and may be more complicated to configure.

Here’s what I’ve come up with:

  • /etc/caddy/Caddyfile can be readable by all, it doesn’t contain any secrets. This allows a regular user (me) to check the config without sudoing as caddy or root.
  • import a file from Caddyfile from the site section, e.g.
*.example.com {
	import ./secrets/tls_letsencrypt.caddyfile
    ...
}
  • the imported file is readable by user caddy only, as a normal user I don’t really need to look at this file
  • the contents of the file is something like
tls {
	issuer acme {
		email "me@example.com"
		dns cloudflare "cloudflare_api_key_here"
		disable_http_challenge
		disable_tlsalpn_challenge
	}
}

Better/easier to manage ideas?

The answer is environment variables.

You can use {env.CF_API_TOKEN} in your Caddyfile, then set the env var as an override to the systemd service:

1 Like

That is not safe on a multi-user host and should not be the recommended way. Any user on the machine can run systemctl show caddy or systemctl cat caddy and see the env variables and their values.

Run this as a regular user (not caddy, not root):

$ systemctl cat caddy | grep Environment
Environment="foo=bar"

It’s probably not a good idea to run Caddy on a multi-user system anyways. But…

You could use EnvironmentFile= instead of Environment= and point it to a file that’s access restricted.

Or you can use Caddy’s --envfile flag:

Either way, environment variables should be used for secrets. Caddy’s current running config, adapted to JSON, gets saved to its config storage location in an autosave.json file, to make it possible to restart Caddy from the last run config (this is for the caddy-api usecase as mentioned here: Keep Caddy Running — Caddy Documentation). So using an {env.*} placeholder is necessary to not have the secret leak to the autosave file as well.

Can you please say more why? I don’t see Apache make that statement for example. What’s inherently insecure about Caddy?

Caddy’s admin API endpoint listens on localhost:2019. Any user on the same machine can access it. Including fetching the current running config.

But that can be disabled. Is there a guide on how to harden Caddy?

It’s not a goal of the Caddy project to solve the “shared hosting” or “multi-user system” usecases. There’s no official guide for that.

Er, I’m pretty sure Caddy’s fine to deploy on multi-user systems as long as you know what you’re doing.

Generally, though, I recommend running 1 Caddy instance and then have an arbiter decide who can do what to the config.

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