Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions .github/workflows/cert-renewal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
name: Certificate Renewal

# on:
# schedule:
# # Run on the 1st of every month at 00:00 UTC
# # GitHub Actions doesn't support "every 800 days" directly,
# # so we check monthly if renewal is needed
# - cron: '0 0 1 * *'
# workflow_dispatch: # Allow manual triggering
on:
push:
branches-ignore:
- 'main'
permissions:
contents: write
pull-requests: write

jobs:
check-and-renew-certificates:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Check certificate expiration
id: check-cert
run: |
# Get the expiration date of the current certificate
CERT_FILE="tests/resources/unittests.crt"

if [ ! -f "$CERT_FILE" ]; then
echo "Certificate file not found!"
echo "needs_renewal=true" >> $GITHUB_OUTPUT
exit 0
fi

# Get certificate expiration date in seconds since epoch
EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$EXPIRY_DATE" +%s)

# Get current date in seconds since epoch
CURRENT_EPOCH=$(date +%s)

# Calculate days until expiration
DAYS_UNTIL_EXPIRY=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 ))

echo "Certificate expires in $DAYS_UNTIL_EXPIRY days"

# Renew if less than 30 days until expiration
# This gives us buffer time before the 825-day Apple limit
if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
echo "Certificate needs renewal (less than 30 days until expiration)"
echo "needs_renewal=true" >> $GITHUB_OUTPUT
else
echo "Certificate is still valid"
echo "needs_renewal=false" >> $GITHUB_OUTPUT
fi

- name: Install OpenSSL
if: steps.check-cert.outputs.needs_renewal == 'true'
run: |
sudo apt-get update
sudo apt-get install -y openssl

- name: Regenerate certificates
if: steps.check-cert.outputs.needs_renewal == 'true'
working-directory: tests/resources
run: |
# Regenerate the certificate (824 days to stay under Apple's 825-day limit)
openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824

# Regenerate the PKCS#12 bundle with macOS-compatible encryption
# Using SHA1 and 3DES instead of modern algorithms that macOS Security Framework doesn't support
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

# Verify the new certificate
echo "New certificate details:"
openssl x509 -in unittests.crt -noout -dates -subject

- name: Check for changes
if: steps.check-cert.outputs.needs_renewal == 'true'
id: check-changes
run: |
if git diff --quiet tests/resources/unittests.crt tests/resources/unittests.p12; then
echo "No changes detected"
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "Changes detected"
echo "has_changes=true" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request
if: steps.check-cert.outputs.needs_renewal == 'true' && steps.check-changes.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Create branch
BRANCH_NAME="cert-renewal-${{ github.run_number }}"
git checkout -b "$BRANCH_NAME"

# Stage and commit changes
git add tests/resources/unittests.crt tests/resources/unittests.p12
git commit -m "chore: renew test certificates for 824 days

Automatically regenerated test certificates to comply with Apple's
825-day certificate lifetime requirement.

Generated using:
- openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
- 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

🤖 Assisted by GenAI"

# Push branch
git push origin "$BRANCH_NAME"

# Create pull request using GitHub CLI
gh pr create \
--title "chore: renew test certificates" \
--body "## Certificate Renewal

This PR automatically renews the test certificates in \`tests/resources/\`.

### Changes
- Updated \`tests/resources/unittests.crt\` (renewed for 824 days)
- Updated \`tests/resources/unittests.p12\` (PKCS#12 bundle)

### Background
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.

### Verification
The certificates were regenerated using the commands documented in \`tests/resources/unittests.readme\`:
\`\`\`bash
openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
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
\`\`\`

### Testing
Please verify that the unit tests pass with the new certificates before merging.

---
🤖 This PR was automatically generated by the certificate renewal workflow." \
--base main \
--head "$BRANCH_NAME"
43 changes: 22 additions & 21 deletions tests/resources/unittests.crt
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID7DCCAtSgAwIBAgIJALEv6FDrJ/8NMA0GCSqGSIb3DQEBCwUAMIGaMQswCQYD
VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEP
MA0GA1UECgwGQW1hem9uMQ0wCwYDVQQLDARTREtzMRIwEAYDVQQDDAlsb2NhbGhv
c3QxMDAuBgkqhkiG9w0BCQEWIWF3cy1zZGstY29tbW9uLXJ1bnRpbWVAYW1hem9u
LmNvbTAeFw0yMzA5MTgxNDIyMTZaFw0yNTEyMjAxNDIyMTZaMIGaMQswCQYDVQQG
EwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEPMA0G
A1UECgwGQW1hem9uMQ0wCwYDVQQLDARTREtzMRIwEAYDVQQDDAlsb2NhbGhvc3Qx
MDAuBgkqhkiG9w0BCQEWIWF3cy1zZGstY29tbW9uLXJ1bnRpbWVAYW1hem9uLmNv
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANdqV0j4DkQDJULWEW8b
s/znGqK2p9wthY8o4btL7nEhGUsMQyae+UwUBDGn0qUhCgEC3g7e8bg0Q2J+dleF
BOnBfsU1obc7H+5oTf5R2gz3L0dgEjwBJM5IpfCgi2OHurU8UsEPe7KZTbhGdPfR
6CWE0yxWkXiH3dQ982dRGHEsPMPhmdksRFH2FEi9ghZiGEpEI55bCQiKQqBoA4gQ
D2yFCTtylgQ19CYBg28d1n941xv2Ok+tyz7DvgEttEQr3BBdBf65QyDcyORABztU
zhHfXyjrviQCtOj8NZu+wYDqxOxbbyBu5GDVbjhD3iJzh5Drqq8g4rAdT8IsjzSG
6nUCAwEAAaMzMDEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGgYDVR0RBBMwEYIJbG9j
YWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQDSURKIog6XLQQDbVpyfAW0
V8exQDzWyjwSF+ZwiTzATPZAiRg5K4UcBa9rB/+I9nkkWeSBBBSYlF5D4QKPEp9a
fZLQ5GRU4AQ1FOQyvvbt+bQJx5nEE68ebuVPkZVQdHlQKmrJVuOzFlO+6tZwvyfP
YppnMJsQawlRgZqPKAronU/5U2S7Z3CPHzAhWH3TsyJAEuu94UabLE3cXM2243rN
HOT7JxKHrCxJotxvsxQEl42wwSZ7tw2cIK6MtavLs3k7OpDol8uge7jWCE32oNQ2
heg5USXHy1qAdw7YXG0WjDq9WN8pz6FwNT51IQSKvn1dcLIVW2uLVv8wn/v69A8z
MIIEFjCCAv6gAwIBAgIUKAsZ3T/7cwYyl/Ho4a6kxByVHYEwDQYJKoZIhvcNAQEL
BQAwgZoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH
DAdTZWF0dGxlMQ8wDQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxEjAQBgNV
BAMMCWxvY2FsaG9zdDEwMC4GCSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVu
dGltZUBhbWF6b24uY29tMB4XDTI1MTIyMjIxNTMwMloXDTI4MDMyNTIxNTMwMlow
gZoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT
ZWF0dGxlMQ8wDQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxEjAQBgNVBAMM
CWxvY2FsaG9zdDEwMC4GCSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVudGlt
ZUBhbWF6b24uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA12pX
SPgORAMlQtYRbxuz/Ocaoran3C2Fjyjhu0vucSEZSwxDJp75TBQEMafSpSEKAQLe
Dt7xuDRDYn52V4UE6cF+xTWhtzsf7mhN/lHaDPcvR2ASPAEkzkil8KCLY4e6tTxS
wQ97splNuEZ099HoJYTTLFaReIfd1D3zZ1EYcSw8w+GZ2SxEUfYUSL2CFmIYSkQj
nlsJCIpCoGgDiBAPbIUJO3KWBDX0JgGDbx3Wf3jXG/Y6T63LPsO+AS20RCvcEF0F
/rlDINzI5EAHO1TOEd9fKOu+JAK06Pw1m77BgOrE7FtvIG7kYNVuOEPeInOHkOuq
ryDisB1PwiyPNIbqdQIDAQABo1IwUDATBgNVHSUEDDAKBggrBgEFBQcDATAaBgNV
HREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFA1xMLFkbCGnUD5dfnF7
5Wcuz9kUMA0GCSqGSIb3DQEBCwUAA4IBAQC0fwJwUv0I9AC99OfVzNU8kE6xpXnO
VXPQncsvdcs3dg35nk9xFM8vuG0ZhSdCQeB2cfGaoTlkiBtahsUH96xIKNknTzbT
JZZDS5omqc7wnJ63k8TM+vs2UgA51taIh79r82b9d5priFc5r4qP2TZM+eCSnKBA
zUYodc0KCaVdeUkUFV718u9JrIHvmZ7uqqvCmSGEeHF2bGiVnBDb6k3ZRBmgjPKA
H0ZfNsrdp2m+1ZJ3HuGGnzddHa5kO1lJyoTIMmW9si2nxaYJmAEom4tFbwq2WEBU
Fcx8nUt9jeOKQuhKR8hEQPAl+B9n8ruUY48SZ6ZUxle6UjL4imkoAjxI
-----END CERTIFICATE-----
Binary file modified tests/resources/unittests.p12
Binary file not shown.
5 changes: 4 additions & 1 deletion tests/resources/unittests.readme
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ Apple won't trust any certificate whose lifetime is over 825 days.
Once it expires unit tests will start failing and it will need to be updated like so:

$ openssl req -x509 -new -key unittests.key -config unittests.conf -out unittests.crt -days 824
$ openssl pkcs12 -export -out unittests.p12 -inkey unittests.key -in unittests.crt -password pass:1234
$ 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

Note: 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.