article

Stay Secure with GitHub Actions

Actions are awesome, they allow you to easily extend your GitHub Actions workflow with new functionality, without the need to reinvent the wheel.

The flexibility and offering of the marketplace with official, verified and 3rd party actions is great, but sadly the wrong security of the marketplace has a high risk associated with it.

There's a huge amount of actions you can use to build the project, but only a small amount is from GitHub officially, or from verified sources. 3rd party actions should be reviewed so they are secure.

GitHub Actions Introduction

Risk

But what is the risk of using potentially compromised or insecure actions?

  • Source code being stolen / leaked
  • Result artifacts being exposed
  • Keys or credentials being stolen / exposed
  • Code getting injected into the runner / resulting app
  • Secrets being available to non trusted entities

Stay secure

GitHub offers different options to allow repo administrators to enforce strict rules on which actions can be used within a given repository.

Beside forcing only specific actions being allowed for builds, it's also possible to prevent workflows to be executed for PRs from forks and even more specifically to prevent fork pull request to retrieve write tokens or secrets.

GitHub Actions Permission Settings
GitHub Actions Permission Settings

Generally official and verified actions can be considered to be ok (please be aware that it is never wrong to also review the code of these plugins, if a high risk system is involved).

Protect Secrets

The easiest step to ensure secrets are only available to trusted parts of your build, is by having them specified for the smallest scope possible.
Prefer specifying secrets only to the steps they are needed for, instead of globally. The general rule of thumb for scoping secrets is:

step > job > workflow 

Fork PRs

One risk with pull requests from forks is, that 3rd party entities can fully modify the workflows and introduce risky actions or extract secrets from the repository. As such, if you can't trust all entities having access to the repository and to forks, do prevent these PRs from running workflows.

3rd party actions

Beside untrusted entities potentially getting access and extracting secrets from a repository, the biggest risk are 3rd party Actions which may contain insecure or malicious code.

The most secure would be to simply prevent any 3rd party action from being used and executed, which sadly impacts the flexibility and power of GitHub Actions. Resulting in significant overhead in having all actions in house.

If regardless of the potential risk involved with 3rd party action, you want to use them, make sure to follow these steps of reviewing the source:

  • Review the code inside the Actions repository
  • Verify the compiled code executed by the action
  • Verify the action.yml executing the expected code
  • (if docker action) Verify the Dockerfile and the executed steps
  • Use specific commits instead of versions

Example

To showcase the above steps let's have a look at the styfle/cancel-workflow-action action. (This action is one of my personal favourites as it will ensure to cancel jobs if they get to be obsolete by new pushed commits on a PR)

Review the code

The first step is to review the code. Luckily actions are required to be on GitHub as such we can directly review it's code:

https://github.com/styfle/cancel-workflow-action/blob/main/src/index.ts

Looking into it, no malicious sections can be found. The plugin uses the github core dependency to retrieve the workflows and cancel a running workflow if applicable.

Verify compiled code

Usually actions will also have their compiled source available in the dist folder, this will be the piece which is ultimately executed by GitHub actions.

https://github.com/styfle/cancel-workflow-action/blob/main/dist/index.js

It's more difficult to check this, generally it helps to compile the action manually and then diff the resulting files to identify any suspicious sections.

Verify action.yml

The entry point for GitHub action to run the specified action is the action.yml so it's important that we verify, it actually executing the source as highlighted above (and not do any other malicious steps)

https://github.com/styfle/cancel-workflow-action/blob/main/action.yml#L16

As we can see, it will actually just execute the dist/index.jsusing node

runs:
  using: 'node12'
  main: 'dist/index.js'

Verify Dockerfile

The used example is a normal action, and does not consist out of a docker container. Just to showcase, here's a small example of an action using a docker container:

https://github.com/EnricoMi/publish-unit-test-result-action/blob/master/action.yml#L25-L27

FROM python:3.6-slim
...
COPY requirements.txt /action/
RUN pip install --upgrade --force --no-cache-dir pip && pip install --upgrade --force --no-cache-dir -r /action/requirements.txt
COPY githubext /action/githubext
COPY publish_unit_test_results.py /action/
ENTRYPOINT ["python", "/action/publish_unit_test_results.py"]

https://github.com/EnricoMi/publish-unit-test-result-action/blob/master/Dockerfile

As this Dockerfile shows, it will use the python container, and then copy various files into the container, and use python to execute the python file including the logic for that action

Use specific commit

After verifying that the action contains no malicious code and is considered save to be used, the last suggestion is to not use labeled versions to include the action in the workflow.

# dangerous
styfle/[email protected]

0.5.0 references to the tag with the same name which is associated with a specific commit (we just verified to be secure).

The problem with this is, that tags can be changed, and associated with a different commit. This would give malicious parties the potential to modify the plugins source to execute malicious code, and then change the tag to point to this code, at any time. This is a high risk as even if we reviewed the action, it could at any point suddenly do something else.

To prevent this find the associated commit hash from the tag and specify the action using the hash in your workfow.

styfle/cancel-workflow-action@148d9a848c6acaf90a3ec30bc5062f646f8a4163

Now your builds will use this specific state of the action, and it's not possible anymore to modify that state, as every commit will result in a different hash.

Conclusion

GitHub actions can be dangerous if used without care, but with a few small steps you can protect your code and your project.

  • Prevent external entities to get access to secrets
  • Disallow 3rd party actions if feasible
  • Limit the scope of secrets as close to where they are needed as possible

Feedback

Got thoughts, feedback, improvements, suggestions, or comments?

Let me know @mike_penz

Attribution

Link preview photo by Jonas Kaiser / Unsplash