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