Skip to content

Commit 09f7f52

Browse files
authored
Automate the renew of the cert used in test (#540)
1 parent 1fbeb2e commit 09f7f52

3 files changed

Lines changed: 168 additions & 19 deletions

File tree

.github/workflows/cert-renewal.yml

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
name: Certificate Renewal
2+
3+
on:
4+
schedule:
5+
# Run on the 1st of every month at 00:00 UTC
6+
# GitHub Actions doesn't support "every 800 days" directly,
7+
# so we check monthly if renewal is needed
8+
- cron: '0 0 1 * *'
9+
workflow_dispatch: # Allow manual triggering
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
jobs:
16+
check-and-renew-certificates:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Check certificate expiration
23+
id: check-cert
24+
run: |
25+
# Get the expiration date of the current certificate
26+
CERT_FILE="tests/resources/unittests.crt"
27+
28+
if [ ! -f "$CERT_FILE" ]; then
29+
echo "Certificate file not found!"
30+
echo "needs_renewal=true" >> $GITHUB_OUTPUT
31+
exit 0
32+
fi
33+
34+
# Get certificate expiration date in seconds since epoch
35+
EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
36+
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$EXPIRY_DATE" +%s)
37+
38+
# Get current date in seconds since epoch
39+
CURRENT_EPOCH=$(date +%s)
40+
41+
# Calculate days until expiration
42+
DAYS_UNTIL_EXPIRY=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 ))
43+
44+
echo "Certificate expires in $DAYS_UNTIL_EXPIRY days"
45+
46+
# Renew if less than 30 days until expiration
47+
# This gives us buffer time before the 825-day Apple limit
48+
if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
49+
echo "Certificate needs renewal (less than 30 days until expiration)"
50+
echo "needs_renewal=true" >> $GITHUB_OUTPUT
51+
else
52+
echo "Certificate is still valid"
53+
echo "needs_renewal=false" >> $GITHUB_OUTPUT
54+
fi
55+
56+
- name: Install OpenSSL
57+
if: steps.check-cert.outputs.needs_renewal == 'true'
58+
run: |
59+
sudo apt-get update
60+
sudo apt-get install -y openssl
61+
62+
- name: Regenerate certificates
63+
if: steps.check-cert.outputs.needs_renewal == 'true'
64+
working-directory: tests/resources
65+
run: |
66+
# Regenerate the certificate (824 days to stay under Apple's 825-day limit)
67+
openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
68+
69+
# Regenerate the PKCS#12 bundle with macOS-compatible encryption
70+
# Using SHA1 and 3DES instead of modern algorithms that macOS Security Framework doesn't support
71+
openssl pkcs12 -export -out unittests.p12 -inkey unittests.key -in unittests.crt -password pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1
72+
73+
# Verify the new certificate
74+
echo "New certificate details:"
75+
openssl x509 -in unittests.crt -noout -dates -subject
76+
77+
- name: Check for changes
78+
if: steps.check-cert.outputs.needs_renewal == 'true'
79+
id: check-changes
80+
run: |
81+
if git diff --quiet tests/resources/unittests.crt tests/resources/unittests.p12; then
82+
echo "No changes detected"
83+
echo "has_changes=false" >> $GITHUB_OUTPUT
84+
else
85+
echo "Changes detected"
86+
echo "has_changes=true" >> $GITHUB_OUTPUT
87+
fi
88+
89+
- name: Create Pull Request
90+
if: steps.check-cert.outputs.needs_renewal == 'true' && steps.check-changes.outputs.has_changes == 'true'
91+
env:
92+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
93+
run: |
94+
# Configure git
95+
git config user.name "github-actions[bot]"
96+
git config user.email "github-actions[bot]@users.noreply.github.com"
97+
98+
# Create branch
99+
BRANCH_NAME="cert-renewal-${{ github.run_number }}"
100+
git checkout -b "$BRANCH_NAME"
101+
102+
# Stage and commit changes
103+
git add tests/resources/unittests.crt tests/resources/unittests.p12
104+
git commit -m "chore: renew test certificates for 824 days
105+
106+
Automatically regenerated test certificates to comply with Apple's
107+
825-day certificate lifetime requirement.
108+
109+
Generated using:
110+
- openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
111+
- openssl pkcs12 -export -out unittests.p12 -inkey unittests.key -in unittests.crt -password pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1
112+
113+
🤖 Assisted by GenAI"
114+
115+
# Push branch
116+
git push origin "$BRANCH_NAME"
117+
118+
# Create pull request using GitHub CLI
119+
gh pr create \
120+
--title "chore: renew test certificates" \
121+
--body "## Certificate Renewal
122+
123+
This PR automatically renews the test certificates in \`tests/resources/\`.
124+
125+
### Changes
126+
- Updated \`tests/resources/unittests.crt\` (renewed for 824 days)
127+
- Updated \`tests/resources/unittests.p12\` (PKCS#12 bundle)
128+
129+
### Background
130+
Apple requires that certificate lifetimes be 825 days or less. These certificates are used in unit tests that create TLS connections between localhost server and client.
131+
132+
### Verification
133+
The certificates were regenerated using the commands documented in \`tests/resources/unittests.readme\`:
134+
\`\`\`bash
135+
openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
136+
openssl pkcs12 -export -out unittests.p12 -inkey unittests.key -in unittests.crt -password pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1
137+
\`\`\`
138+
139+
### Testing
140+
Please verify that the unit tests pass with the new certificates before merging.
141+
142+
---
143+
🤖 This PR was automatically generated by the certificate renewal workflow." \
144+
--base main \
145+
--head "$BRANCH_NAME"

tests/resources/README

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# README FOR unittests.* files
2+
3+
These files are used in unit tests that create TLS connections between a
4+
localhost server and client. We use a single self-signed certificate which
5+
serves as both the server's certificate and the client's root CA.
6+
7+
* unittests.key: private key
8+
* unittests.crt: self-signed certificate
9+
* unittests.conf: configuration for generating unittests.crt
10+
* unittests.p12: pkcs#12 file bundling the certificate and private key. Password is "1234"
11+
12+
Apple won't trust any certificate whose lifetime is over 825 days.
13+
Once it expires unit tests will start failing and it will need to be updated like so:
14+
15+
```sh
16+
$ openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
17+
$ openssl pkcs12 -export -out unittests.p12 -inkey unittests.key -in unittests.crt -password pass:1234 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1
18+
```
19+
20+
Note:
21+
1. The PKCS#12 command uses SHA1 and 3DES encryption for macOS compatibility. Modern OpenSSL 3.x defaults to algorithms that macOS Security Framework cannot import. Thus, force it fallback to use the algo that the current macOS Security Framework supports.
22+
2. .github/workflows/cert-renewal.yml serves to automatically renew the certifications and runs every month to check if the cert is closer to expire.
23+
If new cert added/changed/removed, the automation scripts also need to be updated accordingly.

tests/resources/unittests.readme

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)