aboutsummaryrefslogtreecommitdiffstats
path: root/ops/tls.md
blob: d036d0512a2a7dc6993e7fbdabfc7709aed0bce6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
---
title: TLS documentation
---

There are two current approaches to maintaining TLS certificates:

1. [uacme](https://github.com/ndilieto/uacme)
2. [tlstunnel](https://sr.ht/~emersion/tlstunnel/)

Presently the latter is only used for pages.sr.ht.

## uacme

The configuration we use for uacme is a fucking nightmare, but it does work and
is pretty reliable. In the future it might be nice to switch to tlstunnel for
everything or at least put some more work into the uacme setup.

### Set up

We have a bunch of shell commands that sets up uacme on a server:

```
doas apk add uacme openssl moreutils
doas useradd -md /var/lib/acme -s /sbin/nologin acme
doas mkdir -p /etc/ssl/uacme/private /var/www/.well-known/acme-challenge
doas chown acme:acme /etc/ssl/uacme /etc/ssl/uacme/private
doas chmod g+rX /etc/ssl/uacme /etc/ssl/uacme/private
doas chown acme:acme /var/www/.well-known/acme-challenge
doas touch /var/log/acme.log
doas chown acme:acme /var/log/acme.log
doas vim /usr/local/bin/acme-update-certs
```

Contents of acme-update-certs, edit as necessary:

```
#!/bin/sh
exec >>/var/log/acme.log 2>&1
date

stats() {
    cert="/etc/ssl/uacme/$1/cert.pem"
    if ! [ -e "$cert" ]
    then
        return
    fi
    expiration=$(date -d"$(openssl x509 -enddate -noout -in "$cert" \
        | cut -d= -f2)" -D'%b %d %H:%M:%S %Y GMT' +'%s')
    printf '# TYPE certificate_expiration gauge\n'
    printf '# HELP certificate_expiration Timestamp when SSL certificate will expire\n'
    printf 'certificate_expiration{instance="%s"} %s\n' "$1" "$expiration"
}

acme() {
    site=$1
    shift
    /usr/bin/uacme -v -h /usr/share/uacme/uacme.sh issue $site $* || true
    stats $site | curl --data-binary @- https://push.metrics.sr.ht/metrics/job/$site
}

acme DOMAIN SUBDOMAIN...
```

```
doas nginx -s reload
doas chmod +x /usr/local/bin/acme-update-certs
doas usermod -aG acme nginx
doas -u acme uacme new sir@cmpwn.com
doas -u acme crontab -e
```

Contents of crontab:

```
MAILTO=sir@cmpwn.com
0 0 * * * chronic /usr/local/bin/acme-update-certs
```

Then update the nginx configuration, commenting out the includes for
port443.conf and \*-ssl.conf.

```
doas -u acme /usr/local/bin/acme-update-certs
cat /var/log/acme.log
```

Verify that acme.log looks okay, then uncomment the relevant parts of the nginx
configuration.

```
doas chmod -R g+rX /etc/ssl/uacme /etc/ssl/uacme/private
doas nginx -s reload
# verify TLS configuration
```

Note: wildcard certificates are possible with uacme, but it's a bloody nightmare
so if you want this it's best to go with tlstunnel instead.

### Monitoring

TLS certificate expiration and renewal is monitored by metrics.sr.ht:

[![](https://metrics.sr.ht/chart.svg?title=Days%20until%20certificate%20expiration&query=(certificate_expiration%20-%20time())/60/60/24&since=336h&label={{.instance}}&height=3.5&min=0)](https://metrics.sr.ht/graph?g0.expr=(certificate_expiration%20-%20time())&g0.tab=0&g0.stacked=0&g0.show_exemplars=0&g0.range_input=2w)

The acme update script pushes the expiration date to push.metrics.sr.ht whenever
the cronjob runs. If any certificate's expiration date falls below 1 week, the
"SSL expiration" alarm is raised.

## tlstunnel

tlstunnel automatically adds zero-configuration TLS to arbitrary TCP sockets
using SNI and the PROXY protocol. It is currently used for pages.sr.ht.

### Monitoring

Presently none; see https://todo.sr.ht/~emersion/tlstunnel/24