-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ghrunner): Docker image for self-hosted runners (#887)
* Initial commit of code to create github runner image * add missing parts of pipeline yml * fix some more names * Update .github/workflows/gh-runner-release.yml --------- Co-authored-by: tjololo <[email protected]>
- Loading branch information
Showing
7 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
name: Test build gh-runner | ||
|
||
on: | ||
pull_request: | ||
branches: | ||
- main | ||
paths: | ||
- .github/workflows/gh-runner-pr.yml | ||
- infrastructure/images/gh-runner/** | ||
|
||
permissions: | ||
contents: read | ||
packages: read | ||
|
||
jobs: | ||
build: | ||
name: Test build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Get version from tags | ||
id: version | ||
run: | | ||
tag=${GITHUB_REF/refs\/tags\//} | ||
echo "version=${tag%-demo}" >> $GITHUB_OUTPUT | ||
- name: Get Git commit timestamps | ||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV | ||
- name: Set up QEMU | ||
uses: docker/setup-qemu-action@v3 | ||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
- name: Login to Docker | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Build | ||
id: docker_build | ||
uses: docker/build-push-action@v6 | ||
with: | ||
context: ./infrastructure/images/gh-runner | ||
push: false | ||
platforms: linux/amd64,linux/arm64 | ||
tags: ${{ github.repository }}/gh-runner:test | ||
env: | ||
SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
name: Release gh-runner | ||
|
||
on: | ||
push: | ||
tags: | ||
- "ghrunner-*" | ||
|
||
permissions: | ||
contents: read | ||
packages: write | ||
|
||
jobs: | ||
release: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Get version from tags | ||
id: version | ||
run: | | ||
tag=${GITHUB_REF/refs\/tags\//} | ||
echo "version=${tag%-demo}" >> $GITHUB_OUTPUT | ||
- name: Get Git commit timestamps | ||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV | ||
- name: Set up QEMU | ||
uses: docker/setup-qemu-action@v3 | ||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
- name: Login to Docker | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Build and push | ||
id: docker_build | ||
uses: docker/build-push-action@v6 | ||
with: | ||
context: ./infrastructure/images/gh-runner | ||
push: true | ||
platforms: linux/amd64,linux/arm64 | ||
tags: ghcr.io/${{ github.repository }}/gh-runner:v${{ steps.version.outputs.version }} | ||
env: | ||
SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
FROM ghcr.io/actions/actions-runner:2.319.1 | ||
# for latest release, see https://github.com/actions/runner/releases | ||
|
||
USER root | ||
|
||
# install curl and jq | ||
RUN apt-get update && apt-get install -y curl jq && \ | ||
apt-get clean && \ | ||
rm -rf /var/lib/apt/lists/* | ||
|
||
COPY scripts/entrypoint.sh ./entrypoint.sh | ||
COPY scripts/app-token.sh ./app-token.sh | ||
COPY scripts/token.sh ./token.sh | ||
RUN chmod +x ./entrypoint.sh && \ | ||
chmod +x ./app-token.sh && \ | ||
chmod +x ./token.sh && \ | ||
mkdir /_work && \ | ||
chown runner:docker \ | ||
./entrypoint.sh \ | ||
./app-token.sh \ | ||
./token.sh \ | ||
/_work | ||
|
||
USER runner | ||
|
||
ENTRYPOINT ["./entrypoint.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Altinn Github Runner Image | ||
Default image used for Altinns self-hosted github runners. | ||
|
||
This image is maintained by the platform team. | ||
|
||
This image is ment to be as small and leightweight as possible so we keep the dependencies at a minum, to reduce the maintenance cost. | ||
|
||
If any team needs a custom image they are free to roll their own or extend this, but they will be responsible for maintaining this image. | ||
|
||
Example Dockerfile for an image that in addition to what is available in the base image installs netcat: | ||
|
||
```Dockerfile | ||
FROM ghcr.io/altinn/altinn-platform-gh-runner-base:1.0.0 ##TODO: Add actual image name when available | ||
|
||
USER root | ||
|
||
RUN apt-get update && apt-get install -y curl jq && \ | ||
apt-get clean && \ | ||
rm -rf /var/lib/apt/lists/* | ||
|
||
USER runner | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#!/bin/bash | ||
# | ||
# Request an ACCESS_TOKEN to be used by a GitHub APP | ||
# Environment variable that need to be set up: | ||
# * APP_ID, the GitHub's app ID | ||
# * APP_PRIVATE_KEY, the content of GitHub app's private key in PEM format. | ||
# * APP_LOGIN, the login name used to install GitHub's app | ||
# | ||
|
||
_GITHUB_HOST=${GITHUB_HOST:-"github.com"} | ||
|
||
set -o pipefail | ||
|
||
# If URL is not github.com then use the enterprise api endpoint | ||
if [[ ${_GITHUB_HOST} = "github.com" ]]; then | ||
URI="https://api.${_GITHUB_HOST}" | ||
else | ||
URI="https://${_GITHUB_HOST}/api/v3" | ||
fi | ||
|
||
API_VERSION=v3 | ||
API_HEADER="Accept: application/vnd.github.${API_VERSION}+json" | ||
CONTENT_LENGTH_HEADER="Content-Length: 0" | ||
APP_INSTALLATIONS_URI="${URI}/app/installations" | ||
|
||
JWT_IAT_DRIFT=60 | ||
JWT_EXP_DELTA=600 | ||
|
||
JWT_JOSE_HEADER='{ | ||
"alg": "RS256", | ||
"typ": "JWT" | ||
}' | ||
|
||
|
||
build_jwt_payload() { | ||
now=$(date +%s) | ||
iat=$((now - JWT_IAT_DRIFT)) | ||
jq -c \ | ||
--arg iat_str "${iat}" \ | ||
--arg exp_delta_str "${JWT_EXP_DELTA}" \ | ||
--arg app_id_str "${APP_ID}" \ | ||
' | ||
($iat_str | tonumber) as $iat | ||
| ($exp_delta_str | tonumber) as $exp_delta | ||
| ($app_id_str | tonumber) as $app_id | ||
| .iat = $iat | ||
| .exp = ($iat + $exp_delta) | ||
| .iss = $app_id | ||
' <<< "{}" | tr -d '\n' | ||
} | ||
|
||
base64url() { | ||
base64 | tr '+/' '-_' | tr -d '=\n' | ||
} | ||
|
||
rs256_sign() { | ||
openssl dgst -binary -sha256 -sign <(echo "$1") | ||
} | ||
|
||
request_access_token() { | ||
jwt_payload=$(build_jwt_payload) | ||
encoded_jwt_parts=$(base64url <<<"${JWT_JOSE_HEADER}").$(base64url <<<"${jwt_payload}") | ||
encoded_mac=$(echo -n "${encoded_jwt_parts}" | rs256_sign "${APP_PRIVATE_KEY}" | base64url) | ||
generated_jwt="${encoded_jwt_parts}.${encoded_mac}" | ||
|
||
auth_header="Authorization: Bearer ${generated_jwt}" | ||
|
||
app_installations_response=$(curl -sX GET \ | ||
-H "${auth_header}" \ | ||
-H "${API_HEADER}" \ | ||
"${APP_INSTALLATIONS_URI}" \ | ||
) | ||
access_token_url=$(echo "${app_installations_response}" | jq --raw-output '.[] | select (.account.login == "'"${APP_LOGIN}"'" and .app_id == '"${APP_ID}"') .access_tokens_url') | ||
curl -sX POST \ | ||
-H "${CONTENT_LENGTH_HEADER}" \ | ||
-H "${auth_header}" \ | ||
-H "${API_HEADER}" \ | ||
"${access_token_url}" | \ | ||
jq --raw-output .token | ||
} | ||
|
||
request_access_token |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#!/bin/bash | ||
# shellcheck shell=bash | ||
export PATH=${PATH}:/actions-runner | ||
|
||
##### ENVVARS | ||
# APP_ID ID of app used for registering and setting up runner | ||
# APP_PRIVATE_KEY Private key for app (should be kept in a vault) | ||
# ORG_NAME Name of the org the runner should be added to (For repo runners please supply REPO_NAME as well) | ||
# REPO_NAME (optional) Name of the repository to add this runner to. Leave unset for org runners | ||
# RUNNER_NAME_PREFIX (optional) The name will have random string add after the prefix. Default: github-runner | ||
# RUNNER_WORKDIR (optional) Work dir for the runner. Default: /_work/${RUNNER_NAME} | ||
# LABELS (optional) Runner labels. Default: default | ||
# RUNNER_GROUP (optional) Name of runner group. Default: Default | ||
# RUNNER_GROUP_ID (optional) Id of runner group. Default: 1 | ||
|
||
# Un-export these, so that they must be passed explicitly to the environment of | ||
# any command that needs them. This may help prevent leaks. | ||
export -n ACCESS_TOKEN | ||
export -n RUNNER_TOKEN | ||
export -n APP_ID | ||
export -n APP_PRIVATE_KEY | ||
|
||
_RUNNER_NAME=${RUNNER_NAME_PREFIX:-github-runner}-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '') | ||
_RUNNER_WORKDIR=${RUNNER_WORKDIR:-/_work/${_RUNNER_NAME}} | ||
_LABELS=${LABELS:-default} | ||
_RUNNER_GROUP=${RUNNER_GROUP:-Default} | ||
_RUNNER_GROUP_ID=${RUNNER_GROUP_ID:-1} | ||
_GITHUB_HOST=${GITHUB_HOST:-"github.com"} | ||
_BASE_URI="https://${_GITHUB_HOST}" | ||
|
||
## Unset these, this may help prevent leaks | ||
unset_env() { | ||
unset ACCESS_TOKEN | ||
unset RUNNER_TOKEN | ||
unset APP_ID | ||
unset APP_PRIVATE_KEY | ||
} | ||
|
||
[[ -z ${APP_ID} ]] && ( echo "APP_ID is required"; exit 1 ) | ||
[[ -z ${APP_PRIVATE_KEY} ]] && (echo "APP_PRIVATE_KEY is required"; exit 1) | ||
[[ -z ${ORG_NAME} ]] && (echo "ORG_NAME is required, to define a Repo runner define REPO_NAME as well"; exit 1) | ||
|
||
APP_LOGIN="${ORG_NAME}" | ||
|
||
if [[ -z ${REPO_NAME} ]]; then | ||
_REPO_URL="${_BASE_URI}/${ORG_NAME}" | ||
RUNNER_SCOPE="org" | ||
else | ||
_REPO_URL="${_BASE_URI}/${ORG_NAME}/${REPO_NAME}" | ||
RUNNER_SCOPE="repo" | ||
fi | ||
|
||
echo "Obtaining access token for app_id ${APP_ID} and login ${APP_LOGIN}" | ||
|
||
ACCESS_TOKEN=$(APP_ID="${APP_ID}" APP_PRIVATE_KEY="${APP_PRIVATE_KEY//\\n/${nl}}" APP_LOGIN="${APP_LOGIN}" bash ./app-token.sh) | ||
|
||
|
||
# Retrieve a short lived runner registration token using the APP_LOGIN | ||
_TOKEN=$(ACCESS_TOKEN="${ACCESS_TOKEN}" REPO_URL="${_REPO_URL}" bash ./token.sh) | ||
RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token) | ||
|
||
./config.sh \ | ||
--url "${_REPO_URL}" \ | ||
--token $RUNNER_TOKEN \ | ||
--labels "${_LABELS}" \ | ||
--work "${_RUNNER_WORKDIR}" \ | ||
--name "${_RUNNER_NAME}" \ | ||
--runnergroup "${_RUNNER_GROUP}" \ | ||
--unattended \ | ||
--replace \ | ||
--ephemeral | ||
|
||
unset_env | ||
./run.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#!/bin/bash | ||
|
||
_GITHUB_HOST=${GITHUB_HOST:-"github.com"} | ||
# If URL is not github.com then use the enterprise api endpoint | ||
if [[ ${_GITHUB_HOST} = "github.com" ]]; then | ||
URI="https://api.${_GITHUB_HOST}" | ||
else | ||
URI="https://${_GITHUB_HOST}/api/v3" | ||
fi | ||
|
||
API_VERSION=v3 | ||
API_HEADER="Accept: application/vnd.github.${API_VERSION}+json" | ||
AUTH_HEADER="Authorization: token ${ACCESS_TOKEN}" | ||
CONTENT_LENGTH_HEADER="Content-Length: 0" | ||
|
||
case ${RUNNER_SCOPE} in | ||
org*) | ||
_FULL_URL="${URI}/orgs/${ORG_NAME}/actions/runners/registration-token" | ||
;; | ||
|
||
*) | ||
_FULL_URL="${URI}/repos/${ORG_NAME}/${REPO_NAME}/actions/runners/registration-token" | ||
;; | ||
esac | ||
|
||
RUNNER_TOKEN="$(curl -XPOST -fsSL \ | ||
-H "${CONTENT_LENGTH_HEADER}" \ | ||
-H "${AUTH_HEADER}" \ | ||
-H "${API_HEADER}" \ | ||
"${_FULL_URL}" \ | ||
| jq -r '.token')" | ||
|
||
echo "{\"token\": \"${RUNNER_TOKEN}\", \"full_url\": \"${_FULL_URL}\"}" |