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

feat: added runner-group configuration and github application authentication #174

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
30 changes: 17 additions & 13 deletions modules/gh-runner-mig-vm/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,23 @@ resource "google_secret_manager_secret" "gh-secret" {
}
}
}

resource "google_secret_manager_secret_version" "gh-secret-version" {
provider = google-beta
secret = google_secret_manager_secret.gh-secret.id
secret_data = jsonencode({
"REPO_NAME" = var.repo_name
"REPO_OWNER" = var.repo_owner
"GITHUB_TOKEN" = var.gh_token
"LABELS" = join(",", var.gh_runner_labels)
"REPO_NAME" = var.repo_name
"REPO_OWNER" = var.repo_owner
"RUNNER_VERSION" = var.gh_runner_version
"RUNNER_GROUP" = var.gh_runner_group
"GITHUB_TOKEN" = var.gh_token
"GHA_INSTALLATION_ID" = var.gha_installation_id
"GHA_CLIENT_ID" = var.gha_client_id
"GHA_PRIVATE_KEY" = base64encode(file(var.gha_private_key))
"LABELS" = join(",", var.gh_runner_labels)
})
}


resource "google_secret_manager_secret_iam_member" "gh-secret-member" {
provider = google-beta
project = var.project_id
Expand All @@ -119,7 +124,7 @@ locals {

module "mig_template" {
source = "terraform-google-modules/vm/google//modules/instance_template"
version = "~> 7.0"
version = "~> 11.0"
project_id = var.project_id
machine_type = var.machine_type
network = local.network_name
Expand Down Expand Up @@ -153,13 +158,12 @@ module "mig_template" {
Runner MIG
*****************************************/
module "mig" {
source = "terraform-google-modules/vm/google//modules/mig"
version = "~> 7.0"
project_id = var.project_id
subnetwork_project = var.project_id
hostname = local.instance_name
region = var.region
instance_template = module.mig_template.self_link
source = "terraform-google-modules/vm/google//modules/mig"
version = "~> 11.0"
project_id = var.project_id
hostname = local.instance_name
region = var.region
instance_template = module.mig_template.self_link

/* autoscaler */
autoscaling_enabled = true
Expand Down
64 changes: 63 additions & 1 deletion modules/gh-runner-mig-vm/scripts/shutdown.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,52 @@
# See the License for the specific language governing permissions and
# limitations under the License.

generate_gha_jwt () {
####################
# Generate JWT token
# Doc: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app
####################

client_id="$GHA_CLIENT_ID" # Client ID as first argument

pem=$(echo -n "$GHA_PRIVATE_KEY" | base64 --decode) # file path of the private key as second argument

now=$(date +%s)
iat=$((now - 60)) # Issues 60 seconds in the past
exp=$((now + 600)) # Expires 10 minutes in the future

#b64enc() { tr -d '\n' | tr -d '\r' | base64 | tr '+/' '-_' | tr -d '='; }
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }

header_json='{
"typ":"JWT",
"alg":"RS256"
}'
# Header encode
header=$( echo -n "${header_json}" | b64enc )

payload_json='{
"iat":'"${iat}"',
"exp":'"${exp}"',
"iss":"'"${client_id}"'"
}'
# Payload encode
payload=$( echo -n "${payload_json}" | b64enc )

# Signature
header_payload="${header}"."${payload}"
signature=$(
openssl dgst -sha256 -sign <(echo -n "${pem}") \
<(echo -n "${header_payload}") | b64enc
)

# Create JWT
JWT="${header_payload}"."${signature}"

#printf "%s\n" "$JWT"
}


secretUri=$(curl -sS "http://metadata.google.internal/computeMetadata/v1/instance/attributes/secret-id" -H "Metadata-Flavor: Google")
#secrets URI is of the form projects/$PROJECT_NUMBER/secrets/$SECRET_NAME/versions/$SECRET_VERSION
#split into array based on `/` delimeter
Expand All @@ -37,5 +83,21 @@ else
# Remove action runner from the repo
POST_URL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/actions/runners/remove-token"
fi

#only execute when client_id and private_key are not empty
#use as check to see if we want to use github app for authentication
#because we are overwriting the GITHUB_TOKEN variable
if [[ -n $GHA_CLIENT_ID ]] && [[ -n $GHA_PRIVATE_KEY ]]; then
generate_gha_jwt

#Get access token
#Docs: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app
GITHUB_TOKEN=$(curl --request POST \
--url "https://api.github.com/app/installations/${GHA_INSTALLATION_ID}/access_tokens" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer ${JWT}" \
--header "X-GitHub-Api-Version: 2022-11-28" | jq -r .token)
fi

#remove the runner configuration
RUNNER_ALLOW_RUNASROOT=1 /runner/config.sh remove --unattended --token "$(curl -sS --request POST --url "$POST_URL" --header "authorization: Bearer ${GITHUB_TOKEN}" --header "content-type: application/json" | jq -r .token)"
RUNNER_ALLOW_RUNASROOT=1 /runner/config.sh remove --unattended --token "$(curl -sS --request POST --url "$POST_URL" --header "authorization: Bearer ${GITHUB_TOKEN}" --header "content-type: application/json" | jq -r .token)" --runnergroup "${RUNNER_GROUP}"
65 changes: 63 additions & 2 deletions modules/gh-runner-mig-vm/scripts/startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,51 @@
apt-get update
apt-get -y install jq

generate_gha_jwt () {
####################
# Generate JWT token
# Doc: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app
####################

client_id="$GHA_CLIENT_ID" # Client ID as first argument

pem=$(echo -n "$GHA_PRIVATE_KEY" | base64 --decode) # file path of the private key as second argument

now=$(date +%s)
iat=$((now - 60)) # Issues 60 seconds in the past
exp=$((now + 600)) # Expires 10 minutes in the future

#b64enc() { tr -d '\n' | tr -d '\r' | base64 | tr '+/' '-_' | tr -d '='; }
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }

header_json='{
"typ":"JWT",
"alg":"RS256"
}'
# Header encode
header=$( echo -n "${header_json}" | b64enc )

payload_json='{
"iat":'"${iat}"',
"exp":'"${exp}"',
"iss":"'"${client_id}"'"
}'
# Payload encode
payload=$( echo -n "${payload_json}" | b64enc )

# Signature
header_payload="${header}"."${payload}"
signature=$(
openssl dgst -sha256 -sign <(echo -n "${pem}") \
<(echo -n "${header_payload}") | b64enc
)

# Create JWT
JWT="${header_payload}"."${signature}"

#printf "%s\n" "$JWT"
}

secretUri=$(curl -sS "http://metadata.google.internal/computeMetadata/v1/instance/attributes/secret-id" -H "Metadata-Flavor: Google")
#secrets URI is of the form projects/$PROJECT_NUMBER/secrets/$SECRET_NAME/versions/$SECRET_VERSION
#split into array based on `/` delimeter
Expand All @@ -31,7 +76,23 @@ secrets=$(gcloud secrets versions access "$SECRET_VERSION" --secret="$SECRET_NAM
# we want to use wordsplitting
export $(echo "$secrets" | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")
#github runner version
GH_RUNNER_VERSION="2.283.2"
GH_RUNNER_VERSION=${RUNNER_VERSION:-2.283.3}

#only execute when client_id and private_key are not empty
#use as check to see if we want to use github app for authentication
#because we are overwriting the GITHUB_TOKEN variable
if [[ -n $GHA_CLIENT_ID ]] && [[ -n $GHA_PRIVATE_KEY ]]; then
generate_gha_jwt

#Get access token
#Docs: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app
GITHUB_TOKEN=$(curl --request POST \
--url "https://api.github.com/app/installations/${GHA_INSTALLATION_ID}/access_tokens" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer ${JWT}" \
--header "X-GitHub-Api-Version: 2022-11-28" | jq -r .token)
fi

#get actions binary
curl -o actions.tar.gz --location "https://github.com/actions/runner/releases/download/v${GH_RUNNER_VERSION}/actions-runner-linux-x64-${GH_RUNNER_VERSION}.tar.gz"
mkdir /runner
Expand All @@ -56,7 +117,7 @@ fi
# Register runner
ACTIONS_RUNNER_INPUT_TOKEN="$(curl -sS --request POST --url "$POST_URL" --header "authorization: Bearer ${GITHUB_TOKEN}" --header 'content-type: application/json' | jq -r .token)"
#configure runner
RUNNER_ALLOW_RUNASROOT=1 /runner/config.sh --unattended --replace --work "/runner-tmp" --url "$GH_URL" --token "$ACTIONS_RUNNER_INPUT_TOKEN" --labels "$LABELS"
RUNNER_ALLOW_RUNASROOT=1 /runner/config.sh --unattended --replace --work "/runner-tmp" --url "$GH_URL" --token "$ACTIONS_RUNNER_INPUT_TOKEN" --labels "$LABELS" --runnergroup "${RUNNER_GROUP}"

#install and start runner service
cd /runner || exit
Expand Down
30 changes: 30 additions & 0 deletions modules/gh-runner-mig-vm/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,24 @@ variable "repo_owner" {
description = "Owner of the repo for the Github Action"
}

variable "gh_runner_version" {
type = string
description = "Version of the runner to deploy"
default = "2.283.2"
}

variable "gh_runner_labels" {
type = set(string)
description = "GitHub runner labels to attach to the runners. Docs: https://docs.github.com/en/actions/hosting-your-own-runners/using-labels-with-self-hosted-runners"
default = []
}

variable "gh_runner_group" {
type = string
description = "GitHub runner group to attach to the runners. Docs: https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners#adding-more-self-hosted-runners"
default = ""
}

variable "min_replicas" {
type = number
description = "Minimum number of runner instances"
Expand All @@ -94,6 +106,24 @@ variable "gh_token" {
description = "Github token that is used for generating Self Hosted Runner Token"
}

variable "gha_client_id" {
type = string
description = "Github App ID"
default = ""
}

variable "gha_installation_id" {
type = string
description = "Github Installation ID"
default = ""
}

variable "gha_private_key" {
type = string
description = "Github App private key"
default = "./gha_private-key.pem"
}

variable "service_account" {
description = "Service account email address"
type = string
Expand Down
Loading