Skip to content
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

Add support for Github App Authentication #12

Merged
merged 4 commits into from
Oct 12, 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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ A Runner Token can be used as an alternative to PAT or GitHub App authentication

Refer to [Authenticating with a Runner Token](./docs/runner-token.md).

## Running with Github App Authentication

If you are able to use a GitHub App it is highly recommended over the PAT because you have greater control of the API permissions granted to it and you do not need a bot or service account.

Refer to [Authenticating with GitHub App Authentication](./docs/github-app-authentication.md).

<a id="enterprise-support"></a>

## GitHub Enterprise Support
Expand Down Expand Up @@ -92,3 +98,5 @@ If you encounter any other issues, please [open an issue](https://github.com/red

## Credits
This repository builds on the work done in [bbrowning/github-runner](https://github.com/bbrowning/github-runner), which is forked from [SanderKnape/github-runner](https://github.com/SanderKnape/github-runner).

The Github App creation tutorial is heavily based on the excellent README in [actions-runner-controller/actions-runner-controller](https://github.com/actions-runner-controller/actions-runner-controller)
7 changes: 5 additions & 2 deletions base/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM fedora:33
# Adapted from https://github.com/bbrowning/github-runner/blob/master/Dockerfile
RUN dnf -y upgrade --security && \
dnf -y --setopt=skip_missing_names_on_install=False install \
curl git jq hostname procps findutils which && \
curl git jq hostname procps findutils which openssl && \
dnf clean all

# The UID env var should be used in child Containerfile.
Expand All @@ -20,6 +20,9 @@ WORKDIR /home/${USERNAME}

# Override these when creating the container.
ENV GITHUB_PAT ""
ENV GITHUB_APP_ID ""
ENV GITHUB_APP_INSTALL_ID ""
ENV GITHUB_APP_PEM ""
ENV GITHUB_OWNER ""
ENV GITHUB_REPOSITORY ""
ENV RUNNER_WORKDIR /home/${USERNAME}/_work
Expand All @@ -41,7 +44,7 @@ RUN chown -R ${USERNAME}:0 /home/${USERNAME}/ && \
chgrp -R 0 /home/${USERNAME}/ && \
chmod -R g=u /home/${USERNAME}/

COPY --chown=${USERNAME}:0 entrypoint.sh uid.sh register.sh ./
COPY --chown=${USERNAME}:0 entrypoint.sh uid.sh register.sh get_github_app_token.sh ./

USER $UID

Expand Down
1 change: 1 addition & 0 deletions base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some basic CLI tools are installed in addition to what's in the parent Fedora im
- `git`
- `hostname`
- `jq`
- `openssl`
- `procps` (`ps`, `pgrep`)
- `which`

Expand Down
5 changes: 4 additions & 1 deletion base/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CREDS_FILE="${PWD}/.credentials"

# Assume registration artifacts have been persisted from a previous start
# if no PAT or TOKEN is provided, and simply attempt to start.
if [ -n "${GITHUB_PAT:-}" ] || [ -n "${RUNNER_TOKEN:-}" ]; then
if [ -n "${GITHUB_PAT:-}" ] || [ -n "${RUNNER_TOKEN:-}" ] || [ -n "${GITHUB_APP_ID:-}" ]; then
source ./register.sh
elif [ -e "${CREDS_FILE}" ]; then
echo "No GITHUB_PAT or RUNNER_TOKEN provided. Using existing credentials file ${CREDS_FILE}."
Expand All @@ -22,6 +22,9 @@ fi
if [ -n "${GITHUB_PAT:-}" ]; then
trap 'remove; exit 130' INT
trap 'remove; exit 143' TERM
elif [ -n "${GITHUB_APP_ID:-}" ]; then
trap 'remove_github_app; exit 130' INT
trap 'remove_github_app; exit 143' TERM
else
trap 'exit 130' INT
trap 'exit 143' TERM
Expand Down
29 changes: 29 additions & 0 deletions base/get_github_app_token.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/sh
# Adapted from https://stackoverflow.com/a/62646786 and
# Github's docs: https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app

get_github_app_token() {
NOW=$( date +%s )
IAT=$((${NOW} - 60))
EXP=$((${NOW} + 540))
HEADER_RAW='{"alg":"RS256"}'
HEADER=$( echo -n "${HEADER_RAW}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' )
PAYLOAD_RAW='{"iat":'"${IAT}"',"exp":'"${EXP}"',"iss":'"${GITHUB_APP_ID}"'}'
PAYLOAD=$( echo -n "${PAYLOAD_RAW}" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' )
HEADER_PAYLOAD="${HEADER}"."${PAYLOAD}"

# Making a tmp directory here because /bin/sh doesn't support process redirection <()
tmp_dir=/tmp/github_app_tmp
mkdir "${tmp_dir}"
echo -n "${GITHUB_APP_PEM}" > "${tmp_dir}/github.pem"
echo -n "${HEADER_PAYLOAD}" > "${tmp_dir}/header"
SIGNATURE=$( openssl dgst -sha256 -sign "${tmp_dir}/github.pem" "${tmp_dir}/header" | openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' )
rm -rf "${tmp_dir}"

JWT="${HEADER_PAYLOAD}"."${SIGNATURE}"
INSTALL_URL="https://${GITHUB_API_SERVER}/app/installations/${GITHUB_APP_INSTALL_ID}/access_tokens"
INSTALL_TOKEN_PAYLOAD=$(curl -sSfLX POST -H "Authorization: Bearer ${JWT}" -H "Accept: application/vnd.github.v3+json" "${INSTALL_URL}")
INSTALL_TOKEN=$(echo ${INSTALL_TOKEN_PAYLOAD} | jq .token --raw-output)

echo "${INSTALL_TOKEN}"
}
25 changes: 22 additions & 3 deletions base/register.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

set -eE

# Load Github app authentication helper function
source ./get_github_app_token.sh

if [ -z "${GITHUB_OWNER:-}" ]; then
echo "Fatal: \$GITHUB_OWNER must be set in the environment"
exit 1
Expand All @@ -27,8 +30,8 @@ fi

registration_url="https://${GITHUB_DOMAIN}/${GITHUB_OWNER}${GITHUB_REPOSITORY:+/$GITHUB_REPOSITORY}"

if [ -z "${GITHUB_PAT:-}" ]; then
echo "GITHUB_PAT not set in the environment. Automatic runner removal will be disabled."
if [ -z "${GITHUB_PAT:-}" ] && [ -z "${GITHUB_APP_ID:-}" ]; then
echo "Neither GITHUB_PAT nor the GITHUB_APP variables are set in the environment. Automatic runner removal will be disabled."
echo "Visit ${registration_url}/settings/actions/runners to manually force removal of runner."
fi

Expand All @@ -46,7 +49,15 @@ if [ -z "${RUNNER_TOKEN:-}" ]; then
fi
echo "Obtaining runner token from ${token_url}"

payload=$(curl -sSfLX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url})
if [ -n "${GITHUB_APP_ID:-}" ] && [ -n "${GITHUB_APP_INSTALL_ID:-}" ] && [ -n "${GITHUB_APP_PEM:-}" ]; then
echo "GITHUB_APP environment variables are set. Using GitHub App authentication."
app_token=$(get_github_app_token)
payload=$(curl -sSfLX POST -H "Authorization: token ${app_token}" ${token_url})
else
echo "Using GITHUB_PAT for authentication."
payload=$(curl -sSfLX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url})
fi

export RUNNER_TOKEN=$(echo $payload | jq .token --raw-output)
echo "Obtained registration token"
else
Expand Down Expand Up @@ -79,3 +90,11 @@ remove() {

./config.sh remove --unattended --token "${REMOVE_TOKEN}"
}

remove_github_app() {
app_token=$(get_github_app_token)
payload=$(curl -sSfLX POST -H "Authorization: token ${app_token}" ${token_url%/registration-token}/remove-token)
export REMOVE_TOKEN=$(echo $payload | jq .token --raw-output)

./config.sh remove --unattended --token "${REMOVE_TOKEN}"
}
72 changes: 72 additions & 0 deletions docs/github-app-authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## Setting up a GitHub App for Runner Registration

You can create a GitHub App for your user account, or any organization.

The following app permissions are required for each supported type of runner:

_Note: Links are provided further down to create an app for your logged in user account or an organization with the permissions for all runner types set in each link's query string_

**Required Permissions for Repository Runners:**<br />
**Repository Permissions**

* Actions (read)
* Administration (read / write)
* Metadata (read)

**Required Permissions for Organization Runners:**<br />
**Repository Permissions**

* Actions (read)
* Metadata (read)

**Organization Permissions**
* Self-hosted runners (read / write)


_Note: All API routes mapped to their permissions can be found [here](https://docs.github.com/en/rest/reference/permissions-required-for-github-apps) if you wish to review_

---

**Setup Steps**

If you want to create a GitHub App for your account, open the following link to the creation page, enter any unique name in the "GitHub App name" field, and hit the "Create GitHub App" button at the bottom of the page.

<!-- markdown-link-check-disable-next-line -->
- [Create GitHub Apps on your account](https://github.com/settings/apps/new?url=https://github.com/redhat-actions/openshift-actions-runners&webhook_active=false&public=false&administration=write&actions=read)

If you want to create a GitHub App for your organization, replace the `:org` part of the following URL with your organization name before opening it. Then enter any unique name in the "GitHub App name" field, and hit the "Create GitHub App" button at the bottom of the page to create a GitHub App.

<!-- markdown-link-check-disable-next-line -->
- [Create GitHub Apps on your organization](https://github.com/organizations/:org/settings/apps/new?url=https://github.com/redhat-actions/openshift-actions-runners&webhook_active=false&public=false&administration=write&organization_self_hosted_runners=write&actions=read)

You will see an *App ID* on the page of the GitHub App you created. You will need the value of this App ID later.

Download the private key file by pushing the "Generate a private key" button at the bottom of the GitHub App page. This file will also be used later.

Go to the "Install App" tab on the left side of the page and install the GitHub App that you created for your account or organization.

When the installation is complete, you will be taken to a URL in one of the following formats. The number at the end of the URL will be used as the Installation ID later.

For example, if the URL ends in `settings/installations/12345`, then the Installation ID is `12345`.

- `https://github.com/settings/installations/${INSTALLATION_ID}`
- `https://github.com/organizations/eventreactor/settings/installations/${INSTALLATION_ID}`

### Running Locally with GitHub App Authentication

You need to set the `GITHUB_APP_ID`, `GITHUB_APP_INSTALL_ID`, and `GITHUB_APP_PEM` env variables and pass them to your container.

The easiest way to get the private key in the correct form is to copy paste it into the environment variable. Newlines must be preserved.

To launch and connect a runner to `redhat-actions/openshift-actions-runner` with the labels `local` and `podman`:

```sh
podman run \
--env GITHUB_APP_ID \
--env GITHUB_APP_INSTALL_ID \
--env GITHUB_APP_PEM \
--env GITHUB_OWNER=redhat-actions \
--env GITHUB_REPOSITORY=openshift-actions-runner \
--env RUNNER_LABELS="local,podman" \
quay.io/redhat-github-actions/runner:latest
```