Skip to content

feat: support self-signed certificates #584

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

Merged
merged 9 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
"runner-multi-region",
"runner-pre-registered",
"runner-public",
"runner-certificates"
]
defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@

### ⚠ BREAKING CHANGES

* The module is upgraded to Terraform AWS provider 4.x. All new development will only support the new AWS Terraform provider. We keep a branch `terraform-aws-provider-3` to witch we welcome backports to AWS Terraform 3.x provider. Besides reviewing PR's we will do not any active checking on maintance on this branch. We strongly advise to update your deployment to the new provider version. For more details about upgrading see the [upgrade guide](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade).
* The module is upgraded to Terraform AWS provider 4.x. All new development will only support the new AWS Terraform provider. We keep a branch `terraform-aws-provider-3` to witch we welcome backports to AWS Terraform 3.x provider. Besides reviewing PR's we will do not any active checking on maintenance on this branch. We strongly advise to update your deployment to the new provider version. For more details about upgrading see the [upgrade guide](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade).
* By default, AWS metadata service ((IMDSv2)[https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html]) is enabled and required for both the agent instance and the docker machine instance. For docker machine this require the GitLab managed docker machines distribution is used. Which the module usages by default.


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ module "runner" {
runners_gitlab_url = "https://gitlab.com"

gitlab_runner_registration_config = {
registration_token = "my-token
registration_token = "my-token"
tag_list = "docker"
description = "runner default"
locked_to_project = "true"
Expand Down
120 changes: 120 additions & 0 deletions examples/runner-certificates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Example - Spot Runner - Private subnet

In this scenario the runner agent is running on a single EC2 node.

The example is intended to show how the runner can be configured for self-hosted Gitlab environments with certificates signed by a custom CA.

> This currently only works with the `docker` executor. Support for the `docker+machine` executor is not yet implemented. Contributions are welcome.

## Prerequisites

The terraform version is managed using [tfenv](https://github.com/Zordrak/tfenv). If you are not using `tfenv` please check `.terraform-version` for the tested version.

Before configuring certificates, it is important to review the [Gitlab documentation](https://docs.gitlab.com/runner/configuration/tls-self-signed.html).

In particular, note the following docker images are involved:

> - The **Runner helper image**, which is used to handle Git, artifacts, and cache operations. In this scenario, the user only needs to make a certificate file available at a specific location (for example, /etc/gitlab-runner/certs/ca.crt), and the Docker container will automatically install it for the user.

> - The **user image**, which is used to run the user script. In this scenario, the user must take ownership regarding how to install a certificate, since this is highly dependent on the image itself, and the Runner has no way of knowing how to install a certificate in each possible scenario.

### Certificates for the runner-helper image

The Gitlab **runner-helper image** needs to communicate with your Gitlab instance.

Create a PEM-encoded `.crt` file containing the public certificate of your Gitlab server instance.

```hcl
module {
...
# Public cert of my companys gitlab instance
runners_gitlab_certificate = file("${path.module}/my_gitlab_instance_cert.crt")
...
}
```

Add your CA and intermediary certs to a second PEM-encoded `.crt` file.
```hcl
module {
...
# Other public certs relating to my company.
runners_ca_certificate = file("${path.module}/my_company_ca_cert_bundle.crt")
...
}
```

### Certificates for user images

For **user images**, you must:

1. Mount the certificates from the EC2 host into all user images.

The runner module can be configured to do this step. Configure the module like so:

```terraform
module {
# ...

# Mount EC2 host certs in docker so all user docker images can reference them.
runners_additional_volumes = ["/etc/gitlab-runner/certs/:/etc/gitlab-runner/certs:ro"]

# ...
}
```

2. Trust the certificates from within the user image.

Each user image will need to execute commands to copy the certificates into the correct place and trust them.

The below examples some ways to do this, assuming user images with the Ubuntu OS or similar.
For Alpine OS user images, the specific commands may differ.

**Option 1:** Build a custom user image and update your `Dockerfile`:
```docker
FROM python:3 # Some base image

RUN apt-get -y update
RUN apt-get -y upgrade

RUN apt-get install -y ca-certificates
RUN cp /etc/gitlab-runner/certs/* /usr/local/share/ca-certificates/
RUN update-ca-certificates
...
```

**Option 2:** Add a section to each pipeline using `before_script`:

This change would need to be added to every pipeline file which requires certificates.
It could be customised depending on the OS of the pipeline user image.

```yaml
default:
before_script:
# Install certificates into user image
- apt-get install -y ca-certificates
- cp /etc/gitlab-runner/certs/* /usr/local/share/ca-certificates/
- update-ca-certificates
```

**Option 3:** Add the script from Option 2 into `runners_pre_build_script` variable:

This avoids maintaining the script in each pipeline file, but expects that all user images use the same OS.

```terraform
module {
# ...

runners_pre_build_script = <<EOT
'''
apt-get install -y ca-certificates
cp /etc/gitlab-runner/certs/* /usr/local/share/ca-certificates/
update-ca-certificates
'''
EOT

# ...
}
```

<!-- BEGIN_TF_DOCS -->
<!-- END_TF_DOCS -->
73 changes: 73 additions & 0 deletions examples/runner-certificates/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
data "aws_availability_zones" "available" {
state = "available"
}

module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.70"

name = "vpc-${var.environment}"
cidr = "10.1.0.0/16"

azs = [data.aws_availability_zones.available.names[0]]
public_subnets = ["10.1.101.0/24"]
enable_s3_endpoint = true
map_public_ip_on_launch = false

tags = {
Environment = var.environment
}
}

module "runner" {
source = "../../"

###############################################
# General
###############################################

runners_name = var.runner_name
runners_gitlab_url = var.gitlab_url

runners_executor = "docker"

aws_region = var.aws_region
environment = var.environment

###############################################
# Certificates
###############################################

# Public cert of my companys gitlab instance
runners_gitlab_certificate = file("${path.module}/my_gitlab_instance_cert.crt")

# Other public certs relating to my company.
runners_ca_certificate = file("${path.module}/my_company_ca_cert_bundle.crt")

# Mount EC2 host certs in docker so all user docker images can reference them.
# Each user image will need to do:
# cp /etc/gitlab-runner/certs/* /usr/local/share/ca-certificates/
# update-ca-certificates
# Or similar OS-dependent commands. The above are an example for Ubuntu.
runners_additional_volumes = ["/etc/gitlab-runner/certs/:/etc/gitlab-runner/certs:ro"]

###############################################
# Registration
###############################################

gitlab_runner_registration_config = {
registration_token = var.registration_token
tag_list = "docker_runner"
description = "runner docker - auto"
locked_to_project = "true"
run_untagged = "false"
maximum_timeout = "3600"
}

###############################################
# Network
###############################################
vpc_id = module.vpc.vpc_id
subnet_id = element(module.vpc.public_subnets, 0)

}
15 changes: 15 additions & 0 deletions examples/runner-certificates/my_company_ca_cert_bundle.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
Put your various CA / intermediary public certificates and other general public certs in this file.
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
My certificate authority public cert 1
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
My intermediary public cert 1
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
Some other instance I need to get to public cert, eg my own docker registry server
-----END CERTIFICATE-----
4 changes: 4 additions & 0 deletions examples/runner-certificates/my_gitlab_instance_cert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN CERTIFICATE-----
Put your Gitlab instance public certificate in this file.
Intermediary and CA public certificates are not required.
-----END CERTIFICATE-----
11 changes: 11 additions & 0 deletions examples/runner-certificates/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
provider "aws" {
region = var.aws_region
}

provider "local" {}

provider "null" {}

provider "tls" {}

provider "random" {}
29 changes: 29 additions & 0 deletions examples/runner-certificates/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
variable "aws_region" {
description = "AWS region."
type = string
default = "eu-west-1"
}

variable "environment" {
description = "A name that identifies the environment, will used as prefix and for tagging."
default = "runners-docker"
type = string
}

variable "runner_name" {
description = "Name of the runner, will be used in the runner config.toml"
type = string
default = "docker"
}

variable "gitlab_url" {
description = "URL of the gitlab instance to connect to."
type = string
default = "https://gitlab.com"
}

variable "registration_token" {
description = "Gitlab runner registration token"
type = string
default = "something"
}
26 changes: 26 additions & 0 deletions examples/runner-certificates/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

terraform {
required_version = ">= 1"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.7"
}
local = {
source = "hashicorp/local"
version = "~> 2"
}
null = {
source = "hashicorp/null"
version = "~> 3.0"
}
tls = {
source = "hashicorp/tls"
version = "~> 3"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}
38 changes: 38 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
locals {
# Manage certificates
pre_install_gitlab_certificate = (
length(var.runners_gitlab_certificate) > 0
? <<-EOT
mkdir -p /etc/gitlab-runner/certs/
cat <<- EOF > /etc/gitlab-runner/certs/gitlab.crt
${var.runners_gitlab_certificate}
EOF
EOT
: ""
)
pre_install_ca_certificate = (
length(var.runners_ca_certificate) > 0
? <<-EOT
mkdir -p /etc/gitlab-runner/certs/
cat <<- EOF > /etc/gitlab-runner/certs/ca.crt
${var.runners_ca_certificate}
EOF
EOT
: ""
)
pre_install_certificates_end = <<-EOT
chmod 600 /etc/gitlab-runner/certs/*.crt
chmod -R a+r /etc/gitlab-runner
cp /etc/gitlab-runner/certs/*.crt /etc/pki/ca-trust/source/anchors
update-ca-trust extract
EOT
pre_install_certificates = (
# If either (or both) _certificate variables are specified
length(var.runners_gitlab_certificate) + length(var.runners_ca_certificate) > 0
? join("\n", [
local.pre_install_gitlab_certificate,
local.pre_install_ca_certificate,
local.pre_install_certificates_end
])
: ""
)

# Determine IAM role for runner instance
aws_iam_role_instance_name = coalesce(
var.runner_iam_role_name,
Expand Down
5 changes: 4 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ locals {
runners_userdata = var.runners_userdata
runners_executor = var.runners_executor
runners_install_amazon_ecr_credential_helper = var.runners_install_amazon_ecr_credential_helper
curl_cacert = length(var.runners_gitlab_certificate) > 0 ? "--cacert /etc/gitlab-runner/certs/gitlab.crt" : ""
pre_install_certificates = local.pre_install_certificates
pre_install = var.userdata_pre_install
post_install = var.userdata_post_install
runners_gitlab_url = var.runners_gitlab_url
Expand All @@ -68,7 +70,7 @@ locals {
secure_parameter_store_runner_sentry_dsn = local.secure_parameter_store_runner_sentry_dsn
secure_parameter_store_region = var.aws_region
gitlab_runner_registration_token = var.gitlab_runner_registration_config["registration_token"]
giltab_runner_description = var.gitlab_runner_registration_config["description"]
gitlab_runner_description = var.gitlab_runner_registration_config["description"]
gitlab_runner_tag_list = var.gitlab_runner_registration_config["tag_list"]
gitlab_runner_locked_to_project = var.gitlab_runner_registration_config["locked_to_project"]
gitlab_runner_run_untagged = var.gitlab_runner_registration_config["run_untagged"]
Expand All @@ -82,6 +84,7 @@ locals {
aws_region = var.aws_region
gitlab_url = var.runners_gitlab_url
gitlab_clone_url = var.runners_clone_url
tls_ca_file = length(var.runners_gitlab_certificate) > 0 ? "tls-ca-file=\"/etc/gitlab-runner/certs/gitlab.crt\"" : ""
runners_extra_hosts = var.runners_extra_hosts
runners_vpc_id = var.vpc_id
runners_subnet_id = length(var.subnet_id) > 0 ? var.subnet_id : var.subnet_id_runners
Expand Down
2 changes: 1 addition & 1 deletion outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ output "runner_launch_template_name" {
output "runner_user_data" {
description = "The user data of the Gitlab Runner Agent's launch template."
value = local.template_user_data
}
}
Loading