Skip to content

Commit 107d996

Browse files
committed
github: Add a test for certbot, using pebble.
1 parent 45aacf6 commit 107d996

File tree

6 files changed

+186
-0
lines changed

6 files changed

+186
-0
lines changed

ci/certbot/compose.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
services:
3+
zulip:
4+
environment:
5+
SSL_CERTIFICATE_GENERATION: certbot
6+
networks:
7+
zulip-backend:
8+
ipv4_address: 172.28.5.100
9+
depends_on:
10+
- pebble
11+
volumes: !override
12+
- ./ci/certbot/post-setup.d/:/data/post-setup.d/
13+
extra_hosts:
14+
- "zulip.example.net:172.28.5.100"
15+
16+
database:
17+
networks: [zulip-backend]
18+
memcached:
19+
networks: [zulip-backend]
20+
rabbitmq:
21+
networks: [zulip-backend]
22+
redis:
23+
networks: [zulip-backend]
24+
25+
pebble:
26+
image: ghcr.io/letsencrypt/pebble:latest
27+
volumes:
28+
- ./ci/certbot/pebble-config/:/config/
29+
command: -config /config/pebble-config.json -strict
30+
ports:
31+
- 14000:14000 # HTTPS ACME API
32+
- 15000:15000 # HTTPS Management API
33+
networks: [zulip-backend]
34+
extra_hosts:
35+
- "zulip.example.net:172.28.5.100"
36+
37+
networks:
38+
zulip-backend:
39+
driver: bridge
40+
ipam:
41+
driver: default
42+
config:
43+
- subnet: 172.28.0.0/16
44+
ip_range: 172.28.5.0/24
45+
gateway: 172.28.5.254

ci/certbot/env

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"pebble": {
3+
"listenAddress": "0.0.0.0:14000",
4+
"managementListenAddress": "0.0.0.0:15000",
5+
"certificate": "test/certs/localhost/cert.pem",
6+
"privateKey": "test/certs/localhost/key.pem",
7+
"httpPort": 80,
8+
"tlsPort": 443,
9+
"ocspResponderURL": "",
10+
"externalAccountBindingRequired": false,
11+
"domainBlocklist": ["blocked-domain.example"],
12+
"retryAfter": {
13+
"authz": 3,
14+
"order": 5
15+
},
16+
"keyAlgorithm": "ecdsa",
17+
"profiles": {
18+
"default": {
19+
"description": "The profile you know and love",
20+
"validityPeriod": 7776000
21+
}
22+
}
23+
}
24+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
# This configures certbot to talk to the Pebble instance we started
4+
echo "server=https://pebble:14000/dir" >>/etc/letsencrypt/cli.ini
5+
echo "no-verify-ssl=true" >>/etc/letsencrypt/cli.ini

ci/certbot/self-signed.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
services:
3+
zulip:
4+
environment:
5+
SSL_CERTIFICATE_GENERATION: self-signed

ci/certbot/test.sh

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/bin/bash
2+
3+
set -eux
4+
set -o pipefail
5+
6+
getcert() {
7+
echo | openssl s_client -showcerts -servername zulip.example.net -connect localhost:443 \
8+
| openssl x509 -text -noout
9+
}
10+
wait_for_log() {
11+
logline="$1"
12+
for _ in {1..60}; do
13+
if "${docker[@]:?}" logs zulip --no-log-prefix --no-color | grep "$logline"; then
14+
return 0
15+
fi
16+
sleep 1
17+
done
18+
19+
return 1
20+
}
21+
22+
success=0
23+
for _ in {1..60}; do
24+
getcert | tee cert.pem
25+
26+
if grep "Issuer: CN=Pebble Intermediate CA" cert.pem; then
27+
success=1
28+
break
29+
fi
30+
sleep 1
31+
done
32+
33+
if [ "${success}" = "0" ]; then
34+
echo "Timed out waiting for a Pebble-signed certificate to be served!"
35+
exit 1
36+
fi
37+
38+
## Test renewing -- this should generate and deploy a new certificate
39+
serial=$(grep "Serial Number:" cert.pem)
40+
"${docker[@]:?}" exec zulip /usr/bin/certbot renew --force-renew
41+
getcert | tee cert.pem
42+
newserial=$(grep "Serial Number:" cert.pem)
43+
if [ "${newserial}" = "${serial}" ]; then
44+
echo "Failed to renew -- same serial number?"
45+
exit 1
46+
fi
47+
# For simplicity below, we update $serial
48+
serial="$newserial"
49+
50+
## Restarting the container should not get a new certificate
51+
"${docker[@]}" stop zulip
52+
53+
"${docker[@]}" up zulip --wait
54+
# This will kick off runCertbotAsNeeded; we wait for a bit, tailing
55+
# the logs, until we see the "LetsEncrypt cert generated" line that we
56+
# expect at the end of it.
57+
if ! wait_for_log "LetsEncrypt cert generated"; then
58+
echo "Failed to run Certbot to completion!"
59+
exit 1
60+
fi
61+
getcert | tee cert.pem
62+
newserial=$(grep "Serial Number:" cert.pem)
63+
if [ "$newserial" != "$serial" ]; then
64+
echo "Restarting forced a renewal it should not have!"
65+
exit 1
66+
fi
67+
68+
## Switching to self-signed drops the cert
69+
"${docker[@]}" stop zulip
70+
71+
"${docker[@]}" -f ci/certbot/self-signed.yaml up zulip --wait
72+
logs="$("${docker[@]}" -f ci/certbot/self-signed.yaml logs zulip --no-log-prefix --no-color)"
73+
if echo "$logs" | grep -qEi 'certbot|letsencrypt'; then
74+
echo "Certbot detected in nominally self-signed configuration!"
75+
echo "$logs"
76+
exit 1
77+
fi
78+
79+
getcert | tee cert.pem
80+
if grep -qi pebble cert.pem; then
81+
echo "Certificate is from Pebble, not self-signed!"
82+
exit 1
83+
fi
84+
85+
## Even if certbot is renewed in the container, we still serve the configured self-signed cert
86+
"${docker[@]:?}" exec zulip /usr/bin/certbot renew --force-renew
87+
getcert | tee cert.pem
88+
if grep -qi pebble cert.pem; then
89+
echo "Certificate is from Pebble, not self-signed!"
90+
exit 1
91+
fi
92+
93+
## Switching _back_ to certbot still has the same cert, since it's not expired
94+
"${docker[@]}" stop zulip
95+
"${docker[@]}" up zulip --wait
96+
if ! wait_for_log "LetsEncrypt cert generated"; then
97+
echo "Failed to run certbot codepath in restarted container"
98+
exit 1
99+
fi
100+
getcert | tee cert.pem
101+
newserial=$(grep "Serial Number:" cert.pem)
102+
if [ "$newserial" != "$serial" ]; then
103+
echo "Restarting forced a renewal it should not have!"
104+
exit 1
105+
fi
106+
107+
exit 0

0 commit comments

Comments
 (0)