Skip to content

Check Certificates #4397

Check Certificates

Check Certificates #4397

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-certificates.md
name: Check Certificates
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
on:
create:
push:
paths:
- '.github/workflows/check-certificates.ya?ml'
pull_request:
paths:
- '.github/workflows/check-certificates.ya?ml'
schedule:
# Run every 10 hours.
- cron: '0 */10 * * *'
workflow_dispatch:
repository_dispatch:
env:
# Begin notifications when there are less than this many days remaining before expiration.
EXPIRATION_WARNING_PERIOD: 30
jobs:
run-determination:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.determination.outputs.result }}
steps:
- name: Determine if the rest of the workflow should run
id: determination
run: |
RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x"
REPO_SLUG="arduino/arduino-ide"
if [[
(
# Only run on branch creation when it is a release branch.
# The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead.
"${{ github.event_name }}" != "create" ||
"${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX
) &&
(
# Only run when the workflow will have access to the certificate secrets.
# This could be done via a GitHub Actions workflow conditional, but makes more sense to do it here as well.
(
"${{ github.event_name }}" != "pull_request" &&
"${{ github.repository }}" == "$REPO_SLUG"
) ||
(
"${{ github.event_name }}" == "pull_request" &&
"${{ github.event.pull_request.head.repo.full_name }}" == "$REPO_SLUG"
)
)
]]; then
# Run the other jobs.
RESULT="true"
else
# There is no need to run the other jobs.
RESULT="false"
fi
echo "result=$RESULT" >> $GITHUB_OUTPUT
check-certificates:
name: ${{ matrix.certificate.identifier }}
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
certificate:
# Additional certificate definitions can be added to this list.
- identifier: macOS signing certificate # Text used to identify certificate in notifications.
certificate-secret: APPLE_SIGNING_CERTIFICATE_P12 # Name of the secret that contains the certificate.
password-secret: KEYCHAIN_PASSWORD # Name of the secret that contains the certificate password.
type: pkcs12
- identifier: Windows signing certificate
certificate-secret: INSTALLER_CERT_WINDOWS_CER
# The password for the Windows certificate is not needed, because its not a container, but a single certificate.
type: x509
steps:
- name: Set certificate path environment variable
run: |
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
echo "CERTIFICATE_PATH=${{ runner.temp }}/certificate.p12" >> "$GITHUB_ENV"
- name: Decode certificate
env:
CERTIFICATE: ${{ secrets[matrix.certificate.certificate-secret] }}
run: |
echo "${{ env.CERTIFICATE }}" | base64 --decode > "${{ env.CERTIFICATE_PATH }}"
- name: Verify certificate
env:
CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
run: |
(
openssl ${{ matrix.certificate.type }} \
-in "${{ env.CERTIFICATE_PATH }}" \
-legacy \
-noout \
-passin env:CERTIFICATE_PASSWORD
) || (
echo "::error::Verification of ${{ matrix.certificate.identifier }} failed!!!"
exit 1
)
- name: Slack notification of certificate verification failure
if: failure()
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_MESSAGE: |
:warning::warning::warning::warning:
WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} verification failed!!!
:warning::warning::warning::warning:
SLACK_COLOR: danger
MSG_MINIMAL: true
uses: rtCamp/action-slack-notify@v2
- name: Get days remaining before certificate expiration date
env:
CERTIFICATE_PASSWORD: ${{ secrets[matrix.certificate.password-secret] }}
id: get-days-before-expiration
run: |
if [[ ${{ matrix.certificate.type }} == "pkcs12" ]]; then
EXPIRATION_DATE="$(
(
openssl pkcs12 \
-in "${{ env.CERTIFICATE_PATH }}" \
-clcerts \
-legacy \
-nodes \
-passin env:CERTIFICATE_PASSWORD
) | (
openssl x509 \
-noout \
-enddate
) | (
grep \
--max-count=1 \
--only-matching \
--perl-regexp \
'notAfter=(\K.*)'
)
)"
elif [[ ${{ matrix.certificate.type }} == "x509" ]]; then
EXPIRATION_DATE="$(
(
openssl x509 \
-in ${{ env.CERTIFICATE_PATH }} \
-noout \
-enddate
) | (
grep \
--max-count=1 \
--only-matching \
--perl-regexp \
'notAfter=(\K.*)'
)
)"
fi
DAYS_BEFORE_EXPIRATION="$((($(date --utc --date="$EXPIRATION_DATE" +%s) - $(date --utc +%s)) / 60 / 60 / 24))"
# Display the expiration information in the log.
echo "Certificate expiration date: $EXPIRATION_DATE"
echo "Days remaining before expiration: $DAYS_BEFORE_EXPIRATION"
echo "days=$DAYS_BEFORE_EXPIRATION" >> $GITHUB_OUTPUT
- name: Check if expiration notification period has been reached
id: check-expiration
run: |
if [[ ${{ steps.get-days-before-expiration.outputs.days }} -lt ${{ env.EXPIRATION_WARNING_PERIOD }} ]]; then
echo "::error::${{ matrix.certificate.identifier }} will expire in ${{ steps.get-days-before-expiration.outputs.days }} days!!!"
exit 1
fi
- name: Slack notification of pending certificate expiration
# Don't send spurious expiration notification if verification fails.
if: failure() && steps.check-expiration.outcome == 'failure'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_MESSAGE: |
:warning::warning::warning::warning:
WARNING: ${{ github.repository }} ${{ matrix.certificate.identifier }} will expire in ${{ steps.get-days-before-expiration.outputs.days }} days!!!
:warning::warning::warning::warning:
SLACK_COLOR: danger
MSG_MINIMAL: true
uses: rtCamp/action-slack-notify@v2