Skip to content

Add template workflow to check for problems with signing certificates #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2021
Merged
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
53 changes: 53 additions & 0 deletions workflow-templates/check-certificates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# "Check Certificates" workflow

Workflow file: [check-certificates.yml](check-certificates.yml)

Check code signing certificates for problems or pending expiration.

## Setting up Slack webhook

1. Open https://arduino.slack.com/apps/A0F7XDUAZ-incoming-webhooks
1. Click the "Add to Slack" button
1. From the "Post to Channel" menu, select the appropriate channel.
1. Click the "Add Incoming WebHooks integration"
1. From the "Customize Icon" section, click the "Choose an emoji" button
1. Enter `:warning:`
1. Click the "Save Settings" button
1. Copy the text in the "Webhook URL" field
1. Save the webhook URL to a [repository secret](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `SLACK_WEBHOOK`
- Make sure there is no newline at the end of the secret, otherwise the slack post process will fail.

## Readme badge

Markdown badge:

```markdown
[![Check Certificates status](https://github.com/REPO_OWNER/REPO_NAME/actions/workflows/check-certificates.yml/badge.svg)](https://github.com/REPO_OWNER/REPO_NAME/actions/workflows/check-certificates.yml)
```

Replace the `REPO_OWNER` and `REPO_NAME` placeholders in the URLs with the final repository owner and name ([example](https://raw.githubusercontent.com/arduino-libraries/ArduinoIoTCloud/master/README.md)).

---

Asciidoc badge:

```adoc
image:https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-certificates.yml/badge.svg["Check Certificates status", link="https://github.com/{repository-owner}/{repository-name}/actions/workflows/check-certificates.yml"]
```

Define the `{repository-owner}` and `{repository-name}` attributes and use them throughout the readme ([example](https://raw.githubusercontent.com/arduino-libraries/WiFiNINA/master/README.adoc)).

## Commit message

```
Add CI workflow to check for problems with signing certificates

The workflow runs on a schedule to check for problems with the signing certificates and notify if any are found.
If a problem is found, a notification is posted to the Slack channel configured via the SLACK_WEBHOOK secret.
```

## PR message

```markdown
The workflow runs on a schedule to check for problems with the signing certificates and notify if any are found. If a problem is found, a notification is posted to the Slack channel configured via the `SLACK_WEBHOOK` secret.
```
130 changes: 130 additions & 0 deletions workflow-templates/check-certificates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# 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:
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:
check-certificates:
name: ${{ matrix.certificate.identifier }}
# Only run when the workflow will have access to the certificate secrets.
# TODO: Update repository name.
if: >
(github.event_name != 'pull_request' && github.repository == 'REPO_OWNER/REPO_NAME') ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'REPO_OWNER/REPO_NAME')
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: INSTALLER_CERT_MAC_P12 # Name of the secret that contains the certificate.
password-secret: INSTALLER_CERT_MAC_PASSWORD # Name of the secret that contains the certificate password.

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 pkcs12 \
-in "${{ env.CERTIFICATE_PATH }}" \
-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: |
EXPIRATION_DATE="$(
(
openssl pkcs12 \
-in "${{ env.CERTIFICATE_PATH }}" \
-clcerts \
-nodes \
-passin env:CERTIFICATE_PASSWORD
) | (
openssl x509 \
-noout \
-enddate
) | (
grep \
--max-count=1 \
--only-matching \
--perl-regexp \
'notAfter=(\K.*)'
)
)"

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 "::set-output name=days::$DAYS_BEFORE_EXPIRATION"

- 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# 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:
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:
check-certificates:
name: ${{ matrix.certificate.identifier }}
# Only run when the workflow will have access to the certificate secrets.
# TODO: Update repository name.
if: >
(github.event_name != 'pull_request' && github.repository == 'REPO_OWNER/REPO_NAME') ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'REPO_OWNER/REPO_NAME')
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: INSTALLER_CERT_MAC_P12 # Name of the secret that contains the certificate.
password-secret: INSTALLER_CERT_MAC_PASSWORD # Name of the secret that contains the certificate password.

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 pkcs12 \
-in "${{ env.CERTIFICATE_PATH }}" \
-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: |
EXPIRATION_DATE="$(
(
openssl pkcs12 \
-in "${{ env.CERTIFICATE_PATH }}" \
-clcerts \
-nodes \
-passin env:CERTIFICATE_PASSWORD
) | (
openssl x509 \
-noout \
-enddate
) | (
grep \
--max-count=1 \
--only-matching \
--perl-regexp \
'notAfter=(\K.*)'
)
)"

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 "::set-output name=days::$DAYS_BEFORE_EXPIRATION"

- 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