Setting up a Caddy pki based on a Windows Root CA, (was:getting pki config..)

1. Caddy version (caddy version):

v2.4.0-beta.1 h1:Ed/tIaN3p6z8M3pEiXWJL/T8JmCqV62FrSJCHKquW/I=

2. How I run Caddy:

caddy run

a. System environment:

Windows 10, 20H2

b. Command:

caddy run

c. Service/unit/compose file:

none

d. My complete Caddyfile or JSON config:

localhost {
  tls internal
  respond "local caddy server"
}

3. The problem I’m having:

I want to configure either the “internal” pki issuer or define a new one.

Background: i want to setup a pki issuer with a root certificate/key pair based on a Windows CA, where i can get a certificate for a “subordinate CA”. The root certificate from the Windows CA is already distributed, so i won’t have to distribute another one.

I wanted to look how the “internal” issuer is defined via looking at th localhost:2019/config endpoint, but the pki app configuration is not included.

4. Error messages and/or full log output:

the config endpoint gives me this config (for the minimal Caddyfile):

    "apps": {
        "http": {
            "servers": {
                "srv0": {
                    "listen": [
                        ":443"
                    ],
                    "routes": [
                        {
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "body": "local caddy server",
                                                    "handler": "static_response"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "match": [
                                {
                                    "host": [
                                        "localhost"
                                    ]
                                }
                            ],
                            "terminal": true
                        }
                    ]
                }
            }
        },
        "tls": {
            "automation": {
                "policies": [
                    {
                        "issuers": [
                            {
                                "module": "internal"
                            }
                        ],
                        "subjects": [
                            "localhost"
                        ]
                    }
                ]
            }
        }
    }
}

5. What I already tried:

i searched for an example for configuring the pki app, but could not find one.

i also tried to get a config via localhost:2019/config/apps/pki , but the i got the answer “null”

The internal issuer is working, i get a certificate from " [Caddy Local Authority - ECC Intermediate]" and the certificate files are in the storage in the pki/authorities/local/ directory

Is this behavior (no pki config via :2019 endpoint) wanted? Or should the config be downloadable?

P.S.: when i get this working i will write a wiki entry for this

6. Links to relevant resources:

Your config implies the defaults for the pki app, which is why it doesn’t appear in your config. The default is an authority called “local”.

The Caddyfile doesn’t give you much in terms of control for the pki app. For now, you’ll need to use JSON to configure it:

Ah, OK. I never thought that the default config would not be output.

i adapted the Caddyfile to .json and added a configuration part for the pki app:

	"apps": {
		"http": {
			"servers": {
				"srv0": {
					"listen": [
					":443"
					],
					"routes": [ {
						"match": [ {
							"host": [
							"localhost"
							]
						}
						],
						"handle": [ {
							"handler": "subroute",
							"routes": [ {
								"handle": [ {
									"body": "local caddy server",
									"handler": "static_response"
								}
								]
							}
							]
						}
						],
						"terminal": true
					}
					]
				}
			}
		}		,
		"tls": {
			"automation": {
				"policies": [ {
					"subjects": [
					"localhost"
					],
					"issuers": [ {
						"module": "internal"
					}
					]
				}
				]
			}
		}		,
		"pki": {
			"certificate_authorities": {
				"local": {
					"name": "Local Ca"
				}
			}
		}
	}
}

I simply changed the “name” of the local issuer. After deleteing the certificates and keys and restarting caddy i got now certificates with the new name.
OK, next step is to configure the rest of the issuer. Tomorrow…
Btw. the :2019/config endpoint now shows the config for pki app, but only the non-default value.
Maybe there could be an endpoint showing the full config, including the default values.

The default values aren’t in the config but rather determined by runtime code during the provisioning step. The defaults are generally documented. This is because Caddy tries to be smart enough to do the right thing given a minimal config. It would be less user-friendly to write JSON config if you were required to define a local certificate authority for the internal issuer to work.

After some experimentation i got it working.
I defined an internal pki, used this to get a certificate for a host which i configured to be an acme_server.
Then i had a server which used this acme_server.
My Config:

	"apps": {
		"http": {
			"servers": {
				"srv0": {
					"listen": [
					":443"
					],
					"routes": [ {
						"match": [ {
							"host": [
							"jok-tarox.ipro.leo"
							]
						}						],
						"handle": [ {
							"handler": "subroute",
							"routes": [ {
								"handle": [ {
									"body": "jok-tarox.ipro.leo via caddy server",
									"handler": "static_response"
								}								]
							}							]
						}						],
						"terminal": true
					}					, {
						"match": [ {
							"host": [
							"localhost"
							]
						}						],
						"handle": [ {
							"handler": "subroute",
							"routes": [ {
								"handle": [ {
									"handler": "acme_server",
									"ca": "ipro"
								}								]
							}							]
						}						],
						"terminal": true
					}					],
					"automatic_https": {
						"disable_redirects": true
					}
				}
			}
		}		,
		"tls": {
			"automation": {
				"policies": [ {
					"subjects": [
					"localhost"
					],
					"issuers": [ {
						"module": "internal",
						"ca": "ipro"
					}					]
				}				, {
					"subjects": [
					"jok-tarox.ipro.leo"
					],
					"issuers": [ {
						"ca": "https://localhost/acme/ipro/directory",
						"email": "www@ipro.de",
						"module": "acme",
						"trusted_roots_pem_files": [
						"e:\\caddy\\root.crt"
						]
					}					]
				}				]
			}
		}		,
		"pki": {
			"certificate_authorities": {
				"ipro": {
					"name": "IPRO CA",
					"root_common_name": "pki.container.ipro.de",
					"install_trust": false,
					"root": {
						"certificate": "e:\\caddy\\root.crt",
						"private_key": "e:\\caddy\\root.key",
						"format": "pem_file"
					}
				}
			}
		}
	}
}

( had to disable the automatic redirects, because of some security restrictions binding to port 80, will test it again on a Linux system, where it will really be running in production)

One caveat: the “root_common_name” has to match the CN of the certificate, i first tried with some other name but then the resulting certificates wouldn’t be considered valid.

1 Like

too fast, in between the certificate from root.crt got installed as a “Trusted Root Certification Certificate”.
Which i don’t want. This certificate is signed by our Windows Server CA which is already distributed at all local machines.
After deleting the root.crt as “Trusted Root Certification Certificate”, i got again “NET::ERR_CERT_AUTHORITY_INVALID”. Back to experimentation… Tomorrow

ok, getting further. at the moment i got it semi-working (works ok with Firefox, only partial with Edge)

Some Background:
we have a Windows based CA, with an offline root ca and a intermediate ca running in the local network.
Both certicates are distributed in our LAN and i want to use them as base for the caddy acme server to issue certificates for servers in the LAN and DMZ.
Both Windows ca certificates have no limits in the pathlen (in the Basic constraints part)

Earlier i made the mistake of not setting the pathlen >1 in the csr for the intented caddy root ca certificate, but after correcting this issue, i ran into the next problem. Setting the “root” settings for my pki/certificate_authorities entry i got the following error starting Caddy:

021/02/26 01:03:44.983 INFO    tls.obtain      acquiring lock  {"identifier": "jok-yogas740.fritz.box"}
2021/02/26 01:03:45.021 INFO    tls.obtain      lock acquired   {"identifier": "jok-yogas740.fritz.box"}
2021/02/26 01:03:45.531 WARN    tls.issuance.acme.acme_client   HTTP request failed; retrying   {"url": "https://localhost/acme/ipro/directory", "error": "performing request: Get \"https://localhost/acme/ipro/directory\": x509: certificate signed by unknown authority (possibly because of \"x509: signature algorithm specifies an ECDSA public key, but have public key of type *rsa.PublicKey\" while trying to verify candidate authority certificate \"pki.container.ipro.de\")"}
2021/02/26 01:03:45.813 WARN    tls.issuance.acme.acme_client   HTTP request failed; retrying   {"url": "https://localhost/acme/ipro/directory", "error": "performing request: Get \"https://localhost/acme/ipro/directory\": x509: certificate signed by unknown authority (possibly because of \"x509: signature algorithm specifies an ECDSA public key, but have public key of type *rsa.PublicKey\" while trying to verify candidate authority certificate \"pki.container.ipro.de\")"}
2021/02/26 01:03:46.093 WARN    tls.issuance.acme.acme_client   HTTP request failed; retrying   {"url": "https://localhost/acme/ipro/directory", "error": "performing request: Get \"https://localhost/acme/ipro/directory\": x509: certificate signed by unknown authority (possibly because of \"x509: signature algorithm specifies an ECDSA public key, but have public key of type *rsa.PublicKey\" while trying to verify candidate authority certificate \"pki.container.ipro.de\")"}
2021/02/26 01:03:46.093 ERROR   tls.obtain      will retry      {"error": "[jok-yogas740.fritz.box] Obtain: registering account [mailto:www@ipro.de] with server: provisioning client: performing request: Get \"https://localhost/acme/ipro/directory\": x509: certificate signed by unknown authority (possibly because of \"x509: signature algorithm specifies an ECDSA public key, but have public key of type *rsa.PublicKey\" while trying to verify candidate authority certificate \"pki.container.ipro.de\")", "attempt": 1, "retrying_in": 60, "elapsed": 1.0707998, "max_duration": 2592000}

Only after manually generating the caddy intermediate ca myself as rsa type instead of as ecc (as caddy will do otherwise), i could get past this (also setting the key_type int the automation policy config ).

Now caddy started, but the certficates for both url (https://localhost/ and https://jok-yogas749.fritz.box) where still not recognized as valid.

The localhost i could get valid after, setting the /apps/tls/automation/policies/key_type to rsa406 and the /apps/tls/automation/policies/issuers/internal/sign_with_root/ to true.

Bot Firefox and Edge now see the correct certicate path:
Windows root ca - Windows ca - caddy root ca - localhost cert

Firefox also accepts the certificate of the acme_server issued url https://jok-yogas749.fritz.box, with certificate path:
Windows root ca - Windows ca - caddy root ca - caddy intermediate ca - jok-yoga cert

After visiting the jok-yoga site, both caddy ca’s are installed in the Firefox certificate store, without manual action from me. (i deleted them manually between tries)

Edge says the cert is still not valid, only having the caddy intermediate ca as parent of the host cert

Could a “sign_with_root” setting be made available for the acme_server handler, in the /apps/http/servers/routes/handle/acme_server/ config part ?

Then i think this would work in my use case.

My current config is:

 {

    "apps": {

        "http": {

            "servers": {

                "srv0": {

                    "listen": [

                    ":443"

                    ],

                    "routes": [ {

                        "match": [ {

                            "host": [

                            "jok-yogas740.fritz.box"

                            ]

                        }                       ],

                        "handle": [ {

                            "handler": "subroute",

                            "routes": [ {

                                "handle": [ {

                                    "body": "jok-yogas740.fritz.box via caddy server",

                                    "handler": "static_response"

                                }                               ]

                            }                           ]

                        }                       ],

                        "terminal": true

                    }                   , {

                        "match": [ {

                            "host": [

                            "localhost"

                            ]

                        }                       ],

                        "handle": [ {

                            "handler": "subroute",

                            "routes": [ {

                                "handle": [ {

                                    "handler": "acme_server",

                                    "ca": "ipro"

                                }                               ]

                            }                           ]

                        }                       ],

                        "terminal": true

                    }                   ]

                }

            }

        }       ,

        "tls": {

            "automation": {

                "policies": [ {

                    "subjects": [

                    "localhost"

                    ],

                    "issuers": [ {

                        "module": "internal",

                        "ca": "ipro",

                        "sign_with_root": true

                    }                   ],

                    "key_type": "rsa4096"

                }               , {

                    "subjects": [

                    "jok-yogas740.fritz.box"

                    ],

                    "issuers": [ {

                        "ca": "https://localhost/acme/ipro/directory",

                        "email": "www@ipro.de",

                        "module": "acme",

                        "trusted_roots_pem_files": [

                        "d:\\caddy\\root.crt",

                        "d:\\caddy\\ipro_ca.cer",

                        "d:\\caddy\\ipro-root-ca.cer"

                        ]

                    }                   ],

                    "key_type": "rsa4096"

                }               ]

            }

        }       ,

        "pki": {

            "certificate_authorities": {

                "ipro": {

                    "name": "Kumbi CA",

                    "root": {

                        "certificate": "d:\\caddy\\root.crt",

                        "private_key": "d:\\caddy\\root.key",

                        "format": "pem_file"

                    }                   ,

                    "intermediate": {

                        "certificate": "d:\\caddy\\intermediate.crt",

                        "private_key": "d:\\caddy\\intermediate.key",

                        "format": "pem_file"

                    }

                }

            }

        }

    }

}

Sure, we can try it out:

1 Like

Thanks Matt.

With the caddy.exe from caddy_Windows_go1.16_ec309c6.zip i got “secure connections” for the url that was using acme, from both Firefox and Edge with the acme_server handler option “sign_with_root” set to true.

Do you think it is useful to examine the error i got when i only supplied the caddy root cert and key? Mixing rsa and edcsa?

x509: certificate signed by unknown authority (possibly because of \"x509: signature algorithm specifies an ECDSA public key, but have public key of type *rsa.PublicKey\" while trying to verify candidate authority certificate 

i can provide the cert if you want.

I created a Wiki entry based on this: How to setup a custom Caddy PKI and ACME Server with an existing CA

1 Like

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