├── img
├── scallops.png
└── scallops-infra.png
├── scripts
├── Powershell
│ └── disabledefender.ps1
└── bash
│ ├── gitlab_backup.sh
│ ├── gcloud_logger.sh
│ ├── gitlab_startup.sh
│ └── gitlab_helpers.sh
├── gitlab-runner
├── win-values.yaml
├── linux-values.yaml
├── kaniko-values.yaml
└── dockerhub-values.yaml
├── outputs.tf
├── versions.tf
├── .gitignore
├── provider.tf
├── LICENSE.md
├── debug.tf
├── network.tf
├── locals.tf
├── storage.tf
├── CHANGELOG.md
├── config.tfvars
├── secrets.tf
├── load-balancer.tf
├── iam.tf
├── vars.tf
├── gke.tf
├── main.tf
└── README.MD
/img/scallops.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SygniaLabs/ScallOps/HEAD/img/scallops.png
--------------------------------------------------------------------------------
/img/scallops-infra.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SygniaLabs/ScallOps/HEAD/img/scallops-infra.png
--------------------------------------------------------------------------------
/scripts/Powershell/disabledefender.ps1:
--------------------------------------------------------------------------------
1 | Set-MpPreference -DisableRealtimeMonitoring $true -SubmitSamplesConsent NeverSend
2 | Get-MpPreference -Verbose
--------------------------------------------------------------------------------
/gitlab-runner/win-values.yaml:
--------------------------------------------------------------------------------
1 | rbac:
2 | create: true
3 | rules:
4 | - apiGroups: [""]
5 | resources: ["pods/attach","pods/exec"]
6 | verbs: ["create", "patch", "delete"]
7 | - apiGroups: [""]
8 | resources: ["pods","services"]
9 | verbs: ["get","watch","create", "delete"]
10 | - apiGroups: [""]
11 | resources: ["configmaps","secrets"]
12 | verbs: ["get","create","update","delete"]
13 |
14 | runners:
15 | tags: "windows,kubernetes"
--------------------------------------------------------------------------------
/gitlab-runner/linux-values.yaml:
--------------------------------------------------------------------------------
1 | rbac:
2 | create: true
3 | rules:
4 | - apiGroups: [""]
5 | resources: ["pods/attach","pods/exec"]
6 | verbs: ["create", "patch", "delete", "get"]
7 | - apiGroups: [""]
8 | resources: ["pods","services"]
9 | verbs: ["get","watch","create", "delete"]
10 | - apiGroups: [""]
11 | resources: ["configmaps","secrets"]
12 | verbs: ["get","create","update","delete"]
13 |
14 | runners:
15 | tags: "linux,kubernetes"
--------------------------------------------------------------------------------
/gitlab-runner/kaniko-values.yaml:
--------------------------------------------------------------------------------
1 | rbac:
2 | create: true
3 | rules:
4 | - apiGroups: [""]
5 | resources: ["pods/attach","pods/exec"]
6 | verbs: ["create", "patch", "delete"]
7 | - apiGroups: [""]
8 | resources: ["pods","services"]
9 | verbs: ["get","watch","create", "delete"]
10 | - apiGroups: [""]
11 | resources: ["configmaps","secrets"]
12 | verbs: ["get","create","update","delete"]
13 |
14 | runners:
15 | protected: true
16 | tags: "kaniko,kubernetes"
--------------------------------------------------------------------------------
/gitlab-runner/dockerhub-values.yaml:
--------------------------------------------------------------------------------
1 | rbac:
2 | create: true
3 | rules:
4 | - apiGroups: [""]
5 | resources: ["pods/attach","pods/exec"]
6 | verbs: ["create", "patch", "delete"]
7 | - apiGroups: [""]
8 | resources: ["pods","services"]
9 | verbs: ["get","watch","create", "delete"]
10 | - apiGroups: [""]
11 | resources: ["configmaps","secrets"]
12 | verbs: ["get","create","update","delete"]
13 |
14 | runners:
15 | imagePullPolicy: always
16 | tags: "dockerhub,kubernetes"
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | #Deployment outputs
2 |
3 | output "gitlab_ext_ip" {
4 | description = "Gitlab's external IP address."
5 | value = google_compute_instance.gitlab.network_interface.0.access_config.0.nat_ip
6 | }
7 |
8 | output "lb_ext_ip" {
9 | description = "Load balancer external IP address."
10 | value = google_compute_global_forwarding_rule.lb_forward_rule.ip_address
11 | }
12 |
13 | output "gitlab_root_password_secret" {
14 | description = "Secret name where Gitlab (Web UI) root account's password is stored"
15 | value = google_secret_manager_secret.gitlab_initial_root_pwd.secret_id
16 | }
--------------------------------------------------------------------------------
/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">=1.5.0"
3 |
4 | required_providers {
5 | google = {
6 | source = "hashicorp/google"
7 | version = ">=4.77.0"
8 | }
9 | kubernetes = {
10 | source = "hashicorp/kubernetes"
11 | version = ">= 2.10.0"
12 | }
13 | helm = {
14 | source = "hashicorp/helm"
15 | version = ">= 2.5.1"
16 | }
17 | tls = {
18 | source = "hashicorp/tls"
19 | version = ">= 3.3.0"
20 | }
21 | google-beta = {
22 | source = "hashicorp/google-beta"
23 | version = ">= 4.44.0"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # .tfstate files
5 | *.tfstate
6 | *.tfstate.*
7 |
8 | # Crash log files
9 | crash.log
10 |
11 | # Ignore override files as they are usually used to override resources locally and so
12 | # are not checked in
13 | override.tf
14 | override.tf.json
15 | *_override.tf
16 | *_override.tf.json
17 |
18 |
19 | # Exclude Terraform lock file
20 | .terraform.lock.hcl
21 |
22 | #Exclude state and config files
23 |
24 | backend.tf
25 | current*.tfvars
26 |
27 | # Ignore downloaded backups
28 | gitlab_backups/**
29 |
30 | # Certificates
31 | *.crt
32 | *.key
33 |
34 | # k8s
35 | kubeconfig
--------------------------------------------------------------------------------
/provider.tf:
--------------------------------------------------------------------------------
1 | provider "google" {
2 | alias = "offensive-pipeline"
3 | project = var.project_id
4 | }
5 |
6 | provider "google" {
7 | alias = "dns_infra"
8 | project = var.dns_project_id
9 | }
10 |
11 | data "google_client_config" "default" {}
12 |
13 | provider "kubernetes" {
14 | host = "https://${module.gke.endpoint}"
15 | token = data.google_client_config.default.access_token
16 | cluster_ca_certificate = base64decode(module.gke.ca_certificate)
17 | }
18 |
19 | provider "helm" {
20 | kubernetes {
21 | host = "https://${module.gke.endpoint}"
22 | token = data.google_client_config.default.access_token
23 | cluster_ca_certificate = base64decode(module.gke.ca_certificate)
24 | }
25 | }
--------------------------------------------------------------------------------
/scripts/bash/gitlab_backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
4 |
5 | #Imports
6 | DEPLOYMENT_GCS_PREFIX=`curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/gcs-prefix`
7 | GCLOUD_LOG_NAME="gitlab-backup-exec"
8 |
9 | gsutil cp $DEPLOYMENT_GCS_PREFIX/scripts/bash/gcloud_logger.sh ./
10 | source ./gcloud_logger.sh
11 | gsutil cp $DEPLOYMENT_GCS_PREFIX/scripts/bash/gitlab_helpers.sh ./
12 | source ./gitlab_helpers.sh
13 |
14 |
15 |
16 | check_installation $GCLOUD_LOG_NAME
17 |
18 | if [ $GITLAB_INSTALLED == 'true' ]; then
19 | logger $GCLOUD_LOG_NAME "INFO" "Performing gitlab backup"
20 | get_backup_archive_password $GCLOUD_LOG_NAME
21 | execute_backup $GCLOUD_LOG_NAME $GITLAB_EE_VERSION
22 |
23 | else
24 | logger $GCLOUD_LOG_NAME "ERROR" "Gitlab installation was not found!"
25 | fi
26 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 SYGNIA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/debug.tf:
--------------------------------------------------------------------------------
1 | ### Debugging resources ####
2 |
3 | # Identity Aware Proxy (IAP)
4 | # IAP is GCP feature used to connect to GCE over SSH/RDP with browser or IAP Desktop
5 | # Adding FW rule for connecting to linux nodes, windows nodes and Gitlab instance via IAP
6 | resource "google_compute_firewall" "iap_pipeline" {
7 | count = var.debug_flag ? 1 : 0
8 | name = "${var.infra_name}-allow-iap"
9 | network = module.gcp-network.network_name
10 | provider = google.offensive-pipeline
11 | allow {
12 | protocol = "tcp"
13 | ports = ["22","3389"]
14 | }
15 |
16 | source_ranges = ["35.235.240.0/20"]
17 | target_tags = [
18 | local.gitlab_instance_name,
19 | local.gke_linux_pool_tag,
20 | local.gke_win_pool_tag
21 | ]
22 | }
23 |
24 | # DEBUG: Save Gitlab Server certificate.
25 | resource "local_file" "tls_key_pem_file" {
26 | count = var.debug_flag ? 1 : 0
27 | content = tls_private_key.gitlab-self-signed-cert-key.private_key_pem
28 | filename = "${path.module}/gitlab.local.key"
29 | }
30 | resource "local_file" "tls_cert_pem_file" {
31 | count = var.debug_flag ? 1 : 0
32 | content = tls_self_signed_cert.gitlab-self-signed-cert.cert_pem
33 | filename = "${path.module}/gitlab.local.crt"
34 | }
35 |
36 | # DEBUG: Save kube config file
37 |
38 | module "gke_auth" {
39 | count = var.debug_flag ? 1 : 0
40 | source = "terraform-google-modules/kubernetes-engine/google//modules/auth"
41 | project_id = var.project_id
42 | cluster_name = module.gke.name
43 | location = module.gke.location
44 | }
45 |
46 | resource "local_file" "kubeconfig" {
47 | depends_on = [module.gke_auth]
48 | count = var.debug_flag ? 1 : 0
49 | content = module.gke_auth[0].kubeconfig_raw
50 | filename = "${path.module}/kubeconfig"
51 | }
52 |
--------------------------------------------------------------------------------
/network.tf:
--------------------------------------------------------------------------------
1 | # VPC and subnets
2 |
3 | module "gcp-network" {
4 | source = "terraform-google-modules/network/google"
5 | project_id = var.project_id
6 | network_name = "${var.infra_name}-offensive-pipeline-vpc"
7 |
8 | subnets = [
9 | {
10 | subnet_name = "${var.infra_name}-offensive-pipeline-subnet"
11 | subnet_ip = local.vpc_main_subnet
12 | subnet_region = var.region
13 | }
14 | ]
15 |
16 | secondary_ranges = {
17 | ("${var.infra_name}-offensive-pipeline-subnet") = [
18 | {
19 | range_name = "${var.infra_name}-gke-pods-subnet"
20 | ip_cidr_range = local.gke_pod_subnet
21 | },
22 | {
23 | range_name = "${var.infra_name}-gke-service-subnet"
24 | ip_cidr_range = local.gke_svc_subnet
25 | }
26 | ]
27 | }
28 | }
29 |
30 |
31 | # Operators access rule
32 | resource "google_compute_firewall" "operators" {
33 | name = "${var.infra_name}-operators-access"
34 | network = module.gcp-network.network_name
35 | provider = google.offensive-pipeline
36 | source_ranges = var.operator_ips
37 | target_tags = [local.gitlab_instance_name]
38 |
39 | allow {
40 | protocol = "tcp"
41 | ports = var.operator_ports
42 | }
43 | }
44 |
45 |
46 | # Rule allowing internal access from K8s' Pods to Gitlab
47 | resource "google_compute_firewall" "pods-to-gitlab-access" {
48 | name = "${var.infra_name}-gke-pods-gitlab-access"
49 | network = module.gcp-network.network_name
50 | provider = google.offensive-pipeline
51 | source_ranges = [local.gke_pod_subnet]
52 | target_tags = [local.gitlab_instance_name]
53 |
54 | allow {
55 | protocol = "tcp"
56 | ports = var.operator_ports
57 | }
58 | }
59 |
60 |
61 |
62 | # DNS Record
63 |
64 | resource "google_dns_record_set" "ext-dns" {
65 | provider = google.dns_infra
66 | count = var.external_hostname != "" ? 1 : 0
67 | name = "${var.external_hostname}."
68 | type = "A"
69 | ttl = var.dns_record_ttl
70 | managed_zone = var.dns_managed_zone_name
71 | rrdatas = [google_compute_global_forwarding_rule.lb_forward_rule.ip_address]
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/scripts/bash/gcloud_logger.sh:
--------------------------------------------------------------------------------
1 | logger() {
2 | # Param 1: Log name
3 | # Param 2: Module name
4 | # Param 3: Level (INFO, DEBUG, ERROR)
5 | # Param 4: Message
6 | local log=$1
7 | local module=`hostname`
8 | local level=$2
9 | local message=$3
10 | error_levels=("INFO" "DEBUG" "ERROR")
11 | # Validate
12 | if [ $# -ne 3 ]; then
13 | echo "Bad params"
14 | fi
15 | # Check if error level is valid
16 | if ! [[ " ${error_levels[@]} " =~ " ${level} " ]]; then
17 | gcloud logging write $log "$module: Invalid log error level." --severity="ERROR"
18 | level="DEBUG"
19 | fi
20 | echo "$level: $message"
21 | gcloud logging write $log "$module: $message" --severity=$level
22 | }
23 |
24 |
25 |
26 | ### Try / Except execution wrapper with GCloud logging ###
27 | # Usage 1: exec_wrapper $ERR_ACTION_EXIT $LOGNAME "My Bash Command" # <- This will log an error (To LOGNAME) and exit the script in case of command error
28 | # Usage 2: exec_wrapper $ERR_ACTION_CONT $LOGNAME "My Bash Command" # <- This will only log an error (To LOGNAME) in case of command error
29 | # Successful command outputs will be printed out
30 |
31 | readonly ERR_ACTION_EXIT="Exit"
32 | readonly ERR_ACTION_CONT="Continue"
33 | readonly STD_OUT_PATH="/tmp/stdoutput"
34 | readonly STD_ERR_PATH="/tmp/stderr"
35 |
36 | exec_wrapper () {
37 |
38 | local errAction=$1
39 | local logName=$2
40 | local cmdExec=$3
41 | echo "wrapper exec: $cmdExec"
42 | $cmdExec 1>$STD_OUT_PATH 2>$STD_ERR_PATH
43 |
44 | local errCode=$?
45 | if [ $errCode -ne 0 ]; then
46 | errMsg=$(cat $STD_ERR_PATH)
47 | logger $logName "ERROR" "ErrCode: $errCode, ErrAction: $errAction, Message: $errMsg"
48 | if [[ $errAction == $ERR_ACTION_EXIT ]]; then
49 | logger $logName "DEBUG" "Stopping execution due to error action"
50 | exit 1
51 | fi
52 | fi
53 | cat $STD_OUT_PATH
54 | }
55 |
56 | exec_wrapper_no_print () {
57 | local errAction=$1
58 | local logName=$2
59 | local cmdExec=$3
60 | echo "wrapper exec: no print."
61 | $cmdExec 2> $STD_ERR_PATH
62 |
63 | local errCode=$?
64 | if [ $errCode -ne 0 ]; then
65 | errMsg=$(cat $STD_ERR_PATH)
66 | logger $logName "ERROR" "ErrCode: $errCode, ErrAction: $errAction, Message: $errMsg"
67 | if [[ $errAction == $ERR_ACTION_EXIT ]]; then
68 | logger $logName "DEBUG" "Stopping execution due to error action"
69 | exit 1
70 | fi
71 | fi
72 | }
--------------------------------------------------------------------------------
/locals.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | gitlab_instance_name = "${var.infra_name}-gitlab"
3 | instance_internal_domain = "${local.gitlab_instance_name}.${var.region}-${var.zone}.c.${var.project_id}.internal"
4 | instance_internal_url = "${var.gitlab_instance_protocol}://${local.instance_internal_domain}"
5 | gitlab_package_dl_link = join("/", [
6 | "https://packages.gitlab.com/gitlab/gitlab-ee/packages",
7 | "${var.os_name}",
8 | "${var.os_release}",
9 | "gitlab-ee_${var.gitlab_version}.0_amd64.deb",
10 | "download.deb"
11 | ])
12 | vpc_main_subnet = "10.0.0.0/22" # 10.0.0.0 - 10.0.3.255 , 1024 IPs
13 | gke_pod_subnet = "10.2.0.0/17" # 10.2.0.0 - 10.2.127.255 - 32,768 IPs.
14 | gke_svc_subnet = "10.2.128.0/20" # 10.2.128.0 - 10.2.143.255 - 4096 IPs.
15 | artifact_registry_host = "${var.region}-docker.pkg.dev"
16 | artifact_registry_id = "${var.infra_name}-containers"
17 | artifact_registry_namespace = "${var.project_id}/${local.artifact_registry_id}"
18 | gke_linux_pool_tag = "gke-${var.infra_name}-offensive-pipeline-gke-linux-pool"
19 | gke_win_pool_tag = "gke-${var.infra_name}-offensive-pipeline-windows-pool"
20 | gke_win_pool_start_script = join("/", [
21 | "gs:/",
22 | google_storage_bucket.deployment_utils.name,
23 | google_storage_bucket_object.disable_windows_defender_ps.name
24 | ])
25 |
26 | gitlab_startup_script = join("/", [
27 | "gs:/",
28 | google_storage_bucket.deployment_utils.name,
29 | google_storage_bucket_object.gitlab_startup_script.name
30 | ])
31 |
32 | gitlab_migrate_backup = var.migrate_gitlab ? join("/", [
33 | "gs:/",
34 | google_storage_bucket.deployment_utils.name,
35 | var.migrate_gitlab_backup_path
36 | ]) : ""
37 | }
--------------------------------------------------------------------------------
/scripts/bash/gitlab_startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Gitlab starup script
4 | # This script will either install, migrate or reconfigure Gitlab installation according to the values set in the instance metadata.
5 | # Installation / Migration will be executed once in a life of an instance.
6 | # Reconfiguration will be executed on every boot.
7 | # Reconfiguration updates the Gitlab's External URL and certificates if they changed through metadata.
8 |
9 |
10 | #Vars
11 | DEPLOYMENT_GCS_PREFIX=`curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/gcs-prefix`
12 | GITLAB_TARGET_INSTALL_VERSION=`curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/target-gitlab-version`
13 | GITLAB_PKG_LINK=`curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/gitlab-package-dl-link`
14 | GCS_PATH_TO_BACKUP=`curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/attributes/gcs-path-to-backup`
15 | GCLOUD_LOG_NAME="gitlab-startup"
16 | BACKUP_ARCHIVE_PATH="/tmp/backup_archived.zip"
17 |
18 | #Imports
19 | gsutil cp $DEPLOYMENT_GCS_PREFIX/scripts/bash/gitlab_helpers.sh ./
20 | gsutil cp $DEPLOYMENT_GCS_PREFIX/scripts/bash/gcloud_logger.sh ./
21 | source ./gcloud_logger.sh
22 | source ./gitlab_helpers.sh
23 |
24 |
25 | # Start
26 | logger $GCLOUD_LOG_NAME "INFO" "Starting Gitlab instance setup"
27 |
28 | check_installation $GCLOUD_LOG_NAME
29 | set_gitlab_vars $GCLOUD_LOG_NAME
30 |
31 | if [ $GITLAB_INSTALLED == 'false' ]; then
32 | logger $GCLOUD_LOG_NAME "INFO" "Starting Gitlab installation"
33 | gitlab_deps_install $GCLOUD_LOG_NAME
34 | gitlab_install $GCLOUD_LOG_NAME $GITLAB_PKG_LINK
35 | setup_cicd_vars $GCLOUD_LOG_NAME
36 |
37 | if [ $GCS_PATH_TO_BACKUP == 'NONE' ]; then
38 | create_groups $GCLOUD_LOG_NAME
39 | import_scallops_recipes $GCLOUD_LOG_NAME
40 | create_cicd_vars $GCLOUD_LOG_NAME
41 |
42 | else
43 | logger $GCLOUD_LOG_NAME "INFO" "Migrating Gitlab from provided backup $GCS_PATH_TO_BACKUP"
44 | get_backup_archive $GCLOUD_LOG_NAME $GCS_PATH_TO_BACKUP $BACKUP_ARCHIVE_PATH
45 | get_backup_archive_password $GCLOUD_LOG_NAME
46 | restore_backup $GCLOUD_LOG_NAME $BACKUP_ARCHIVE_PATH
47 | update_cicd_vars $GCLOUD_LOG_NAME
48 | fi
49 |
50 | seed_instance_reg_token $GCLOUD_LOG_NAME
51 | seed_gitlab_root_pwd $GCLOUD_LOG_NAME
52 | seed_scallops_recipes_runner_token $GCLOUD_LOG_NAME
53 | setup_gitlab_backup $GCLOUD_LOG_NAME $DEPLOYMENT_GCS_PREFIX
54 |
55 | else
56 | logger $GCLOUD_LOG_NAME "INFO" "Skipping Gitlab installation"
57 | check_upgrade $GCLOUD_LOG_NAME $GITLAB_TARGET_INSTALL_VERSION
58 | fi
59 |
60 |
61 | reconfigure_gitlab $GCLOUD_LOG_NAME
62 | logger $GCLOUD_LOG_NAME "INFO" "Gitlab instance setup completed"
--------------------------------------------------------------------------------
/storage.tf:
--------------------------------------------------------------------------------
1 | # CICD utilities Storage
2 |
3 | resource "random_string" "deployment_utils_bucket_suffix" {
4 | length = 4
5 | special = false
6 | lower = true
7 | upper = false
8 | }
9 |
10 | resource "google_storage_bucket" "deployment_utils" {
11 | name = "${var.infra_name}-utils-${random_string.deployment_utils_bucket_suffix.result}"
12 | location = "US"
13 | storage_class = "STANDARD"
14 | uniform_bucket_level_access = true
15 | provider = google.offensive-pipeline
16 | force_destroy = true
17 | }
18 |
19 |
20 | resource "google_storage_bucket_object" "disable_windows_defender_ps" {
21 | name = "scripts/Powershell/disabledefender.ps1"
22 | bucket = google_storage_bucket.deployment_utils.name
23 | source = "${path.module}/scripts/Powershell/disabledefender.ps1"
24 | }
25 |
26 |
27 | resource "google_storage_bucket_object" "gitlab_startup_script" {
28 | depends_on = [
29 | google_storage_bucket_object.gitlab_helpers_script,
30 | google_storage_bucket_object.gcloud_logger_script,
31 | google_storage_bucket_object.gitlab_backup_script]
32 |
33 | name = "scripts/bash/gitlab_startup.sh"
34 | bucket = google_storage_bucket.deployment_utils.name
35 | source = "${path.module}/scripts/bash/gitlab_startup.sh"
36 | }
37 |
38 | resource "google_storage_bucket_object" "gitlab_helpers_script" {
39 | name = "scripts/bash/gitlab_helpers.sh"
40 | bucket = google_storage_bucket.deployment_utils.name
41 | source = "${path.module}/scripts/bash/gitlab_helpers.sh"
42 | }
43 |
44 | resource "google_storage_bucket_object" "gcloud_logger_script" {
45 | name = "scripts/bash/gcloud_logger.sh"
46 | bucket = google_storage_bucket.deployment_utils.name
47 | source = "${path.module}/scripts/bash/gcloud_logger.sh"
48 | }
49 |
50 | resource "google_storage_bucket_object" "gitlab_backup_script" {
51 | depends_on = [google_storage_bucket_object.gitlab_helpers_script,
52 | google_storage_bucket_object.gcloud_logger_script]
53 |
54 | name = "scripts/bash/gitlab_backup.sh"
55 | bucket = google_storage_bucket.deployment_utils.name
56 | source = "${path.module}/scripts/bash/gitlab_backup.sh"
57 | }
58 |
59 | # Migration resource
60 |
61 | # Transfer is done in this way since state file won't support big files in resource attributes.
62 | resource "null_resource" "transfer_gitlab_backup" {
63 | count = var.migrate_gitlab ? 1 : 0
64 | triggers = {
65 | gcs_backup_path = "gs://${var.migrate_gitlab_backup_bucket}/${var.migrate_gitlab_backup_path}"
66 | }
67 | provisioner "local-exec" {
68 | when = create
69 | command = "gsutil cp gs://${var.migrate_gitlab_backup_bucket}/${var.migrate_gitlab_backup_path} ${local.gitlab_migrate_backup}"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 |
4 | ## [0.6] - 2024-08-19
5 | ### Added
6 | - Added LB resource and related resources
7 | - Added application external Certificate service
8 | - Modify DNS record set to LB IP instead of Gitlab instance
9 | - Assigned Gitlab to instance group
10 | - Added option to specify external integration IP addresses.
11 | - Upgraded google terraform provider
12 |
13 |
14 | ## [0.5] - 2023-07-19
15 | ### Added
16 | - Enabled runner session server on linux-runner to allow the use of Interactive Web Terminal (Job Debug button)
17 |
18 | ### Changed
19 | - Shortened runner poll intervals
20 | - Optimize K8s performance
21 | - Upgraded GKE to version 1.26.5
22 | - Containers are now stored in Artifact Registry instead of deprecating Container Registry (GCR).
23 | - Modified node pools disk type to SSD
24 | - Modified Linux node image type to COS_CONTAINERD
25 | - Enabled image streaming
26 | - Upgraded related providers and modules
27 | - Modified k8s pdb resources according to provider upgrades
28 | - Gitlab 16.1.2 upgrade
29 | - Default to Gitlab 16.1.2-ee and runner 0.54.0
30 | - Option to set target Gitlab version to upgrade the application from tf vars
31 | - Added relevant locals
32 | - Added multiple ubuntu release options for the instance
33 | - Changed runner TOML config to be set from terraform instead of Helm chart Yaml.
34 | - Modified Gitlab instance disk to 160G SSD.
35 | - Added Gitlab DEB package link metadata variable.
36 |
37 | ### Fixed
38 | - Backup creation bug. The created backup snapshot was not deleted after upload, causing overload to the local disk.
39 | - Modified IAM binding to add memeber instead of overwrite all members on user resources.
40 | - Fixed Gitlab restore bug via new exec-wrapper function.
41 |
42 | ## [0.4] - 2022-10-22
43 | ### Added
44 | - Option to specify Scallop-Recipes repo URL (For auto import)
45 | - Error handling for Gitlab installation and backup Bash scripts
46 | - Deployment TF debug option - IAP firewall rules, export configuration files
47 | - Integration with GCP logger for Gitlab installation and backup
48 |
49 | ### Changed
50 | - README + architecture image
51 | - Updated K8s RBAC permissions for runners
52 | - Removed generated backup password. Password secret is supplied by the user.
53 | - Updated tfvars template
54 | - Upgraded Gitlab, K8s, Runners versions
55 | - Adjusted instance level CI/CD variables names
56 | - Upgraded Windows node pool machine type to 4 vCPU & 16GB RAM
57 | - Now limiting concurrent jobs for 30 in Windows and 50 in Linux
58 | - K8s nodes are now preemptible
59 |
60 | ### Fixed
61 | - Fixed TF resources dependencies
62 | - Fixed Defender disarm detection issue
63 | - Fixed K8s Scale up issues
64 |
65 | ## [0.3] - 2022-01-07
66 | ### Added
67 | - Added support to migrate from an older Gitlab instance
68 | - Added Gitlab instance automatic backup capability
69 | - K8s Pod disruption budget resources to fix scale down issue
70 | - New runner that supports Dockerhub credentials to pull private images
71 |
72 | ### Changed
73 | - Modified max pods per node in K8s, meanning more CI jobs simoultaneously
74 | - Scopred bucket admin permissions to conainer registry bucket only
75 | - Reorganized resources into seperate files
76 | - Increased CPUs on linux pool
77 | - Removed Gitlab API token seeding. Gitlab modifications performed using gitlab-rails console
78 |
79 | ### Fixed
80 | - Windows Runner configuration
81 | - Minor fixes, duplicate code removal
82 | - Fixed Linux won't scale down problem
83 |
84 |
85 | ## [0.2] - 2021-12-21
86 | ### Fixed
87 | - Windows Runner configuration
88 | - Relativ paths
89 | - Runner version update
90 |
91 |
92 | ## [0.1] - 2021-08-09
93 | ### Initial
94 | - Initial working deployment
95 |
--------------------------------------------------------------------------------
/config.tfvars:
--------------------------------------------------------------------------------
1 | ##### Scallops IAC variables #####
2 | #### Note that some variables are required (#Required), and some variables modifications will take effect also after deployment (#PostDeploymentModifiable).
3 |
4 |
5 | ## GCP Project ID
6 | project_id = "" #Required
7 |
8 | ## The name you wish to have as a prefix for the deployment's resources. Must comply with ^[a-z]([a-z0-9]*[a-z0-9])$
9 | infra_name = "scallops" #Required
10 |
11 | ## The name of an existing bucket you wish to receive backups to. Terraform will create the required permission to upload the backup archive.
12 | backups_bucket_name = "" #Required #PostDeploymentModifiable
13 |
14 | ## An existing secret ID in the same GCP project (project_id) storing a password for the backup process (Allowed symbols for secret value: -_ )
15 | ## Creating a secret through GCP secret manager https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets#create
16 | gitlab_backup_key_secret_id = "" #Required #PostDeploymentModifiable
17 |
18 |
19 |
20 | ## Gitlab version to install
21 | ## Ruuner chart version must be compaitibe with the Gitlab version -> https://docs.gitlab.com/runner/#gitlab-runner-versions
22 | ## Note the Gitlab application version from the selected Chart version -> https://artifacthub.io/packages/helm/gitlab/gitlab-runner
23 | ## You can make upgrades to your Gitlab instance from here. Just reset the instance once the `apply` completes.
24 | # gitlab_version = "16.1.2-ee"
25 | # runner_chart_url = "https://gitlab-charts.s3.amazonaws.com/gitlab-runner-0.54.0.tgz"
26 |
27 |
28 | ## IP addresses that can interact with the Gitlab instance via HTTP/S (Office IP / Home IPs)
29 | # operator_ips = [] #Optional #PostDeploymentModifiable
30 |
31 | ## Enable debugging resources such as IAP Firewall rules, and export of config files
32 | # debug_flag = false #Optional #PostDeploymentModifiable
33 |
34 | ## The Gitlab instance Web server protocol, http or https.
35 | # gitlab_instance_protocol = "https" #Optional
36 |
37 | ## Region for the k8s cluster, Gitlab instance and network.
38 | # region = us-central-1 #Optional
39 |
40 | ## Zone for the k8s cluster, Gitlab instance and network.
41 | # zone = "a" #Optional
42 |
43 |
44 |
45 | ## External DNS ## #Optional #PostDeploymentModifiable #GitlabRestartRequired
46 | ## Uncomment the 3 lines below if wishing to supply external DNS name for accessing Gitalb instance
47 |
48 | # dns_project_id = "" # The project ID where the managed DNS zone is located
49 | # dns_managed_zone_name = "mydomain-com" # The configured managed DNS zone name
50 | # external_hostname = "scallops.mydomain.com" # The hostname you wish to set of the instance (Must be subdomain of the managed zone)
51 |
52 |
53 |
54 | ## Docker hub credentials (https://docs.docker.com/docker-hub/access-tokens/) #Optional #PostDeploymentModifiable
55 | ## An existing secret name in secret-manager storing Dockerhub credentials to fetch private container images (format is username:password or username:access-token).
56 | # dockerhub-creds-secret = ""
57 |
58 |
59 | ## Scallops-Recipes repository. Use the default repository or specify alternative fork in a Git path HTTPS format.
60 | ## The specified repository will be imported to Gitlab as the Scallops-Recipes repository.
61 | ## *Ignored if performing a migration
62 | # scallops_recipes_git_url = "https://github.com/SygniaLabs/ScallOps-Recipes.git" #Optional
63 | # scallops_recipes_git_creds_secret = "my-github-creds-secret" #Optional
64 |
65 |
66 |
67 | ## Migration variables #Optional
68 | ## If you plan on migrating from a different gitlab instance, uncomment all migration variables below, and follow requirements.
69 | ## 1. 'gitlab_backup_key_secret_id' secret must store the password value decrypting the archived backup zip.
70 | ## 2. 'gitlab_version' must be equal to the version you are migrating from.
71 | ## 3. Operation requires Gsutil on the terraform deployer system as backup will be downloaded locally
72 |
73 | # migrate_gitlab = true ## If performing migration from another Gitlab instance and got a backup file from previous instance. true/false.
74 | # migrate_gitlab_backup_bucket = "" ## The Google Storage Bucket to your Gitlab backup e.g. 'mybucket1-abcd'
75 | # migrate_gitlab_backup_path = "" ## The path to the archived backup zip e.g 'backups/gitlab-xxx-backup.zip'
--------------------------------------------------------------------------------
/secrets.tf:
--------------------------------------------------------------------------------
1 | #### Secret creation and version manager ####
2 |
3 |
4 | # Self signed TLS certificate generation
5 |
6 | resource "tls_private_key" "gitlab-self-signed-cert-key" {
7 | algorithm = "ECDSA"
8 | ecdsa_curve = "P384"
9 | }
10 |
11 | resource "tls_self_signed_cert" "gitlab-self-signed-cert" {
12 | private_key_pem = tls_private_key.gitlab-self-signed-cert-key.private_key_pem
13 |
14 | subject {
15 | common_name = "gitlab.local"
16 | organization = "Company"
17 | }
18 |
19 | dns_names = flatten([
20 | "${local.gitlab_instance_name}.local",
21 | local.instance_internal_domain,
22 | var.external_hostname != "" ? [var.external_hostname] : []
23 | ])
24 |
25 | ip_addresses = ["10.0.0.2"]
26 | validity_period_hours = 87600 //Certificate will be valid for 10 years
27 |
28 | allowed_uses = [
29 | "key_encipherment",
30 | "digital_signature"
31 | ]
32 | }
33 |
34 |
35 | # Gitlab server certificate
36 |
37 | resource "google_secret_manager_secret" "gitlab-self-signed-cert-key" {
38 | provider = google.offensive-pipeline
39 | secret_id = "${local.gitlab_instance_name}-cert-key"
40 | labels = {
41 | label = "gitlab-cert"
42 | }
43 | replication {
44 | user_managed {
45 | replicas {
46 | location = var.region
47 | }
48 | }
49 | }
50 | }
51 |
52 |
53 | resource "google_secret_manager_secret_version" "gitlab-self-signed-cert-key-version" {
54 | secret = google_secret_manager_secret.gitlab-self-signed-cert-key.id
55 | secret_data = tls_private_key.gitlab-self-signed-cert-key.private_key_pem
56 | }
57 |
58 |
59 | resource "google_secret_manager_secret" "gitlab-self-signed-cert-crt" {
60 | provider = google.offensive-pipeline
61 | secret_id = "${local.gitlab_instance_name}-cert-crt"
62 | labels = {
63 | label = "gitlab-cert"
64 | }
65 | replication {
66 | user_managed {
67 | replicas {
68 | location = var.region
69 | }
70 | }
71 | }
72 | }
73 |
74 |
75 | resource "google_secret_manager_secret_version" "gitlab-self-signed-cert-crt-version" {
76 | secret = google_secret_manager_secret.gitlab-self-signed-cert-crt.id
77 | secret_data = tls_self_signed_cert.gitlab-self-signed-cert.cert_pem
78 | }
79 |
80 |
81 |
82 | # Gitlab installation & deployment
83 |
84 | resource "random_password" "gitlab_runner_registration_token" {
85 | length = 20
86 | special = true
87 | override_special = "-_"
88 | }
89 | resource "random_password" "gitlab_initial_root_pwd" {
90 | length = 16
91 | special = true
92 | override_special = "-_"
93 | }
94 |
95 |
96 | # Gitlab runner registration token
97 |
98 | resource "google_secret_manager_secret" "gitlab_runner_registration_token" {
99 | provider = google.offensive-pipeline
100 | secret_id = "${local.gitlab_instance_name}-runner-reg"
101 | labels = {
102 | label = "gitlab"
103 | }
104 | replication {
105 | user_managed {
106 | replicas {
107 | location = var.region
108 | }
109 | }
110 | }
111 | }
112 |
113 |
114 | resource "google_secret_manager_secret_version" "gitlab_runner_registration_token" {
115 | secret = google_secret_manager_secret.gitlab_runner_registration_token.id
116 | secret_data = random_password.gitlab_runner_registration_token.result
117 | }
118 |
119 |
120 | # Gitlab initial root password
121 | resource "google_secret_manager_secret" "gitlab_initial_root_pwd" {
122 | provider = google.offensive-pipeline
123 | secret_id = "${local.gitlab_instance_name}-root-password"
124 | labels = {
125 | label = "gitlab"
126 | }
127 | replication {
128 | user_managed {
129 | replicas {
130 | location = var.region
131 | }
132 | }
133 | }
134 | }
135 |
136 |
137 | resource "google_secret_manager_secret_version" "gitlab_initial_root_pwd" {
138 | secret = google_secret_manager_secret.gitlab_initial_root_pwd.id
139 | secret_data = random_password.gitlab_initial_root_pwd.result
140 | }
141 |
142 |
143 | # Docker hub credentials secret
144 |
145 | data "google_secret_manager_secret_version" "dockerhub-secret" {
146 | provider = google.offensive-pipeline
147 | count = var.dockerhub-creds-secret != "" ? 1 : 0
148 | secret = var.dockerhub-creds-secret
149 | }
--------------------------------------------------------------------------------
/load-balancer.tf:
--------------------------------------------------------------------------------
1 | resource "google_compute_managed_ssl_certificate" "lb_proxy_ssl_cert" {
2 | provider = google.offensive-pipeline
3 | count = var.external_hostname != "" ? 1 : 0
4 | name = "${local.gitlab_instance_name}-ssl-cert"
5 | managed {
6 | domains = [var.external_hostname]
7 | }
8 | }
9 |
10 | resource "google_compute_health_check" "gitlab_service_hc" {
11 | provider = google.offensive-pipeline
12 | name = "${local.gitlab_instance_name}-backend-hc"
13 | timeout_sec = 5
14 | check_interval_sec = 30
15 | healthy_threshold = 1
16 | unhealthy_threshold = 3
17 |
18 | dynamic "http_health_check" {
19 | for_each = var.gitlab_instance_protocol == "http" ? [1] : []
20 | content {
21 | port_name = "http"
22 | port_specification = "USE_NAMED_PORT"
23 | request_path = "/robots.txt"
24 | proxy_header = "NONE"
25 | }
26 | }
27 |
28 | dynamic "https_health_check" {
29 | for_each = var.gitlab_instance_protocol == "https" ? [1] : []
30 | content {
31 | port_name = "https"
32 | port_specification = "USE_NAMED_PORT"
33 | request_path = "/robots.txt"
34 | proxy_header = "NONE"
35 | }
36 | }
37 |
38 | }
39 |
40 | resource "google_compute_security_policy" "lb_allow_operators_policy" {
41 | provider = google.offensive-pipeline
42 | name = "${local.gitlab_instance_name}-backend-allowed-ips"
43 |
44 |
45 |
46 | rule {
47 | action = "allow"
48 | priority = "1000"
49 | description = "Allow specific IPs"
50 | match {
51 | versioned_expr = "SRC_IPS_V1"
52 | config {
53 | src_ip_ranges = var.operator_ips
54 | }
55 | }
56 |
57 | }
58 |
59 | # Conditionally create rule for external integration address ranges
60 | dynamic "rule" {
61 | for_each = var.external_integration_ranges != null ? [1] : []
62 | content {
63 | action = "allow"
64 | priority = 1001
65 | description = "External Integration IP addresses"
66 |
67 | match {
68 | versioned_expr = "SRC_IPS_V1"
69 | config {
70 | src_ip_ranges = var.external_integration_ranges
71 | }
72 | }
73 | }
74 | }
75 |
76 |
77 | rule {
78 | action = "deny(502)"
79 | priority = "2147483647"
80 | description = "Default rule deny all addresses"
81 | match {
82 | versioned_expr = "SRC_IPS_V1"
83 | config {
84 | src_ip_ranges = ["*"]
85 | }
86 | }
87 |
88 | }
89 | }
90 |
91 | resource "google_compute_backend_service" "gitlab_instance_service" {
92 | provider = google.offensive-pipeline
93 | name = "${local.gitlab_instance_name}-backend"
94 | port_name = var.gitlab_instance_protocol
95 | protocol = upper(var.gitlab_instance_protocol)
96 | timeout_sec = 10
97 | security_policy = google_compute_security_policy.lb_allow_operators_policy.self_link
98 | backend {
99 | group = google_compute_instance_group.gitlab_instance.self_link
100 | }
101 | health_checks = [
102 | google_compute_health_check.gitlab_service_hc.self_link,
103 | ]
104 | }
105 |
106 | resource "google_compute_url_map" "service_url_map" {
107 | provider = google.offensive-pipeline
108 | name = "${local.gitlab_instance_name}-urlmap"
109 | default_service = google_compute_backend_service.gitlab_instance_service.self_link
110 | }
111 |
112 | resource "google_compute_target_https_proxy" "lb_https_proxy" {
113 | provider = google.offensive-pipeline
114 | name = "${local.gitlab_instance_name}-https-proxy"
115 | url_map = google_compute_url_map.service_url_map.self_link
116 | ssl_certificates = (
117 | var.external_hostname != "" ?
118 | [google_compute_managed_ssl_certificate.lb_proxy_ssl_cert.0.self_link] : []
119 | )
120 | }
121 |
122 | resource "google_compute_global_forwarding_rule" "lb_forward_rule" {
123 | provider = google.offensive-pipeline
124 | name = "${local.gitlab_instance_name}-fwd-rule"
125 | target = google_compute_target_https_proxy.lb_https_proxy.self_link
126 | port_range = "443"
127 | }
128 |
129 | # https://cloud.google.com/load-balancing/docs/firewall-rules
130 | resource "google_compute_firewall" "allow_lb_gitlab_access" {
131 | provider = google.offensive-pipeline
132 | name = "${local.gitlab_instance_name}-allow-lb"
133 | network = module.gcp-network.network_name
134 | source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]
135 | target_tags = google_compute_instance.gitlab.tags
136 | allow {
137 | protocol = "tcp"
138 | ports = var.operator_ports
139 | }
140 | }
--------------------------------------------------------------------------------
/iam.tf:
--------------------------------------------------------------------------------
1 | #### IAM svc accounts, roles and bindings ####
2 |
3 | ## Service Accounts ##
4 |
5 | # Gitlab compute instance service account
6 | resource "google_service_account" "gitlab_service_account" {
7 | account_id = "${local.gitlab_instance_name}-svc"
8 | display_name = "Gitlab Service Account"
9 | provider = google.offensive-pipeline
10 | }
11 |
12 | # Service account with capability to push container images to sepcific repository in artifact registry
13 | resource "google_service_account" "kaniko" {
14 | account_id = "${var.infra_name}-gke-bucket"
15 | display_name = "Service Account for Pods to access artifact registry repository"
16 | provider = google.offensive-pipeline
17 | }
18 |
19 | ## Roles ##
20 |
21 | resource "random_string" "custom_roles_suffix" {
22 | length = 4
23 | special = false
24 | lower = true
25 | upper = false
26 | }
27 |
28 | # Create role with permissions to backup only without read/overwrite/delete
29 | resource "google_project_iam_custom_role" "backup_archive_role" {
30 | role_id = "gitlab_backupArchive_${var.infra_name}_${random_string.custom_roles_suffix.result}"
31 | title = "Gitlab Backup Role for ${var.infra_name}"
32 | description = "A role attached to the gitlab compute service account allowing it to update new backup archives to the specified bucket (var.backups_bucket_name)."
33 | permissions = [
34 | "storage.objects.list",
35 | "storage.objects.create",
36 | "storage.multipartUploads.create",
37 | "storage.multipartUploads.listParts",
38 | "storage.multipartUploads.abort"
39 | ]
40 | provider = google.offensive-pipeline
41 | }
42 |
43 |
44 | # Role that allows startup script write logs
45 | resource "google_project_iam_custom_role" "compute_log_role" {
46 | role_id = "gitlab_customRole_${var.infra_name}_${random_string.custom_roles_suffix.result}"
47 | title = "Write logs role for ${var.infra_name}"
48 | description = "A role attached to the gitlab compute instance allowing it to write logs."
49 | permissions = ["logging.logEntries.create"]
50 | provider = google.offensive-pipeline
51 | }
52 |
53 |
54 | ## Bindings ##
55 |
56 | # Service account user role binding
57 | resource "google_project_iam_member" "sa_binding" {
58 | project = var.project_id
59 | provider = google.offensive-pipeline
60 | role = "roles/iam.serviceAccountUser"
61 | member = "serviceAccount:${google_service_account.gitlab_service_account.email}"
62 | }
63 |
64 | # Gitlab instance IAM Binding to storage
65 | resource "google_storage_bucket_iam_binding" "binding" {
66 | bucket = google_storage_bucket.deployment_utils.name
67 | role = "roles/storage.objectViewer" # To Read startup scripts on deployment utils bucket.
68 | members = [
69 | "serviceAccount:${google_service_account.gitlab_service_account.email}"
70 | ]
71 | }
72 |
73 |
74 | # Attach gitlab compute service account to the bucket with the backup role
75 | resource "google_storage_bucket_iam_binding" "backup_bucket_binding" {
76 | bucket = var.backups_bucket_name
77 | role = google_project_iam_custom_role.backup_archive_role.name
78 | members = [
79 | "serviceAccount:${google_service_account.gitlab_service_account.email}"
80 | ]
81 | }
82 |
83 | # Bind the compute custom role the the service account
84 | resource "google_project_iam_binding" "compute_binding" {
85 | project = var.project_id
86 | provider = google.offensive-pipeline
87 | role = google_project_iam_custom_role.compute_log_role.name
88 | members = [
89 | "serviceAccount:${google_service_account.gitlab_service_account.email}",
90 | ]
91 | }
92 |
93 |
94 | # Bind artifact registry write access to specific repository
95 | resource "google_artifact_registry_repository_iam_member" "writer" {
96 | project = google_artifact_registry_repository.containers.project
97 | location = google_artifact_registry_repository.containers.location
98 | repository = google_artifact_registry_repository.containers.name
99 | role = "roles/artifactregistry.writer"
100 | member = "serviceAccount:${google_service_account.kaniko.email}"
101 | }
102 |
103 | resource "google_service_account_key" "artifact_registry_writer" {
104 | service_account_id = google_service_account.kaniko.name
105 | }
106 |
107 |
108 | # IAM Bindings for Gitlab instance to secrets
109 | resource "google_secret_manager_secret_iam_binding" "gitlab-self-key-binding" {
110 | project = google_secret_manager_secret.gitlab-self-signed-cert-key.project
111 | secret_id = google_secret_manager_secret.gitlab-self-signed-cert-key.secret_id
112 | role = "roles/secretmanager.secretAccessor"
113 | members = [
114 | "serviceAccount:${google_service_account.gitlab_service_account.email}",
115 | ]
116 | }
117 |
118 | resource "google_secret_manager_secret_iam_binding" "gitlab-self-crt-binding" {
119 | project = google_secret_manager_secret.gitlab-self-signed-cert-crt.project
120 | secret_id = google_secret_manager_secret.gitlab-self-signed-cert-crt.secret_id
121 | role = "roles/secretmanager.secretAccessor"
122 | members = [
123 | "serviceAccount:${google_service_account.gitlab_service_account.email}",
124 | ]
125 | }
126 |
127 |
128 | resource "google_secret_manager_secret_iam_binding" "gitlab_runner_registration_token" {
129 | project = google_secret_manager_secret.gitlab_runner_registration_token.project
130 | secret_id = google_secret_manager_secret.gitlab_runner_registration_token.secret_id
131 | role = "roles/secretmanager.secretAccessor"
132 | members = [
133 | "serviceAccount:${google_service_account.gitlab_service_account.email}",
134 | ]
135 | }
136 |
137 |
138 | resource "google_secret_manager_secret_iam_binding" "gitlab_initial_root_pwd" {
139 | project = google_secret_manager_secret.gitlab_initial_root_pwd.project
140 | secret_id = google_secret_manager_secret.gitlab_initial_root_pwd.secret_id
141 | role = "roles/secretmanager.secretAccessor"
142 | members = [
143 | "serviceAccount:${google_service_account.gitlab_service_account.email}",
144 | ]
145 | }
146 |
147 |
148 | resource "google_secret_manager_secret_iam_member" "gitlab_backup_key" {
149 | project = var.project_id
150 | secret_id = var.gitlab_backup_key_secret_id
151 | role = "roles/secretmanager.secretAccessor"
152 | member = "serviceAccount:${google_service_account.gitlab_service_account.email}"
153 | }
154 |
155 | resource "google_secret_manager_secret_iam_member" "git_creds" {
156 | count = var.scallops_recipes_git_creds_secret != "" ? 1 : 0
157 | project = var.project_id
158 | secret_id = var.scallops_recipes_git_creds_secret
159 | role = "roles/secretmanager.secretAccessor"
160 | member = "serviceAccount:${google_service_account.gitlab_service_account.email}"
161 | }
--------------------------------------------------------------------------------
/vars.tf:
--------------------------------------------------------------------------------
1 | # Deployment wide variables
2 | variable "project_id" {
3 | type = string
4 | description = "(required) GCP Project ID to deploy to"
5 | }
6 |
7 | variable "dns_project_id" {
8 | type = string
9 | description = "If provided external_hostname, specify GCP Project ID where managed zone is located"
10 | default = ""
11 | }
12 |
13 | variable "infra_name" {
14 | type = string
15 | description = "(required) Infrastructure name or Team name"
16 | validation {
17 | condition = can(regex("^[a-z]([a-z0-9]*[a-z0-9])$", var.infra_name)) // Due to Certificate SAN, and service account name translation to email, and 1 another.
18 | error_message = "The infrastructure name must comply with the following regex ^[a-z]([a-z0-9]*[a-z0-9])$ )."
19 | }
20 | }
21 |
22 | # Backup variables
23 | variable "backups_bucket_name" {
24 | type = string
25 | description = "The name of the bucket backups are stored. Bucket must exist before apply. Terrafrom will add objectCreator permission to the gitlab svc account."
26 | }
27 |
28 | variable "gitlab_backup_key_secret_id" {
29 | description = "An existing secret ID in the same GCP project (project_id) storing a password for the backup process (Allowed symbols: -_ )"
30 | type = string
31 | default = ""
32 | }
33 |
34 | # Migration variables
35 | variable "migrate_gitlab" {
36 | type = bool
37 | description = "If performing migration from another Gitlab instance and got a backup file from previous instance"
38 | default = false
39 | }
40 |
41 |
42 | variable "migrate_gitlab_backup_bucket" {
43 | type = string
44 | description = "The Google Storage Bucket to your Gitlab backup e.g. 'mybucket1-abcd'"
45 | default = ""
46 | }
47 |
48 | variable "migrate_gitlab_backup_path" {
49 | type = string
50 | description = "The path to the archived backup zip 'backups/gitlab-xxx-backup.zip'"
51 | default = ""
52 | }
53 |
54 |
55 |
56 | # Gitlab instance related variables
57 |
58 | variable "gitlab_instance_protocol" {
59 | type = string
60 | description = "(optional) Protocol to use for Gitlab instance http / https"
61 | default = "https"
62 | validation {
63 | condition = can(regex("^https?$", var.gitlab_instance_protocol))
64 | error_message = "The gitlab_instance_protocol can be either http/https."
65 | }
66 | }
67 |
68 | variable "gitlab_version" {
69 | type = string
70 | description = "Gitlab version to install (e.g. 15.2.1-ee). If performing migration, you must specify the Gitlab backup version from the previous instance"
71 | default = "16.1.2-ee"
72 | validation {
73 | condition = can(regex("^[0-9]+.[0-9]+.[0-9]+-ee$", var.gitlab_version))
74 | error_message = "Invalid Gitlab version"
75 | }
76 | }
77 |
78 | variable "plans" {
79 | type = map
80 | default = {
81 | "2x8" = "n1-standard-2" #(56.72$, 0.097118$)
82 | }
83 | }
84 |
85 | variable "size" {
86 | type = string
87 | default = "2x8"
88 | }
89 |
90 | variable "os_images" {
91 | type = map
92 | default = {
93 | "ubuntu" = {
94 | "jammy" = "ubuntu-2204-lts"
95 | "focal" = "ubuntu-2004-lts"
96 | "bionic" = "ubuntu-1804-lts"
97 | }
98 | }
99 | }
100 |
101 | variable "os_name" {
102 | type = string
103 | description = "(optional) OS Image"
104 | default = "ubuntu"
105 | }
106 |
107 | variable "os_release" {
108 | type = string
109 | description = "(optional) OS Image"
110 | default = "focal"
111 | }
112 |
113 | variable "scallops_recipes_git_url" {
114 | type = string
115 | description = "Scallops-Recipes repository. Git URL must be provided in the following format: https://
22 |
233 |
234 |