Conditionally set ca directive based on variable

1. The problem I’m having:

I’m trying to set the tls.ca directive conditionally based on an environment variable. Is this something that is possible or am I on a wild goose chase?

2. Error messages and/or full log output:

Here is what I’m trying:

*.example.com, example.com {
	# Set the CA directory based on the environment type
	map $ENVIRONMENT_TYPE {CA_DIRECTORY} {
		~(prod|production|PROD|PRODUCTION) https://acme-v02.api.letsencrypt.org/directory
		default https://acme-staging-v02.api.letsencrypt.org/directory
	}

	# Set TLS options for wildcard certificate
	tls {
		dns cloudflare {$CADDY_CLOUDFLARE_API_TOKEN}
		resolvers 1.1.1.1 8.8.8.8
		propagation_timeout 15m
		ca {CA_DIRECTORY}
	}
}

When I try that I get in the logs:

caddy-dev  | {"level":"error","ts":1742306177.432562,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"*.example.com","issuer":"-{CA_DIRECTORY}","error":"parse \"https://{CA_DIRECTORY}\": invalid character \"{\" in host name"}
caddy-dev  | {"level":"error","ts":1742306177.4325614,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"example.com","issuer":"-{CA_DIRECTORY}","error":"parse \"https://{CA_DIRECTORY}\": invalid character \"{\" in host name"}
caddy-dev  | {"level":"error","ts":1742306177.4325862,"logger":"tls.obtain","msg":"will retry","error":"[*.example.com] Obtain: parse \"https://{CA_DIRECTORY}\": invalid character \"{\" in host name","attempt":1,"retrying_in":60,"elapsed":0.000776375,"max_duration":2592000}
caddy-dev  | {"level":"error","ts":1742306177.4325912,"logger":"tls.obtain","msg":"will retry","error":"[example.com] Obtain: parse \"https://{CA_DIRECTORY}\": invalid character \"{\" in host name","attempt":1,"retrying_in":60,"elapsed":0.000784958,"max_duration":2592000}
Gracefully stopping... (press Ctrl+C again to force)

3. Caddy version:

v2.9.1

4. How I installed and ran Caddy:

Running Caddy in docker desktop on my arm mac book

b. Command:

docker compose up

c. Service/unit/compose file:

volumes:
  caddy_data:
  caddy_config:

services:
  caddy:
    image: serfriz/caddy-cloudflare-crowdsec:2
    container_name: caddy-${ENVIRONMENT_TYPE}
    restart: unless-stopped
    environment:
      - ENVIRONMENT_TYPE
      - CADDY_ACME_EMAIL
      - CADDY_CLOUDFLARE_API_TOKEN
      # - CADDY_CROWDSEC_API_TOKEN
      # - CADDY_CROWDSEC_API_HOST
      # - CADDY_CROWDSEC_API_PORT
    ports:
      - 127.0.0.1:2019:2019/tcp
      - 80:80/tcp
      - 443:443/tcp
      - 443:443/udp
    volumes:
      - caddy_data:/data
      - caddy_config:/config
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/conf.d:/etc/caddy/conf.d
      - ./caddy/sites.d:/etc/caddy/sites.d

d. My complete Caddy config:

{
	# Email for Let's Encrypt
	email {$CADDY_ACME_EMAIL}
	# Set TLS verification option
	acme_dns cloudflare {$CADDY_CLOUDFLARE_API_TOKEN}
	servers {
		# Set client IP headers
		client_ip_headers X-Forwarded-For X-Real-IP CF-Connecting-IP
		# Set trusted proxies to Cloudflare addresses
		trusted_proxies cloudflare {
			# Pull the Cloudflare IP addresses from the Cloudflare API every x hours
			interval 12h
			# Set timeout for the Cloudflare API request
			timeout 15s
		}
	}
}

*.example.com, example.com  {
	# Set the CA directory based on the environment type
	map $ENVIRONMENT_TYPE {CA_DIRECTORY} {
		~(prod|production|PROD|PRODUCTION) https://acme-v02.api.letsencrypt.org/directory
		default https://acme-staging-v02.api.letsencrypt.org/directory
	}

	# Set TLS options for wildcard certificate
	tls {
		dns cloudflare {$CADDY_CLOUDFLARE_API_TOKEN}
		resolvers 1.1.1.1 8.8.8.8
		propagation_timeout 15m
		ca {CA_DIRECTORY}
	}
}

5. Links to relevant resources:

I got the idea of trying to use a map from this: If/else (conditional) reverse_proxy

Change this:

map $ENVIRONMENT_TYPE {CA_DIRECTORY} {

to this:

map {$ENVIRONMENT_TYPE} {CA_DIRECTORY} {

See Environment variables for more details.

Never mind, even with that fix, Caddy will still use {CA_DIRECTORY} as a literal rather than evaluating its value.

Mapped placeholders aren’t evaluated until they’re used, which means your map won’t be processed until someone tries to browse your site. However, TLS configuration needs to be initialized during startup.

A better approach would be to define CA_DIRECTORY as an environment variable based on your ENVIRONMENT_TYPE, and then use:

tls {
    ca {$CA_DIRECTORY}
    # your other settings
}
1 Like

Got it, thank you! Like you said I’ll just define the directory as an env var.

1 Like

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