diff --git a/.github/workflows/github-self-hosted-runner-testing.yaml b/.github/workflows/github-self-hosted-runner-testing.yaml new file mode 100644 index 000000000..705d4386b --- /dev/null +++ b/.github/workflows/github-self-hosted-runner-testing.yaml @@ -0,0 +1,38 @@ +# The name of the pipeline. Must be unique. +name: "Terraform - AWS" + +on: + push: + # only run when files in this path changes + # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#example-using-positive-and-negative-patterns-1 + paths: + - 'terraform-modules/aws/helm/github_runner/**' + branches: + - main + pull_request: + # only run when files in this path changes + # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#example-using-positive-and-negative-patterns-1 + paths: + - 'terraform-modules/aws/helm/github_runner/**' + +jobs: + ## This generates a matrix of changed directory to run Terraform on + self_hosted_runner: + #runs-on: ubuntu-latest + # Label from the CRD deployment: https://github.com/actions-runner-controller/actions-runner-controller#runner-labels + runs-on: custom-runner + env: + # The path that you want to construct the matrix on. Only files in this + # path that has changed will be included in. + TERRAFORM_CHECK_PATH: terraform-environments/aws/dev + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Runing on self hosted runner + run: | + echo hi + ps aux + curl -v https://651D35E97B291CC344F098C47028D880.gr7.us-west-2.eks.amazonaws.com -k diff --git a/terraform-modules/aws/helm/github_runner/README.md b/terraform-modules/aws/helm/github_runner/README.md new file mode 100644 index 000000000..32736fe87 --- /dev/null +++ b/terraform-modules/aws/helm/github_runner/README.md @@ -0,0 +1,12 @@ +# Github Runner + +Source: https://github.com/actions-runner-controller/actions-runner-controller + + +Requirements: +* cert-manager +* kubernetes-external-secrets (optional) + +# Runner type support + +Currently this module only supports the PAT runner type. diff --git a/terraform-modules/aws/helm/github_runner/helm_values.tpl.yaml b/terraform-modules/aws/helm/github_runner/helm_values.tpl.yaml new file mode 100644 index 000000000..d43c1eb19 --- /dev/null +++ b/terraform-modules/aws/helm/github_runner/helm_values.tpl.yaml @@ -0,0 +1,6 @@ +--- +# Using our own self signed cert +# https://github.com/actions-runner-controller/actions-runner-controller#using-without-cert-manager +certManagerEnabled: false +admissionWebHooks: + caBundle: ${ca_public_key} diff --git a/terraform-modules/aws/helm/github_runner/main.tf b/terraform-modules/aws/helm/github_runner/main.tf new file mode 100644 index 000000000..2a0df4c0f --- /dev/null +++ b/terraform-modules/aws/helm/github_runner/main.tf @@ -0,0 +1,164 @@ +locals { + helm_repository = "https://actions-runner-controller.github.io/actions-runner-controller" + official_chart_name = "actions-runner-controller" +} + +# +# create namespace +# +module "namespace" { + source = "github.com/ManagedKube/kubernetes-ops//terraform-modules/aws/kubernetes/namespace?ref=v1.0.50" + + name = var.k8s_namespace +} + +# +# Helm values +# +data "template_file" "helm_values" { + template = file("${path.module}/helm_values.tpl.yaml") + vars = { + ca_public_key = base64encode(module.cert.ca_public_key) + } +} + +module "helm_generic" { + source = "github.com/ManagedKube/kubernetes-ops//terraform-modules/aws/helm/helm_generic?ref=v1.0.27" + + repository = local.helm_repository + official_chart_name = local.official_chart_name + user_chart_name = var.user_chart_name + helm_version = var.helm_chart_version + namespace = var.k8s_namespace + helm_values = data.template_file.helm_values.rendered + helm_values_2 = var.helm_values_2 + + depends_on = [ + module.cert, + kubernetes_secret_v1.this, + module.namespace, + ] +} + +# +# kubernetes-external-secret +# +# Using the Github PAT method +# doc: https://github.com/actions-runner-controller/actions-runner-controller#deploying-using-pat-authentication +# This is the secret referencing the PAT token in AWS Secret +resource "kubernetes_manifest" "kube_secret_crd" { + count = var.enable_kubernetes_external_secret ? 1 : 0 + + manifest = { + apiVersion = "kubernetes-client.io/v1" + kind = "ExternalSecret" + + metadata = { + name = var.kubernetes_external_secret_name + namespace = var.k8s_namespace + } + + spec = { + backendType = "secretsManager" + + data = [ + { + # AWS Secrets name + key = var.aws_secret_name + # The name in the k8s secret + name = var.k8s_secret_key_name + }, + ] + } + } + + depends_on = [ + module.namespace + ] +} + +# +# Generate self signed certs for use with the runner: +# doc: https://github.com/actions-runner-controller/actions-runner-controller#using-without-cert-manager +# Even the cert-manager is configured to generate a self signed cert +# +module "cert" { + source = "github.com/ManagedKube/kubernetes-ops//terraform-modules/generate-cert?ref=github-runner" + + ca_public_key_file_path = "/tmp/ca_public_key_file" + public_key_file_path = "/tmp/public_key_file" + private_key_file_path = "/tmp/private_key_file" + owner = "k8s" + ca_common_name = "k8s" + common_name = "k8s" + ip_addresses = [] + validity_period_hours = 43830 + organization_name = "k8s" + + dns_names = [ + "webhook-service.${var.k8s_namespace}.svc", + "webhook-service.${var.k8s_namespace}.svc.cluster.local", + "${var.k8s_namespace}-webhook.${var.k8s_namespace}.svc", + "${var.k8s_namespace}-webhook.${var.k8s_namespace}.svc", + ] +} + +resource "kubernetes_secret_v1" "this" { + metadata { + name = "${var.k8s_namespace}-serving-cert" + # name = "webhook-server-cert" + namespace = var.k8s_namespace + } + + data = { + "tls.crt" = module.cert.client_cert + "tls.key" = module.cert.client_private_key + } + + type = "kubernetes.io/tls" + + depends_on = [ + module.cert, + module.namespace, + ] +} + +# +# Github Action Runner deployments +# +# The above deploys the control nodes. This deploys the actual Github Action runners +# where the jobs will run in. This creates the "RunnerDeployment" CRD which will +# Create the runner deployments. +# +# Docs: https://github.com/actions-runner-controller/actions-runner-controller#runnerdeployments +# +# To view the runner: github.com -> settings -> Actions -> Runners +# +resource "kubernetes_manifest" "runnerDeployment" { + manifest = { + apiVersion = "actions.summerwind.dev/v1alpha1" + kind = "RunnerDeployment" + + metadata = { + name = var.runner_deployment_name + namespace = var.k8s_namespace + } + + spec = { + replicas = var.runner_number_of_replicas + + template ={ + spec = { + repository = var.runner_repository_name + # env = [] + + # The labels on how to target this runner from the GHA's workflow files + # Doc: https://github.com/actions-runner-controller/actions-runner-controller#runner-labels + labels = [ + var.runner_label + ] + } + } + } + } +} diff --git a/terraform-modules/aws/helm/github_runner/variables.tf b/terraform-modules/aws/helm/github_runner/variables.tf new file mode 100644 index 000000000..c0d5b6872 --- /dev/null +++ b/terraform-modules/aws/helm/github_runner/variables.tf @@ -0,0 +1,67 @@ +variable "user_chart_name" { + default = "actions-runner-controller" + description = "The Helm name to install this chart under" +} + +variable "helm_chart_version" { + default = "0.15.1" + description = "The version of this helm chart to use" +} + +variable "k8s_namespace" { + default = "actions-runner-controller" +} + +variable "helm_values_2" { + type = string + default = "" + description = "Helm values that will overwrite the helm chart defaults and this modules default for further user customization" +} + +variable "enable_kubernetes_external_secret" { + type = bool + description = "To create the kubernetes-external-secret or not. Only if you are using the kubernetes-external-secret controller" + default = false +} + +variable "kubernetes_external_secret_name" { + type = string + description = "The kubernetes-external secret name and the secret name. enable_kubernetes_external_secret must be set to true" + default = "controller-manager" +} + +variable "aws_secret_name" { + type = string + description = "The name of the AWS Secret to pull from. enable_kubernetes_external_secret must be set to true" + default = "github_token" +} + +variable "k8s_secret_key_name" { + type = string + description = "The key name in the k8s secret. enable_kubernetes_external_secret must be set to true" + default = "github_token" +} + +variable "runner_repository_name" { + type = string + description = "Runner config. The repository name to associate this runner to" + default = null +} + +variable "runner_label" { + type = string + description = "Runner config. The label to place onto the runner and the label to use on the runs-on field in the GHA workflow file." + default = "self-hosted" +} + +variable "runner_deployment_name" { + type = string + description = "Runner config. The runner CRD deployment name." + default = "runnerdeploy" +} + +variable "runner_number_of_replicas" { + type = number + description = "Runner config. The number of runner replicas to create" + default = 1 +} diff --git a/terraform-modules/generate-cert/README.md b/terraform-modules/generate-cert/README.md new file mode 100644 index 000000000..f5c5cb866 --- /dev/null +++ b/terraform-modules/generate-cert/README.md @@ -0,0 +1,8 @@ +# Generate Cert + +Source: https://github.com/gruntwork-io/private-tls-cert + +This repository contains a Terraform module that can be used to generate self-signed TLS certificate. To be more accurate, the module generates the following: + +* A Certificate Authority (CA) public key +* The public and private keys of a TLS certificate signed by the CA diff --git a/terraform-modules/generate-cert/main.tf b/terraform-modules/generate-cert/main.tf new file mode 100644 index 000000000..ec6c31c32 --- /dev/null +++ b/terraform-modules/generate-cert/main.tf @@ -0,0 +1,72 @@ +# --------------------------------------------------------------------------------------------------------------------- +# CREATE A CA CERTIFICATE +# --------------------------------------------------------------------------------------------------------------------- + +resource "tls_private_key" "ca" { + algorithm = "${var.private_key_algorithm}" + ecdsa_curve = "${var.private_key_ecdsa_curve}" + rsa_bits = "${var.private_key_rsa_bits}" +} + +resource "tls_self_signed_cert" "ca" { + key_algorithm = "${tls_private_key.ca.algorithm}" + private_key_pem = "${tls_private_key.ca.private_key_pem}" + is_ca_certificate = true + + validity_period_hours = "${var.validity_period_hours}" + allowed_uses = var.ca_allowed_uses + + subject { + common_name = "${var.ca_common_name}" + organization = "${var.organization_name}" + } + + # Store the CA public key in a file. + # provisioner "local-exec" { + # command = "echo '${tls_self_signed_cert.ca.cert_pem}' > '${var.ca_public_key_file_path}' && chmod ${var.permissions} '${var.ca_public_key_file_path}' && chown ${var.owner} '${var.ca_public_key_file_path}'" + # } +} + +# --------------------------------------------------------------------------------------------------------------------- +# CREATE A TLS CERTIFICATE SIGNED USING THE CA CERTIFICATE +# --------------------------------------------------------------------------------------------------------------------- + +resource "tls_private_key" "cert" { + algorithm = "${var.private_key_algorithm}" + ecdsa_curve = "${var.private_key_ecdsa_curve}" + rsa_bits = "${var.private_key_rsa_bits}" + + # Store the certificate's private key in a file. + # provisioner "local-exec" { + # command = "echo '${tls_private_key.cert.private_key_pem}' > '${var.private_key_file_path}' && chmod ${var.permissions} '${var.private_key_file_path}' && chown ${var.owner} '${var.private_key_file_path}'" + # } +} + +resource "tls_cert_request" "cert" { + key_algorithm = "${tls_private_key.cert.algorithm}" + private_key_pem = "${tls_private_key.cert.private_key_pem}" + + dns_names = var.dns_names + ip_addresses = var.ip_addresses + + subject { + common_name = "${var.common_name}" + organization = "${var.organization_name}" + } +} + +resource "tls_locally_signed_cert" "cert" { + cert_request_pem = "${tls_cert_request.cert.cert_request_pem}" + + ca_key_algorithm = "${tls_private_key.ca.algorithm}" + ca_private_key_pem = "${tls_private_key.ca.private_key_pem}" + ca_cert_pem = "${tls_self_signed_cert.ca.cert_pem}" + + validity_period_hours = "${var.validity_period_hours}" + allowed_uses = var.allowed_uses + + # Store the certificate's public key in a file. + # provisioner "local-exec" { + # command = "echo '${tls_locally_signed_cert.cert.cert_pem}' > '${var.public_key_file_path}' && chmod ${var.permissions} '${var.public_key_file_path}' && chown ${var.owner} '${var.public_key_file_path}'" + # } +} diff --git a/terraform-modules/generate-cert/outputs.tf b/terraform-modules/generate-cert/outputs.tf new file mode 100644 index 000000000..e1ab9cb5b --- /dev/null +++ b/terraform-modules/generate-cert/outputs.tf @@ -0,0 +1,11 @@ +output "ca_public_key" { + value = tls_self_signed_cert.ca.cert_pem +} + +output "client_cert" { + value = tls_locally_signed_cert.cert.cert_pem +} + +output "client_private_key" { + value = tls_private_key.cert.private_key_pem +} diff --git a/terraform-modules/generate-cert/variables.tf b/terraform-modules/generate-cert/variables.tf new file mode 100644 index 000000000..7364b0ccd --- /dev/null +++ b/terraform-modules/generate-cert/variables.tf @@ -0,0 +1,92 @@ +# --------------------------------------------------------------------------------------------------------------------- +# REQUIRED PARAMETERS +# You must provide a value for each of these parameters. +# --------------------------------------------------------------------------------------------------------------------- + +variable "ca_public_key_file_path" { + description = "Write the PEM-encoded CA certificate public key to this path (e.g. /etc/tls/ca.crt.pem)." +} + +variable "public_key_file_path" { + description = "Write the PEM-encoded certificate public key to this path (e.g. /etc/tls/my-app.crt.pem)." +} + +variable "private_key_file_path" { + description = "Write the PEM-encoded certificate private key to this path (e.g. /etc/tls/my-app.key.pem)." +} + +variable "owner" { + description = "The OS user who should be given ownership over the certificate files." +} + +variable "organization_name" { + description = "The name of the organization to associate with the certificates (e.g. Acme Co)." +} + +variable "ca_common_name" { + description = "The common name to use in the subject of the CA certificate (e.g. acme.co cert)." +} + +variable "common_name" { + description = "The common name to use in the subject of the certificate (e.g. acme.co cert)." +} + +variable "dns_names" { + description = "List of DNS names for which the certificate will be valid (e.g. foo.example.com)." + type = list(string) +} + +variable "ip_addresses" { + description = "List of IP addresses for which the certificate will be valid (e.g. 127.0.0.1)." + type = list(string) +} + +variable "validity_period_hours" { + description = "The number of hours after initial issuing that the certificate will become invalid." +} + +# --------------------------------------------------------------------------------------------------------------------- +# OPTIONAL PARAMETERS +# These parameters have reasonable defaults. +# --------------------------------------------------------------------------------------------------------------------- + +variable "ca_allowed_uses" { + description = "List of keywords from RFC5280 describing a use that is permitted for the CA certificate. For more info and the list of keywords, see https://www.terraform.io/docs/providers/tls/r/self_signed_cert.html#allowed_uses." + type = list(string) + + default = [ + "cert_signing", + "key_encipherment", + "digital_signature", + ] +} + +variable "allowed_uses" { + description = "List of keywords from RFC5280 describing a use that is permitted for the issued certificate. For more info and the list of keywords, see https://www.terraform.io/docs/providers/tls/r/self_signed_cert.html#allowed_uses." + type = list(string) + + default = [ + "key_encipherment", + "digital_signature", + ] +} + +variable "permissions" { + description = "The Unix file permission to assign to the cert files (e.g. 0600)." + default = "0600" +} + +variable "private_key_algorithm" { + description = "The name of the algorithm to use for private keys. Must be one of: RSA or ECDSA." + default = "RSA" +} + +variable "private_key_ecdsa_curve" { + description = "The name of the elliptic curve to use. Should only be used if var.private_key_algorithm is ECDSA. Must be one of P224, P256, P384 or P521." + default = "P256" +} + +variable "private_key_rsa_bits" { + description = "The size of the generated RSA key in bits. Should only be used if var.private_key_algorithm is RSA." + default = "2048" +}