Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: consolidate some advice, add section on fixing workflows

...

Each build will have access to a GITHUB_TOKEN to perform GitHub API calls. The permissions associated with this token depend on the trigger that started the build. You can find an overview here. Tokens for workflows triggered by pull_request are safe, but others are not. For those:

  • always make sure to configure minimal permissions and branch restrictions.
  • if using the 'checkout' action, always enable persist-credentials: false 

Builds triggered with pull_request_target 

...

  • Always extract into a directory separate from the trusted code in a step before checking out said  trusted code. This stops files extracted from the archive from clobbering trusted code.
  • A separate directory can also prevent files in the artifact impersonating often used python modules like pip  as running python -m pip ...  would execute a pip.py file in the cwd
  • Validate any content you retrieve from an artifact before you use it to avoid command injection, especially in steps using bash  and Github Actions macros.
    • This includes using cat  on such files as well as putting their content into environment variables (a popular exploit is to modify LD_PRELOAD, some examples ), step outputs to use via ${{ steps.id.outputs.sus_content }} to be used in e.g. if  in bash.
  • If you REALLY HAVE TO  run untrusted code (for example as part of your build steps) - you should only do it inside a docker container that you should not pass any credentials to.
  • If you build images from Dockerfile that is untrusted make sure you use .dockerignore which is  not coming from the PR, which  Ignores everything from context by default. Add **  as the first line and only adds (via !directory_path or !file_path ) the files that you need during the build
  • Make sure your checkout actions have persist-credentials: false  set - otherwise your GITHUB_TOKEN will be stored locally in git credentials and available to any steps in your job


 3rd3rd-party actions

The Apache Infrastructure GitHub Actions Policy states actions outside of apache/*, github/* and actions/* must be pinned to the specific git hash (SHA1) of the action that has been reviewed for use by the project. For instance, you MUST pin foobar/baz-action@8843d7f92416211de9ebb963ff4ce28125932878.(TODO follow up): There are two critical security vulnerability reports opened by Unknown User (potiuk) 30 December 2020 with GitHub Actions - both of them triaged and awaiting for actions on the GitHub side. GitHub Security Lab who in December encouraged users topost their experiences is engaged as well.  Those issues can be all mitigated (Apache Airflow implemented all mitigations) but they are not what most projects do. 

Mitigations

If you decide to use GitHub Actions,  those are recommendations (there are varying opinions on sub-modules use, though):

...

Fixing workflows

When a security issue is fixed in a workflow triggered by pull_request_target, you must not only fix it on your main branch, but fix/delete all branches that have the vulnerable workflow, as PRs to those branches would still trigger the vulnerable code.

Further mitigations

  • (TODO: verify this advice is still current. using external actions but pinning them and having them reviewed by Infra might supersede this advice)NEVER use 3rd-party actions directly in your workflows - use the "submodule" pattern. Example PR Tobiasz Kędzierski  opened in SuperSet showing how this could be done. Also ASF INFRA allow-listed some of the popular Actions out there, including my "cancel workflow" action, but I there is no public list of those available. The nice things about submodules is that they do not bring action code to your repo. They link to commit hashes of the Actions, and that integrates well with the GitHub review process so that committers have better chance to review the changes before they are merged. By using submodules, you are automatically following the GitHub recommendations for hardening of security for 3rd-party actions.(TODO: verify this advice is still current. checkout actions should have read-only access unless using 'pull_request_target', and for 'pull_request_target' you (also?) must lock down the GITHUB_TOKEN) ALWAYS add "persist-credentials: false" to all your checkout actions. This is not done by default and is a huge security risk because it leaves your repository (and hundreds of thousands of others) open to 3rd-party dependencies to modify your repository (!) if you have any kind of "master" builds enabled. This is a "hidden" feature of the checkout action that is not at all obvious, but it leaves write access to your repository widely open to any code that you install during the build process. This is a very dangerous default.
  • NEVER directly run code that might come with "forked" PRs in your workflows. There are certain exotic (but useful) workflows that are dangerous. For example, with "workflow_run" you might need to cancel duplicate workflows. Those workflows by default run with "master" code, but sometimes you might need to check out the incoming PR code for those. The host environment can have access (in various ways) to the "WRITE" GITHUB_TOKEN that has permission to modify your repository WITHOUT RESTRICTION OR NOTIFICATION. NEVER run the code that is checked out from the PR in your host environment. If you need to, run it in Docker Container to provide isolation from the host environment to avoid the "write" access leaking to users who prepare such a PR from their fork.
  • NEVER install and run 3rd-party dependencies in the host of your build workflow code. Again there are ways those dependencies can obtain the "WRITE" GITHUB_TOKEN and change anything in your repository without your knowledge.  There are very common "schedule" and  "push" workflows that are especially prone to such abuse. Those run with "WRITE" access, and again there are ways to obtain the GitHub Token by these Actions and code that runs in your workflow. If you execute any 3rd-party code, run it in Docker containers to keep isolation from your "build" host environment to avoid leaking "write" access to those 3rd parties.
  • If you REALLY HAVE TO  run untrusted code (for example as part of your build steps) - you should only do it inside a docker container that you should not pass any credentials to.
  • If you build images from Dockerfile that is untrusted make sure you use .dockerignore which is  not coming from the PR, which  Ignores everything from context by default. Add **  as the first line and only adds (via !directory_path or !file_path ) the files that you need during the build