Index ¦ Archives ¦ Atom

Automating security things with GitHub Actions

"Give a small boy a hammer and he will find that everything he encounters needs a pounding."

It used to be that when I needed to run small scheduled security automation tasks, I'd set them up in the crontab on a Linux box and pipe results into sendmail. At first the box was an actual server, then it became a VPS (still love me some ARP Networks) or a cloud compute instance.

Then serverless become all the rage and I moved on to using Python scripts in AWS Lambda, triggered by a CloudWatch Events rule with output piped into SNS.

More recently, I've been doing some of this with GitHub Actions - turns out it is not just for CI/CD, and is useful as a basic job-running solution. No server to manage, no cloud infrastructure to Terraform up... just good ol' YAML-ized bash wrapped with sprinkings of workflow syntax.

The first example is just an automated daily Nuclei run against a list (using the matrix strategy) of base URLs.

name: Nuclei Scan
on:
  schedule:
  - cron: "0 8 * * *" # Runs at 08:00 UTC every day
  push:
    branches: [ nuclei-scan ]
  workflow_dispatch:
jobs:
  nuclei-scan:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        url:
          - https://google.com
          - https://microsoft.com
    steps:
    - name: Scan ${{ matrix.url }} with Nuclei
      uses: projectdiscovery/nuclei-action@508c1868d4884e9fb6eb4d8d680cbc6b493c7ec5
      with:
        target: ${{ matrix.url }}

This uses the public nuclei-action which makes it pretty straightforward.

Output is accessible through the GitHub Actions tab on the repository is lives in:

GitHub Actions UI Screenshot 1

(There's also the option of writing output to files, then adding them to the GitHub Actions workflow run as artifacts. This is documented at https://docs.github.com/en/actions/advanced-guides/storing-workflow-data-as-artifacts).

The second example sets a list of URLs & strings to check HTTP responses for. If the match isn't found, it'll error & also ping a Slack webhook with a notification. (Slack Workflow Builder with custom messages / variables and an "Acknowledge" button was useful here.)

name: HTTP Response Scan
on:
  schedule:
  - cron: "0 8 * * *" # Runs at 08:00 UTC every day
  push:
    branches: [ http-response-scan ]
  workflow_dispatch:
jobs:
  check-http-response-match:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          # `target` is the URL to check
          # `match` is the string to check the HTTP response for
          # `check` describes what is being checked & is used in failure notifications
          - target: "https://abridge.io"
            match: "<meta http-equiv=\\\"Content-Security-Policy\\\" content="
            check: "Check that content security policy is included on Abridge page body"
          - target: "https://abridge.io"
            match: "Strict-Transport-Security: max-age=31536000; includeSubDomains"
            check: "Check that Strict-Transport-Security header is included in Abridge headers"
    steps:
    - name: ${{ matrix.check }}
      run : |
        curl --include --silent ${{ matrix.target }} | grep --ignore-case "${{ matrix.match }}"
    - name: If failed, get raw response to help investigate
      if: ${{ failure() }}
      run: |
        curl --include --silent ${{ matrix.target }}
    - name: If failed, ping Slack
      if: ${{ failure() }}
      uses: slackapi/slack-github-action@v1.15.0
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFIER_WEBHOOK }}
      with:
        payload: "{\"source\":\"check-http-response-match\",\"url\":\"https://github.com/chair6/security-actions/actions/workflows/check-http-response-match.yml\",\"notification\":\":bangbang: ${{ matrix.check }} failed.\"}"

These are just two simple examples. Adding more steps, or more useful bash, is easy.

A few benefits of using Actions:

  • No infrastructure to manage, just a YAML file. (I mean, it's still YAML, but at least it's convenient...)

  • Change control is GitHub repository controls (PRs and branch protection FTW) and audit log is just the git commit log.

  • Jobs can be setup to run on schedule, or workflow_dispatch will let you run it on demand.

  • GitHub provides a reasonable UX for viewing job status.

One of the main frustrations? Figuring out how to correctly escape the quoted strings inside your bash that is embedded inside your YAML.

The example workflows embedded above are also available at https://github.com/chair6/security-actions/tree/main/.github/workflows.

Interested in reading more? I'd recommend taking a look at GitHub Actions for security and compliance now. It was published just a few days ago and has got a few useful ideas about other security workflows you could consider as well. Check out the recent GitHub Actions Security Best Practices from Reethi at Salesforce while you're at it.

© Jamie Finnigan; opinions my own and not my employers. Built using Pelican. Modified from theme by Giulio Fidente on github.