Caddy with FreeIPA ACME the Stubborn Way
Thomas Büttner
Im currently looking into caddy as a replacement for my rudimentary NGINX reverse proxy configuration, since i want to reinstall and maybe simplify some of my infrastructure i wanted to give caddy a try.
To test my Ignition config that i intend to use for the finally deployment of my new pet server i am spinning up a CoreOS VM and try some things out.
One of the things i wanted to try out was “real” TLS with caddy but since the Hostnames and the VM itself are not on the Internet i wanted to try the ACME facility in FreeIPAbecause there is a acme_ca
directive in the caddy documentation… and with that fun began.
The Problem
FreeIPAs ACME system works quite well if you use certbot - but it appears that caddy is in fact not certbot - so of course the whole thing didn’t work and i got this angry response from the pki-tomcat service:
1<h1>HTTP Status 500 – Internal Server Error</h1>
2<hr class=\"line\" />
3<p><b>Type</b> Exception Report</p>
4<p><b>Message</b> java.lang.Exception: Unsupported JWS algorithm: ES256</p>
Of course i did what any sysadmin would, ask the ether if someone else encountered this problem…
And found one other poor soul (post).
It turns out that caddy always requests new ACME Accounts with a P-256/ES256
type key, with no way to change the default (spoiler it is hardcoded).
To make matters worse dogtagpki has a similar default it only accepts RS256
type keys.
The (stubborn) “Solution”
Since the post in the caddy gave me at least some pointers i did some RTFS and skimmed through the caddy source code (open source to the rescue, also there is no way im touching my IPA).
After some digging i found the offender it is the package certmagic
which handles all the ACME stuff inside caddy, and of course the ACME account key type is hardcoded, but i have found a workaround…
Which consists of:
- Downloading the caddy source
- Running
go mod vendor
- Removing the readonly mode from the source file
- Applying a
horrendous hacksmall patch to certmagic package - Running
go build
With this little modification caddy is now able to request certificates from the FreeIPA ACME server.
The buts
But i hear you say “Doesn’t that mean that you-”:
- cannot use upstream binaries
- yes
- have to patch it every time caddy pushes an update
- also Yes
- get no support since your effectively running your own fork
- of course Yes
and the most important: “WHY?” because i am stubborn and don’t intent to use it for production this was merely a kind of Proof of Concept and challenge for myself if i manage to get this working, also this hack is basically just hardcoding a different algorithm so in no way better than the original problem.
The Patch
1diff --git a/account.go b/account.go
2index f3cb755..3d7bdd7 100644
3--- a/account.go
4+++ b/account.go
5@@ -21,6 +21,7 @@ import (
6 "crypto/ecdsa"
7 "crypto/elliptic"
8 "crypto/rand"
9+ "crypto/rsa"
10 "encoding/json"
11 "errors"
12 "fmt"
13@@ -76,7 +77,7 @@ func (*ACMEIssuer) newAccount(email string) (acme.Account, error) {
14 if email != "" {
15 acct.Contact = []string{"mailto:" + email} // TODO: should we abstract the contact scheme?
16 }
17- privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
18+ privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
19 if err != nil {
20 return acct, fmt.Errorf("generating private key: %v", err)
21 }