Working with events and placholders/variables

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

./caddy run --config caddy.json

a. System environment:

Linux Mint

b. Command:

./caddy run --config caddy.json

c. Service/unit/compose file:

N/A

d. My complete Caddy config:

{
	"admin": {
		"listen": "localhost:2022",
		"config": {
			"persist": false
		}
	},
	"storage": {
		"module": "file_system",
		"root": "/tmp/tmp.z9wZiSgqPF/storage/"
	},
	"logging": {
		"logs": {
			"default": {
				"level": "DEBUG"
			}
		}
	},
	"apps": {
		"tls": {
			"certificates": {
				"automate": [
					"localhost"
				]
			},
			"automation": {
				"policies": [
					{
						"subjects": [
							"localhost"
						],
						"issuers": [
							{
								"module": "internal",
								"ca": "customca",
								"lifetime": 300000000000
							}
						]
					}
				]
			}
		},
		"pki": {
			"certificate_authorities": {
				"customca": {
					"name": "my custom CA name",
					"root_common_name": "custom CA - root",
					"intermediate_common_name": "custom CA - intermediate",
					"install_trust": false
				},
				"local": {
					"install_trust": false
				}
			}
		},
		"events": {
			"subscriptions": [
				{
					"events": ["cert_obtained"],
					"handlers": [
						{
							"handler": "exec",
							"command": "/usr/bin/echo",
							"args": [
								"Storage path: {caddy.storage.file_system}",
								"Hostname: {event.data.identifier}"
							]
						}
					]
				}
			]
		}
	}
}

3. The problem I’m having:

I want to use placeholders in my Caddy configuration to get to certain configuration values I configured already. As of now I have to write the path of the tls cert storage by hand. Since Caddy knows where it puts its certs and or other things, I am wondering how I can access this data with placeholders within caddy itself.

4. Error messages and/or full log output:

I looked up how whether placeholders/variables is something I could use to get there, but that didn’t work.
I used

"{caddy.storage.file_system.root}",

to see whether this would resolve into the app dir directory, but no luck.
All in all I could not find much information on where placeholders exists and how to use them other than these two occasions:

I was under the impression one could use the module space with the dotted notation to access the data stored there. If not, then I’m unsure where all the current and default placeholders/variables are coming from (knowing core caddy only loads config and storage module).

2022/11/06 14:57:28.914 INFO    using provided configuration    {"config_file": "caddy.json", "config_adapter": ""}
2022/11/06 14:57:28.915 INFO    admin   admin endpoint started  {"address": "localhost:2022", "enforce_origin": false, "origins": ["//[::1]:2022", "//127.0.0.1:2022", "//localhost:2022"]}
2022/11/06 14:57:28.918 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc0004db730"}
2022/11/06 14:57:28.918 INFO    pki.ca.customca root certificate trust store installation disabled; unconfigured clients may show warnings      {"path": "storage:pki/authorities/customca/root.crt"}
2022/11/06 14:57:28.918 INFO    pki.ca.local    root certificate trust store installation disabled; unconfigured clients may show warnings      {"path": "storage:pki/authorities/local/root.crt"}
2022/11/06 14:57:28.919 INFO    serving initial configuration
2022/11/06 14:57:28.919 INFO    tls     cleaning storage unit   {"description": "FileStorage:/tmp/tmp.z9wZiSgqPF/storage/"}
2022/11/06 14:57:28.919 INFO    tls     finished cleaning storage units
2022/11/06 14:57:28.919 INFO    tls.obtain      acquiring lock  {"identifier": "localhost"}
2022/11/06 14:57:28.923 INFO    tls.obtain      lock acquired   {"identifier": "localhost"}
2022/11/06 14:57:28.923 INFO    tls.obtain      obtaining certificate   {"identifier": "localhost"}
2022/11/06 14:57:28.923 DEBUG   events  event   {"name": "cert_obtaining", "id": "dbff38b0-8b2e-4c15-a803-08af677d2798", "origin": "tls", "data": {"identifier":"localhost"}}
2022/11/06 14:57:28.923 DEBUG   tls.obtain      trying issuer 1/1       {"issuer": "customca"}
2022/11/06 14:57:28.924 DEBUG   pki.ca.customca using intermediate signer       {"serial": "317642555269810020570392108369604340405", "not_before": "2022-11-06 14:57:28 +0000 UTC", "not_after": "2022-11-13 14:57:28 +0000 UTC"}
2022/11/06 14:57:28.924 INFO    tls.obtain      certificate obtained successfully       {"identifier": "localhost"}
2022/11/06 14:57:28.924 DEBUG   events  event   {"name": "cert_obtained", "id": "75d25453-329e-4d2c-a40f-4aabd4eaf771", "origin": "tls", "data": {"identifier":"localhost","issuers":"customca","renewal":false,"storage_key":"localhost"}}
2022/11/06 14:57:28.924 INFO    tls.obtain      releasing lock  {"identifier": "localhost"}
2022/11/06 14:57:28.925 WARN    tls     stapling OCSP   {"error": "no OCSP stapling for [localhost]: no OCSP server specified in certificate", "identifiers": ["localhost"]}
2022/11/06 14:57:28.925 DEBUG   tls.cache       added certificate to cache      {"subjects": ["localhost"], "expiration": "2022/11/06 15:02:29.000", "managed": true, "issuer_key": "customca", "hash": "3fd9631d80719f0548112ecbcacfc7f2f9bc5256f051a5e03a24461618733883", "cache_size": 1, "cache_capacity": 10000}
2022/11/06 14:57:28.925 DEBUG   events  event   {"name": "cached_managed_cert", "id": "40c3e03c-d605-47f5-bd1e-751d81aa4c80", "origin": "tls", "data": {"sans":["localhost"]}}
Storage path:  Hostname: localhost
2022/11/06 14:57:38.377 INFO    admin.api       received request        {"method": "POST", "host": "localhost:2022", "uri": "/stop", "remote_ip": "127.0.0.1", "remote_port": "35748", "headers": {"Accept":["*/*"],"Content-Type":["application/json"],"User-Agent":["curl/7.68.0"]}}
2022/11/06 14:57:38.377 WARN    admin.api       exiting; byeee!! 👋
2022/11/06 14:57:38.377 INFO    tls.cache.maintenance   stopped background certificate maintenance      {"cache": "0xc0004db730"}
2022/11/06 14:57:38.377 INFO    admin   stopped previous server {"address": "localhost:2022"}
2022/11/06 14:57:38.377 INFO    admin.api       shutdown complete       {"exit_code": 0}

Eventually I want to make this work but without hardcoded paths if possible:

{
    "events": {
        "subscriptions": [
            {
                "events": [
                    "cert_obtained"
                ],
                "handlers": [
                    {
                        "handler": "exec",
                        "command": "/usr/bin/cp",
                        "args": [
                            "/tmp/tmp.z9wZiSgqPF/storage/certificates/customca/{event.data.identifier}/{event.data.identifier}.crt",
                            "/tmp/tmp.z9wZiSgqPF/storage/certificates/customca/{event.data.identifier}/{event.data.identifier}.key",
                            "/tmp/tmp.z9wZiSgqPF/"
                        ]
                    }
                ]
            }
        ]
    }
}

5. What I already tried:

Use placeholders to dynamically insert the correct app dir path.

6. Links to relevant resources:

See above

1 Like

I could also just have a script that’s being called by the event handler which gets the path via the admin API… not sure what’s the smartest/easiest way to tackle this. :thinking:

Hmm, good question. We could probably provide some placeholders for the globally configured storage location.

Probably makes sense to open this as a feature request on GitHub :+1:

1 Like

Yeah, that’s interesting.

But what if the storage backend doesn’t use files? The storage keys are not necessarily paths, for example if they’re stored in a DB they’re just… strings.

1 Like

Yeah this would only work for the file storage driver. It should add itself to the global replacer or something, I guess.

Hey you two - thanks for chiming in and sorry for not getting back to you.

Ok, then I’ll head on to GitHub and open a feature request issue.

But then maybe the string is something which one can build on to with other tooling - a script, a program that reads in the string and from there can get what is needed? This is super vague as I have no idea how this would look like in a real Caddy config yet :smiley:

Ah and yes, so for now I’ll just stick to using a static path and that works just the way I need it.
Thanks for bringing the event system to Caddy :tada:

1 Like