Skip to content

Commit c870745

Browse files
coibibkayman-mk
andauthored
feat: add new authentication method for GitLab >= 16 (#876)
## Description GitLab released a new [authentication architecture for their runners](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens) since the version 16.0.0. This MR handle this new architecture while maintaining backward compatibility. ## Migrations required Highly recommended as the old GitLab Runner registration method will be removed with GitLab 17. Migration steps: 1. create a group access token for your GitLab group (needs the owner role and the api scope). To be refreshed manually once a year. 2. store the token in a SSM parameter and set the variable `runner_gitlab.access_token_secure_parameter_store_name` to this SSM parameter 3. remove `runner_gitlab_registration_config.registration_token`. No longer needed. 4. add `type = `project or group`, `group_id = <GitLab group number>` (for group runners) or `project_id = <GitLab project id>` (for project runners) to the `runner_gitlab_registration_config` section. ## Verification - running the old authentication method on GitLab 16: success - running the new authentication method on GitLab 16: success --------- Signed-off-by: François Bibron <[email protected]> Co-authored-by: François Bibron <[email protected]> Co-authored-by: Matthias Kay <[email protected]>
1 parent 620459a commit c870745

File tree

6 files changed

+112
-18
lines changed

6 files changed

+112
-18
lines changed

.cspell.json

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"filesha",
2828
"formatlist",
2929
"gitter",
30+
"glrt",
3031
"glrunners",
3132
"instancelifecycle",
3233
"kics",

docs/usage.md

+46-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ The base image used to host the GitLab Runner agent is the latest available Amaz
1818
this module a hard coded list of AMIs per region was provided. This list has been replaced by a search filter to find the latest
1919
AMI. Setting the filter to `amzn2-ami-hvm-2.0.20200207.1-x86_64-ebs` will allow you to version lock the target AMI if needed.
2020

21+
> 💥 **If you are using GitLab >= 16.0.0**: `registration_token` will be deprecated!
22+
23+
>GitLab >= 16.0.0 has removed the `registration_token` since they are working on a [new token architecture](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/). This module handle these changes, you need to provide a personal access token with `api` scope for the runner to authenticate itself.
24+
25+
>The workflow is as follows ([migration steps](https://github.com/cattle-ops/terraform-aws-gitlab-runner/pull/876)):
26+
>1. The runner make an API call (with the access token) to create a new runner on GitLab depending on its type (`instance`, `group` or `project`).
27+
>2. GitLab answers with a token prefixed by `glrt-` and we put it in SSM.
28+
>3. The runner will get the config from `/etc/gitlab-runner/config.toml` and will listen for new jobs from your GitLab instance.
29+
2130
## Install the module
2231

2332
Run `terraform init` to initialize Terraform. Next you can run `terraform plan` to inspect the resources that will be created.
@@ -36,7 +45,7 @@ terraform destroy
3645

3746
## Scenarios
3847

39-
### Scenario: Basic usage
48+
### Scenario: Basic usage on GitLab **< 16.0.0**
4049

4150
Below is a basic examples of usages of the module. Regarding the dependencies such as a VPC, have a look at the [default example](https://github.com/cattle-ops/terraform-aws-gitlab-runner/tree/main/examples/runner-default).
4251

@@ -69,6 +78,42 @@ subnet_ids = module.vpc.private_subnets
6978
}
7079
```
7180

81+
### Scenario: Basic usage on GitLab **>= 16.0.0**
82+
83+
Below is a basic examples of usages of the module if your GitLab instance version is >= 16.0.0.
84+
85+
```hcl
86+
module "runner" {
87+
# https://registry.terraform.io/modules/cattle-ops/gitlab-runner/aws/
88+
source = "cattle-ops/gitlab-runner/aws"
89+
90+
aws_region = "eu-west-1"
91+
environment = "spot-runners"
92+
93+
vpc_id = module.vpc.vpc_id
94+
subnet_ids_gitlab_runner = module.vpc.private_subnets
95+
subnet_id_runners = element(module.vpc.private_subnets, 0)
96+
97+
runners_name = "docker-default"
98+
runners_gitlab_url = "https://gitlab.com"
99+
100+
runner_gitlab_access_token_secure_parameter_store_name = "gitlab_access_token_ssm__name"
101+
102+
103+
runner_gitlab_registration_config = {
104+
type = "instance" # or "group" or "project"
105+
# group_id = 1234 # for "group"
106+
# project_id = 5678 # for "project"
107+
tag_list = "docker"
108+
description = "runner default"
109+
locked_to_project = "true"
110+
run_untagged = "false"
111+
maximum_timeout = "3600"
112+
}
113+
114+
}
115+
```
116+
72117
### Scenario: Multi-region deployment
73118

74119
Name clashes due to multi-region deployments for global AWS resources create by this module (IAM, S3) can be avoided by including a

main.tf

+6-1
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,23 @@ locals {
7474
secure_parameter_store_gitlab_runner_registration_token_name = var.runner_gitlab_registration_token_secure_parameter_store_name
7575
secure_parameter_store_runner_token_key = local.secure_parameter_store_runner_token_key
7676
secure_parameter_store_runner_sentry_dsn = local.secure_parameter_store_runner_sentry_dsn
77+
secure_parameter_store_gitlab_token_name = var.runner_gitlab.access_token_secure_parameter_store_name
7778
secure_parameter_store_region = data.aws_region.current.name
78-
gitlab_runner_registration_token = lookup(var.runner_gitlab_registration_config, "registration_token", "__GITLAB_REGISTRATION_TOKEN_FROM_SSM__")
79+
gitlab_runner_registration_token = var.runner_gitlab_registration_config.registration_token
7980
gitlab_runner_description = var.runner_gitlab_registration_config["description"]
8081
gitlab_runner_tag_list = var.runner_gitlab_registration_config["tag_list"]
8182
gitlab_runner_locked_to_project = var.runner_gitlab_registration_config["locked_to_project"]
8283
gitlab_runner_run_untagged = var.runner_gitlab_registration_config["run_untagged"]
8384
gitlab_runner_maximum_timeout = var.runner_gitlab_registration_config["maximum_timeout"]
85+
gitlab_runner_type = var.runner_gitlab_registration_config["type"]
86+
gitlab_runner_group_id = var.runner_gitlab_registration_config["group_id"]
87+
gitlab_runner_project_id = var.runner_gitlab_registration_config["project_id"]
8488
gitlab_runner_access_level = var.runner_gitlab_registration_config.access_level
8589
sentry_dsn = var.runner_manager.sentry_dsn
8690
public_key = var.runner_worker_docker_machine_fleet.enable == true ? tls_private_key.fleet[0].public_key_openssh : ""
8791
use_fleet = var.runner_worker_docker_machine_fleet.enable
8892
private_key = var.runner_worker_docker_machine_fleet.enable == true ? tls_private_key.fleet[0].private_key_pem : ""
93+
use_new_runner_authentication_gitlab_16 = var.runner_gitlab_registration_config.type != ""
8994
})
9095

9196
template_runner_config = templatefile("${path.module}/template/runner-config.tftpl",

outputs.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ output "runner_launch_template_name" {
5555

5656
output "runner_user_data" {
5757
description = "(Deprecated) The user data of the Gitlab Runner Agent's launch template. Set `var.debug.output_runner_user_data_to_file` to true to write `user_data.sh`."
58-
value = local.template_user_data
58+
value = nonsensitive(local.template_user_data)
5959
}
6060

6161
output "runner_config_toml_rendered" {

template/gitlab-runner.tftpl

+42-8
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,49 @@ then
3232
[[ "$valid_token_response" != "200" ]] && valid_token=false
3333
fi
3434

35-
gitlab_runner_registration_token=${gitlab_runner_registration_token}
36-
# fetch registration token from SSM
37-
if [[ "$gitlab_runner_registration_token" == "__GITLAB_REGISTRATION_TOKEN_FROM_SSM__" ]]
38-
then
39-
gitlab_runner_registration_token=$(aws ssm get-parameter --name "${secure_parameter_store_gitlab_runner_registration_token_name}" --with-decryption --region "${secure_parameter_store_region}" | jq -r ".Parameter | .Value")
40-
fi
41-
4235
if [[ "${runners_token}" == "__REPLACED_BY_USER_DATA__" && "$token" == "null" ]] || [[ "$valid_token" == "false" ]]
4336
then
44-
token=$(curl ${curl_cacert} --request POST -L "${runners_gitlab_url}/api/v4/runners" \
37+
if [ "${use_new_runner_authentication_gitlab_16}" == "true" ]
38+
then
39+
runner_type_param=""
40+
if [ "${gitlab_runner_type}" = "group" ]; then
41+
if [ -z "${gitlab_runner_group_id}" ]; then
42+
echo "ERROR: If the runner type is group, you must specify a group_id".
43+
exit 1
44+
fi
45+
runner_type_param='--form group_id=${gitlab_runner_group_id}'
46+
elif [ "${gitlab_runner_type}" = "project" ]; then
47+
if [ -z "${gitlab_runner_project_id}" ]; then
48+
echo "ERROR: If the runner type is project_type, you must specify a project_id".
49+
exit 1
50+
fi
51+
runner_type_param='--form project_id=${gitlab_runner_project_id}'
52+
fi
53+
54+
# fetch gitlab token from SSM
55+
gitlab_token=$(aws ssm get-parameter --name "${secure_parameter_store_gitlab_token_name}" --with-decryption --region "${secure_parameter_store_region}" | jq -r ".Parameter | .Value")
56+
57+
token=$(curl ${curl_cacert} --request POST -L "${runners_gitlab_url}/api/v4/user/runners" \
58+
--header "private-token: $gitlab_token" \
59+
--form "tag_list=${gitlab_runner_tag_list}" \
60+
--form "description=${gitlab_runner_description}" \
61+
--form "locked=${gitlab_runner_locked_to_project}" \
62+
--form "run_untagged=${gitlab_runner_run_untagged}" \
63+
--form "maximum_timeout=${gitlab_runner_maximum_timeout}" \
64+
--form "runner_type=${gitlab_runner_type}_type" \
65+
$runner_type_param \
66+
--form "access_level=${gitlab_runner_access_level}" \
67+
| jq -r '.token')
68+
else
69+
gitlab_runner_registration_token=${gitlab_runner_registration_token}
70+
71+
# fetch registration token from SSM
72+
if [[ "$gitlab_runner_registration_token" == "__GITLAB_REGISTRATION_TOKEN_FROM_SSM__" ]]
73+
then
74+
gitlab_runner_registration_token=$(aws ssm get-parameter --name "${secure_parameter_store_gitlab_runner_registration_token_name}" --with-decryption --region "${secure_parameter_store_region}" | jq -r ".Parameter | .Value")
75+
fi
76+
77+
token=$(curl ${curl_cacert} --request POST -L "${runners_gitlab_url}/api/v4/runners" \
4578
--form "token=$gitlab_runner_registration_token" \
4679
--form "tag_list=${gitlab_runner_tag_list}" \
4780
--form "description=${gitlab_runner_description}" \
@@ -50,6 +83,7 @@ then
5083
--form "maximum_timeout=${gitlab_runner_maximum_timeout}" \
5184
--form "access_level=${gitlab_runner_access_level}" \
5285
| jq -r .token)
86+
fi
5387
aws ssm put-parameter --overwrite --type SecureString --name "${secure_parameter_store_runner_token_key}" --value="$token" --region "${secure_parameter_store_region}"
5488
fi
5589

variables.tf

+16-7
Original file line numberDiff line numberDiff line change
@@ -299,16 +299,23 @@ variable "runner_cloudwatch" {
299299
variable "runner_gitlab_registration_config" {
300300
description = "Configuration used to register the Runner. See the README for an example, or reference the examples in the examples directory of this repo. There is also a good GitLab documentation available at: https://docs.gitlab.com/ee/ci/runners/configure_runners.html"
301301
type = object({
302-
registration_token = optional(string, "")
302+
registration_token = optional(string, "__GITLAB_REGISTRATION_TOKEN_FROM_SSM__")
303303
tag_list = optional(string, "")
304304
description = optional(string, "")
305+
type = optional(string, "") # mandatory if gitlab_runner_version >= 16.0.0
306+
group_id = optional(string, "") # mandatory if type is group
307+
project_id = optional(string, "") # mandatory if type is project
305308
locked_to_project = optional(string, "")
306309
run_untagged = optional(string, "")
307310
maximum_timeout = optional(string, "")
308311
access_level = optional(string, "not_protected") # this is the only mandatory field calling the GitLab get token for executor operation
309312
})
310313

311314
default = {}
315+
validation {
316+
condition = contains(["group", "project", "instance", ""], var.runner_gitlab_registration_config.type)
317+
error_message = "The executor currently supports `group`, `project` or `instance`."
318+
}
312319
}
313320

314321
variable "runner_gitlab" {
@@ -319,14 +326,16 @@ variable "runner_gitlab" {
319326
runner_version = Version of the [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/-/releases).
320327
url = URL of the GitLab instance to connect to.
321328
url_clone = URL of the GitLab instance to clone from. Use only if the agent can’t connect to the GitLab URL.
329+
access_token_secure_parameter_store_name = The name of the SSM parameter to read the GitLab access token from. It must have the `api` scope and be pre created.
322330
EOT
323331
type = object({
324-
ca_certificate = optional(string, "")
325-
certificate = optional(string, "")
326-
registration_token = optional(string, "__REPLACED_BY_USER_DATA__")
327-
runner_version = optional(string, "15.8.2")
328-
url = optional(string, "")
329-
url_clone = optional(string, "")
332+
ca_certificate = optional(string, "")
333+
certificate = optional(string, "")
334+
registration_token = optional(string, "__REPLACED_BY_USER_DATA__")
335+
runner_version = optional(string, "15.8.2")
336+
url = optional(string, "")
337+
url_clone = optional(string, "")
338+
access_token_secure_parameter_store_name = optional(string, "gitlab-runner-access-token")
330339
})
331340
}
332341

0 commit comments

Comments
 (0)