garbash-www

archive of ~alex garbash.com page
git clone git://git.alexkarle.com.com/garbash-www
Log | Files | Refs | README | LICENSE

013-wildcard-cert.md (3301B) [raw]


      1 ---
      2 title: 013-wildcard-cert
      3 ---
      4 
      5 # 013-wildcard-cert
      6 
      7 Thur Apr 28, 2022
      8 
      9 I've been running two internal services for a while:
     10 
     11 * lists.garbash.com -- mailing list archive
     12 * irc.garbash.com -- [gamja](https://git.sr.ht/~emersion/gamja) IRC client
     13   (and [soju](https://soju.im) bouncer)
     14 
     15 Because we're not using split DNS (hosting our own DNS server for
     16 clients on the VPN), these are kept internal only by having the public
     17 DNS have the internal IP addresses:
     18 
     19 	$ host irc.garbash.com
     20 	irc.garbash.com has address 10.6.6.1
     21 
     22 This works great, except it becomes harder to obtain a TLS certificate.
     23 My favorite way to get a TLS certificate on OpenBSD is acme-client(8),
     24 which is in base and works out of the box but does not support dns-01
     25 ACME challenges required for wildcard certs. As such, all sites requiring
     26 certs need to be publicly accessible for HTTP challenges.
     27 
     28 It would have been OK to serve over plain HTTP (in the sense that the
     29 services are served over a wireguard tunnel, so they're already encrypted),
     30 but browsers only allow desktop notifications for HTTPS sites, so to get
     31 notifications for `gamja`, I needed a wildcard cert.
     32 
     33 The wildcard cert turned out to be not too hard. For a couple months I
     34 used [`uacme`](https://github.com/ndilieto/uacme) because it was in ports,
     35 but getting the client to update our DNS in Linode wasn't supported in
     36 the upstream project (as far I can tell). So for a couple times I actually
     37 ran the tool with manual DNS mode--updating the TXT records by hand myself.
     38 
     39 This clearly isn't sustainable (mostly because it requires remembering every
     40 couple months to redo it), so I moved to [`acme.sh`](https://github.com/acmesh-official/acme.sh),
     41 which, despite not being in ports, was super easy to install and use.
     42 
     43 To get the new certs, I created a new user:
     44 
     45 	# adduser
     46 	... acmesh, nologin, daemon, etc ...
     47 
     48 Then I created a `certs` group so that all the services that need the certs
     49 can read the certificates:
     50 
     51 	# addgroup certs
     52 	# usermod -G _soju certs
     53 	# usermod -G acmesh certs
     54 
     55 I had to manually `chmod` some of the directories of `acme.sh` to allow
     56 group-writable, and `chown` those directories to `acmesh:certs`.
     57 
     58 Finally, installing the cert was as simple as:
     59 
     60 	$ export LINODE_V4_API_KEY=...
     61 	$ ./acme.sh --install -m alex@garbash.com  # one time
     62 	$ ./acme.sh --issue --dns dns_linode_v4 --dnssleep 300 -d *.garbash.com
     63 
     64 This installed the certs to `/home/acmesh/.acme.sh`. `httpd(8)` needs the
     65 fullchain and private key:
     66 
     67 
     68 	server "lists.garbash.com" {
     69 		listen on * tls port 443
     70 		directory auto index
     71 		root "/htdocs/lists"
     72 		tls {
     73 			certificate "/home/acmesh/.acme.sh/*.garbash.com/fullchain.cer"
     74 			key "/home/acmesh/.acme.sh/*.garbash.com/*.garbash.com.key"
     75 		}
     76 		location "/.well-known/acme-challenge/*" {
     77 			root "/acme"
     78 			request strip 2
     79 		}
     80 	}
     81 
     82 
     83 The final step is to modify the crontab to restart the services
     84 when it runs successfully! Since this is running as the `acmesh`
     85 user, I needed to give it permission to run the `rcctl` command
     86 passwordless by adding the following to doas.conf:
     87 
     88 	permit nopass acmesh as root cmd /usr/sbin/rcctl args restart httpd
     89 
     90 Adding the following to the crontab will cause it to run on success:
     91 
     92 	--reloadcmd '/usr/sbin/rcctl restart httpd'
     93 
     94 Hopefully I won't need to think about this for a while!