diff --git a/README.md b/README.md
index 7b37d0a..ad831d9 100644
--- a/README.md
+++ b/README.md
@@ -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).
+
## GitHub Enterprise Support
@@ -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)
diff --git a/base/Containerfile b/base/Containerfile
index 0287f6e..e7fb5a3 100644
--- a/base/Containerfile
+++ b/base/Containerfile
@@ -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.
@@ -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
@@ -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
diff --git a/base/README.md b/base/README.md
index 0809e60..ffa5481 100644
--- a/base/README.md
+++ b/base/README.md
@@ -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`
diff --git a/base/entrypoint.sh b/base/entrypoint.sh
index 4eedd79..9fcfa48 100755
--- a/base/entrypoint.sh
+++ b/base/entrypoint.sh
@@ -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}."
@@ -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
diff --git a/base/get_github_app_token.sh b/base/get_github_app_token.sh
new file mode 100755
index 0000000..83dfa4a
--- /dev/null
+++ b/base/get_github_app_token.sh
@@ -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}"
+}
\ No newline at end of file
diff --git a/base/register.sh b/base/register.sh
index 57418ed..a05ba53 100755
--- a/base/register.sh
+++ b/base/register.sh
@@ -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
@@ -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
@@ -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
@@ -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}"
+}
\ No newline at end of file
diff --git a/docs/github-app-authentication.md b/docs/github-app-authentication.md
new file mode 100644
index 0000000..df2e8f3
--- /dev/null
+++ b/docs/github-app-authentication.md
@@ -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:**
+**Repository Permissions**
+
+* Actions (read)
+* Administration (read / write)
+* Metadata (read)
+
+**Required Permissions for Organization Runners:**
+**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.
+
+
+- [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.
+
+
+- [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
+```