├── backend.tf ├── .gitmodules ├── CODEOWNERS ├── providers.tf ├── Jenkinsfile_updatecli ├── versions.tf ├── updatecli ├── values.yaml └── updatecli.d │ └── service-ips.yaml ├── outputs.tf ├── Jenkinsfile_k8s ├── .gitignore ├── locals.tf ├── pkg.origin.jenkins.io.tf ├── .terraform.lock.hcl ├── ec2_agents_authorized_keys ├── README.adoc └── network.tf /backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" {} 3 | } 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".shared-tools"] 2 | path = .shared-tools 3 | url = https://github.com/jenkins-infra/shared-tools 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Order is important. The last matching pattern has the most precedence. 2 | 3 | * @jenkins-infra/terraform 4 | * @jenkins-infra/kubernetes 5 | -------------------------------------------------------------------------------- /providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | default_tags { 4 | tags = local.common_tags 5 | } 6 | } 7 | 8 | provider "local" { 9 | } 10 | -------------------------------------------------------------------------------- /Jenkinsfile_updatecli: -------------------------------------------------------------------------------- 1 | updatecli(action: 'diff') 2 | 3 | if (env.BRANCH_IS_PRIMARY) { 4 | // Only trigger a daily check on the principal branch 5 | properties([pipelineTriggers([cron('@daily')])]) 6 | updatecli(action: 'apply') 7 | } 8 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 1.12, <1.13" 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | } 8 | local = { 9 | source = "hashicorp/local" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /updatecli/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | user: "jenkins-infra-updatecli" 4 | email: "178728+jenkins-infra-updatecli[bot]@users.noreply.github.com" 5 | token: "UPDATECLI_GITHUB_TOKEN" 6 | branch: "main" 7 | owner: "jenkins-infra" 8 | repository: "aws" 9 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | resource "local_file" "jenkins_infra_data_report" { 2 | content = jsonencode({ 3 | "pkg.origin.jenkins.io" = { 4 | "service_ips" = { 5 | "ipv4" = data.aws_instance.pkg_origin_jenkins_io.public_ip, 6 | } 7 | "outbound_ips" = { 8 | "ipv4" = data.aws_instance.pkg_origin_jenkins_io.public_ip, 9 | } 10 | }, 11 | }) 12 | filename = "${path.module}/jenkins-infra-data-reports/aws.json" 13 | } 14 | output "jenkins_infra_data_report" { 15 | value = local_file.jenkins_infra_data_report.content 16 | } 17 | -------------------------------------------------------------------------------- /Jenkinsfile_k8s: -------------------------------------------------------------------------------- 1 | if (env.BRANCH_IS_PRIMARY) { 2 | // Only trigger a daily check on the principal branch 3 | properties([pipelineTriggers([cron('@daily')])]) 4 | } 5 | 6 | terraform( 7 | stagingCredentials: [ 8 | string(variable: 'AWS_ACCESS_KEY_ID', credentialsId: 'staging-terraform-aws-access-key'), 9 | string(variable: 'AWS_SECRET_ACCESS_KEY', credentialsId:'staging-terraform-aws-secret-key'), 10 | file(variable: 'BACKEND_CONFIG_FILE', credentialsId: 'staging-terraform-aws-backend-config'), 11 | ], 12 | productionCredentials: [ 13 | string(variable: 'AWS_ACCESS_KEY_ID', credentialsId: 'production-terraform-aws-access-key'), 14 | string(variable: 'AWS_SECRET_ACCESS_KEY', credentialsId:'production-terraform-aws-secret-key'), 15 | file(variable: 'BACKEND_CONFIG_FILE', credentialsId: 'production-terraform-aws-backend-config'), 16 | ], 17 | publishReports: ['jenkins-infra-data-reports/aws.json'], 18 | ) 19 | -------------------------------------------------------------------------------- /.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 any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | 31 | # Custom backend setup 32 | backend-config 33 | kubeconfig* 34 | # Custom files generated by our pipelines and tools 35 | terraform-plan-for-humans.txt 36 | terraform-plan-output.txt 37 | tfplan 38 | -------------------------------------------------------------------------------- /locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | aws_account_id = "200564066411" 3 | 4 | common_tags = { 5 | "scope" = "terraform-managed" 6 | "repository" = "jenkins-infra/aws" 7 | "cb:user" = "dduportal" 8 | "cb:environment" = "production" 9 | "cb:owner" = "Community-Team" 10 | "cb-env-type" = "external" 11 | } 12 | 13 | # Tracked by 'updatecli' from the following source: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 14 | outbound_ips_trusted_ci_jenkins_io = "104.209.128.236" 15 | # Tracked by 'updatecli' from the following source: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 16 | outbound_ips_infra_ci_jenkins_io = "20.57.120.46 52.179.141.53 172.210.200.59 20.10.193.4" 17 | # Tracked by 'updatecli' from the following source: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 18 | outbound_ips_private_vpn_jenkins_io = "52.232.183.117" 19 | # TODO: track with updatecli 20 | inbound_ips_archives_jenkins_io = "46.101.121.132 2a03:b0c0:3:d0::9bc:d001" 21 | # TODO: track with updatecli 22 | inbound_ips_ftp_osl_osuosl_org = "140.211.166.134 2605:bc80:3010::134" 23 | } 24 | -------------------------------------------------------------------------------- /updatecli/updatecli.d/service-ips.yaml: -------------------------------------------------------------------------------- 1 | name: Update external service IPs 2 | 3 | scms: 4 | default: 5 | kind: github 6 | spec: 7 | user: "{{ .github.user }}" 8 | email: "{{ .github.email }}" 9 | owner: "{{ .github.owner }}" 10 | repository: "{{ .github.repository }}" 11 | token: "{{ requiredEnv .github.token }}" 12 | username: "{{ .github.username }}" 13 | branch: "{{ .github.branch }}" 14 | 15 | sources: 16 | getTrustedCiJenkinsIo: 17 | kind: json 18 | spec: 19 | file: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 20 | key: .trusted\.ci\.jenkins\.io.outbound_ips 21 | transformers: 22 | - trimprefix: "[" 23 | - trimsuffix: "]" 24 | 25 | getInfraCiJenkinsIo: 26 | kind: json 27 | spec: 28 | file: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 29 | key: .infra\.ci\.jenkins\.io.outbound_ips 30 | transformers: 31 | - trimprefix: "[" 32 | - trimsuffix: "]" 33 | 34 | getPrivateVpn: 35 | kind: json 36 | spec: 37 | file: https://reports.jenkins.io/jenkins-infra-data-reports/azure-net.json 38 | key: .private\.vpn\.jenkins\.io.outbound_ips 39 | transformers: 40 | - trimprefix: "[" 41 | - trimsuffix: "]" 42 | 43 | targets: 44 | setTrustedCiJenkinsIo: 45 | sourceid: getTrustedCiJenkinsIo 46 | name: Update trusted.ci.jenkins.io outbound IPs 47 | kind: hcl 48 | spec: 49 | file: locals.tf 50 | path: locals.outbound_ips_trusted_ci_jenkins_io 51 | scmid: default 52 | 53 | setInfraCiJenkinsIo: 54 | sourceid: getInfraCiJenkinsIo 55 | name: Update infra.ci.jenkins.io outbound IPs 56 | kind: hcl 57 | spec: 58 | file: locals.tf 59 | path: locals.outbound_ips_infra_ci_jenkins_io 60 | scmid: default 61 | 62 | setPrivateVpn: 63 | sourceid: getPrivateVpn 64 | name: Update private.vpn.jenkins.io outbound IPs 65 | kind: hcl 66 | spec: 67 | file: locals.tf 68 | path: locals.outbound_ips_private_vpn_jenkins_io 69 | scmid: default 70 | 71 | actions: 72 | default: 73 | kind: github/pullrequest 74 | scmid: default 75 | title: Update the service IPs 76 | spec: 77 | labels: 78 | - dependencies 79 | -------------------------------------------------------------------------------- /pkg.origin.jenkins.io.tf: -------------------------------------------------------------------------------- 1 | data "aws_instance" "pkg_origin_jenkins_io" { 2 | filter { 3 | name = "tag:Name" 4 | values = ["jenkins-pkg"] 5 | } 6 | } 7 | 8 | resource "aws_network_interface_sg_attachment" "ssh_to_pkg" { 9 | security_group_id = aws_security_group.restricted_ssh.id 10 | network_interface_id = data.aws_instance.pkg_origin_jenkins_io.network_interface_id 11 | } 12 | 13 | resource "aws_network_interface_sg_attachment" "http_to_pkg" { 14 | security_group_id = aws_security_group.unrestricted_http.id 15 | network_interface_id = data.aws_instance.pkg_origin_jenkins_io.network_interface_id 16 | } 17 | 18 | resource "aws_network_interface_sg_attachment" "pkg" { 19 | security_group_id = aws_security_group.pkg.id 20 | network_interface_id = data.aws_instance.pkg_origin_jenkins_io.network_interface_id 21 | } 22 | 23 | resource "aws_security_group" "pkg" { 24 | name = "pkg" 25 | description = "Custom network rules for the pkg VM (pkg.origin.jenkins AND updates.jenkins.io)" 26 | vpc_id = data.aws_vpc.default.id 27 | 28 | tags = local.common_tags 29 | } 30 | 31 | resource "aws_vpc_security_group_egress_rule" "allow_outbound_ssh" { 32 | for_each = toset([ 33 | for ip in flatten(concat( 34 | split(" ", local.inbound_ips_archives_jenkins_io), # Sync to archives.jenkins.io with SSH/Rsync 35 | split(" ", local.inbound_ips_ftp_osl_osuosl_org), # Sync to OSUOSL 36 | )) : ip 37 | if can(cidrnetmask("${ip}/32")) 38 | ]) 39 | 40 | description = "Allow outbound SSH for sync" 41 | security_group_id = aws_security_group.pkg.id 42 | cidr_ipv4 = "${each.value}/32" 43 | from_port = 22 44 | ip_protocol = "tcp" 45 | to_port = 22 46 | } 47 | 48 | resource "aws_vpc_security_group_egress_rule" "allow_outbound_rsync" { 49 | for_each = toset([ 50 | for ip in flatten(concat( 51 | split(" ", local.inbound_ips_archives_jenkins_io), # Sync to archives.jenkins.io with SSH/Rsync 52 | split(" ", local.inbound_ips_ftp_osl_osuosl_org), # Sync to OSUOSL 53 | )) : ip 54 | if can(cidrnetmask("${ip}/32")) 55 | ]) 56 | 57 | description = "Allow outbound SSH for sync" 58 | security_group_id = aws_security_group.pkg.id 59 | cidr_ipv4 = "${each.value}/32" 60 | from_port = 387 61 | ip_protocol = "tcp" 62 | to_port = 387 63 | } 64 | -------------------------------------------------------------------------------- /.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.49.0" 6 | hashes = [ 7 | "h1:BKrMq4aIOvXbJA9fd0kdmIm3Q01MQcheDIEzXtrkNf4=", 8 | "h1:EMzIW40AXkmr5qYv2ynb6ToWO7oRwMNYHwHo20kXAdY=", 9 | "h1:RZtXnBRpO4LNmmz0tXJQLa2heqk9VFGblFZtRCZkm/M=", 10 | "h1:Y3xvYjzBIwYSbcnZDcs6moiy30uxRoY5oT2ExQHKG5A=", 11 | "zh:0979b07cdeffb868ea605e4bbc008adc7cccb5f3ba1d3a0b794ea3e8fff20932", 12 | "zh:2121a0a048a1d9419df69f3561e524b7e8a6b74ba0f57bd8948799f12b6ad3a1", 13 | "zh:573362042ba0bd18e98567a4f45d91b09eb0d223513518ba04f16a646a906403", 14 | "zh:57be7a4d6c362be2fa586d270203f4eac1ee239816239a9503b86ebc8fa1fef0", 15 | "zh:5c72ed211d9234edd70eac9d77c3cafc7bbf819d1c28332a6d77acf227c9a23c", 16 | "zh:7786d1a9781f8e8c0079bf58f4ed4aeddec0caf54ad7ddcf43c47936d545a04f", 17 | "zh:82133e7d39787ee91ed41988da71beecc2ecb900b5da94b3f3d77fbc4d4dc722", 18 | "zh:8cdb1c154dead85be8352afd30eaf41c59249de9e7e0a8eb4ab8e625b90a4922", 19 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 20 | "zh:ac215fd1c3bd647ae38868940651b97a53197688daefcd70b3595c84560e5267", 21 | "zh:c45db22356d20e431639061a72e07da5201f4937c1df6b9f03f32019facf3905", 22 | "zh:c9ba90e62db9a4708ed1a4e094849f88ce9d44c52b49f613b30bb3f7523b8d97", 23 | "zh:d2be3607be2209995c80dc1d66086d527de5d470f73509e813254067e8287106", 24 | "zh:e3fa20090f3cebf3911fc7ef122bd8c0505e3330ab7d541fa945fea861205007", 25 | "zh:ef1b9d5c0b6279323f2ecfc322db8083e141984cfe1bb2f33c0f4934fccb69e3", 26 | ] 27 | } 28 | 29 | provider "registry.terraform.io/hashicorp/local" { 30 | version = "2.5.1" 31 | hashes = [ 32 | "h1:/GAVA/xheGQcbOZEq0qxANOg+KVLCA7Wv8qluxhTjhU=", 33 | "h1:8oTPe2VUL6E2d3OcrvqyjI4Nn/Y/UEQN26WLk5O/B0g=", 34 | "h1:fm2EuMlsdPTuv2tKwx3PMJzWJUh7aMtU9Eky7t4fMys=", 35 | "h1:tjcGlQAFA0kmQ4vKkIPPUC4it1UYxLbg4YvHOWRAJHA=", 36 | "zh:0af29ce2b7b5712319bf6424cb58d13b852bf9a777011a545fac99c7fdcdf561", 37 | "zh:126063ea0d79dad1f68fa4e4d556793c0108ce278034f101d1dbbb2463924561", 38 | "zh:196bfb49086f22fd4db46033e01655b0e5e036a5582d250412cc690fa7995de5", 39 | "zh:37c92ec084d059d37d6cffdb683ccf68e3a5f8d2eb69dd73c8e43ad003ef8d24", 40 | "zh:4269f01a98513651ad66763c16b268f4c2da76cc892ccfd54b401fff6cc11667", 41 | "zh:51904350b9c728f963eef0c28f1d43e73d010333133eb7f30999a8fb6a0cc3d8", 42 | "zh:73a66611359b83d0c3fcba2984610273f7954002febb8a57242bbb86d967b635", 43 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 44 | "zh:7ae387993a92bcc379063229b3cce8af7eaf082dd9306598fcd42352994d2de0", 45 | "zh:9e0f365f807b088646db6e4a8d4b188129d9ebdbcf2568c8ab33bddd1b82c867", 46 | "zh:b5263acbd8ae51c9cbffa79743fbcadcb7908057c87eb22fd9048268056efbc4", 47 | "zh:dfcd88ac5f13c0d04e24be00b686d069b4879cc4add1b7b1a8ae545783d97520", 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /ec2_agents_authorized_keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCl6d/p2IRynzsvGkJKZWxtA7j6wru0DTPMARfPzq0udgGkrVImw8b6wPcD5GNT+g6u5ND08pNpdnslF6V7jS65xo1OXGKhQulVJi5ixcMJCgW2NHwdtr4RPujn5aU5YzsAxfRxfb6KVObbUqAQdjGS4Xyr5IMvzXnkG1xO+UvBRV9EEmN1cqyFeSHO3tD3UfkNsgSWyS+SR3elyXpc24VTi5O9KsvDHtP8DknGf90JydPiCLzK8ZJj7l9SxYWrsFL/w2gxc3pA819+i9kxr5ZK7RGd386xSDeAu7z0761C0WiYvN9pmOU8VfYdMQu7w1KVmqA77ybcfBLyQ+2TMsoPSlPOAS7NsoNlIqvE4+EkAtZhBNJfK2em9nzjYZLSuoPGty4NabCPgmTnXwAFwXSjMpwDrOpRuxjW9fXbxFT3LjGq2jU3Kjmh+2hoRvX1Xfrtk5TWgKm2t8CQeriLBi7aoLiRdPuDSuQUP2/Ucp5htEXkInB5cp/L1OUu6NrGiPDObG91BZA+u8HncuRFYgOVr3gjvzwO80HzEUQBjeaJo9Eop4bRe4jCxnBbpbYCIKA64G4boQ74ltSgcmR5cpsWDA8mFoHmzkB+ynDXSUJTOCC3SvLKtiHGF9KxErs8GfhteSx2WHbit9mb6+WWT68f4T5M3Ksx8tyU6OmHr1uENw== jenkins-infra@jenkins.io # ci.jenkins.io 2 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDYlEZmGs43NHtJysZpSJX/THB2JBcCP0o+8ijJVAADhzuRYE5Jdd+dwdCWyGzGc8r2pxdw+1l3NHHkhRqTWIgH9h77VLnavrqDf6dTuC05q4RTBw5E6AknKB3L0a4IzpXtvT+MbdkkJaNEFWpK63oGSxbO2kaTUXzYiJg/BnpMo8gKyeF1t1RBZ249xgWqmb4SDkM7hOLC7HF8rJjHXNFvhBAm99aWwr0MhFwmEp6fRAiI5AC52owIewXN9dkXs8oMXjQ4J6g7dO5Vwdu2M19Uk0DbaqEJmngoSYtOikpkFSlnJO3iOqYUXh6uEZLd1OjZbxbcvnmxMlI6UfKkVaKGWzLb6G8yz+Ahz9oxKmq11QsUoMedHZlwHNgPpJ+TS3p5TlDZYGsuVRm7EnMN0+KecXzoZk6i/kJO+uQ0oMxdtZCYVlwdTiOW/85WqB7lSVokIWAkRmV89kHEbgSJLgrH9aXr4xw2jhzN+F9HT2Z5mOoVU3Exkursu/bXhdocIhdtXQVBDIA0hcvouUGar6Db7VK6NQeJoAWUVVELhBZP+rGhbg5WjGZANPPi01VMIDhks5KXZt0AtaQxpZCaynbasm0g7fHvy+BP3bTQcpa95hBWYL0tjon5BPLTkzK4q282pQluba0bralLDgF0SoNw4F83w/hu7YwHMoSC9Njzcw== jenkins-infra@jenkins.io # infra.ci.jenkins.io 3 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyqFXgoSBl7jmQp56FK3d/qocnNvot/R9dF0UwfmTNJ1DkdA9vVYcXwz9M72K+rkT5vkd+VSsNjTBcfhynxeX69yX7j+Ao9nmd7ImaIyhgI3NVjE8F6VBr9IAlvt/+IY/nLP9clCRAzA5T5QCHFlZQW9bnHJ9DmTfICFGlwRaY/t58c3gz7oLOLFejeAqz0PHdwhieWJgfYQP3rPDXttPxlPw0mkIDwHQfLJV/HOFAbfAd7v/zF5VoSPTGoVHerwKoRQital2+6sY0j5LQUO1kqLaNQi1sh04QweIxZeti8+Q4qn0hOdJlbmaekT7jtG1j/D1omohDOSCjYSiiffaaDUZKW1waL03aHnc5ptNKAv7GE7fbgubYHCu61IuXAXQG5XOcbbeDk55UlzP4G8/nTF1xuD+yXhXM2aQVd6nGlHFpGgVnctWfVPPxTE6OZ3FRY7zfC13TF9Irh+xENkql7TF5j9ho3yo9o4xpgmMNvgkvYiAO5ypy74aMSbJ/vkjvVM1y88rKgVVdWWDW9EojAobCP7mPfYMEnM57hHDKrzYLNZgbU8Rgjpt2X8uIb+D+kRZ7IVFAadE0A+7/JzfWyHxNk6b5gXxJKRVOg6TYn979y3b1qzObYqaa10no1YtzVt5OKWNdfnCow4u4ovsrIZuOxELhUD6A9LBhyP9ELw== jenkins-infra@jenkins.io # release.ci.jenkins.io 4 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDJBYCE0febAONYx1LhC4YchyV1k+Icte8kg0wKSkjB3lEUTCTzPG5/oUrbGj3rkFeDRSXeVCjTpjk45LeZ2+NWjQrHDv9GZcN95K/JDYDhFPizz2nFmwajbIb+0xa37Vy44+x4I94F+Blnba0xA3uILGI3w5FJZAW+m+h+T0SoInGiRJp1DFYi8RQO+G1bEf0+ndyYBFnuLJRQyMAK21JMtA2aADoV37lzqxxgGfCtup2BITZX4bPipG35t1Fplagj5CsMAMqDxgXk2dsCKAQxEtsWSlRi1K9+x3SXubORg3Z+Xs26YZYb5N9K4dQj2FnNFpSJ7kZNj3A3cPv1MT8+632WfxgFkgbSQNP0+KutAFr6EZAh49WxoL/gvwU3OD4FL2J4SQ3XRx1zSkOtbzR6Z63fSM/feptWtoF6Jv6e6w+zIsjzvEalLhdl1ngEiXIJ/V5xneCc8epBQoOCjs9vFwPHwA4z/2iDS7CYsrL8uX1arf3hMI2b+FYWoITUpWYi4rOv4y9zkwHfPOJizUHrc/jwtFHrJ3JtHhVqGDnj/mcdnlwGIja7uQ0XlQKF1+kwRpWOPrRJoFgZ/j+RJkUZ+u8YSVZ7KwHI2YlxbBQiLMoFy6bh8XP/nooV+mo1uQDoZsc6mSz+kiKL2Rg/DuyhFumoSkY0WeBnyiGn4//ecw== jenkins-infra@jenkins.io # trusted.ci.jenkins.io 5 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Jenkins Infra on AWS 2 | :toc: 3 | :private_repo_name: terraform-states 4 | :private_repo_url: https://github.com/jenkins-infra/{private_repo_name} 5 | 6 | This repository hosts the infrastructure-as-code definition for all the link:https://aws.amazon.com/[Amazon Web Services (AWS)-hosted] resources for the link:https://www.jenkins.io/projects/infrastructure/[Jenkins Infrastructure Project]. 7 | 8 | == Requirements 9 | 10 | * An AWS account with the ability to assume the role `infra-admin` on the AWS account used for the Jenkins infrastructure 11 | * The requirements (of the shared tools) listed at link:https://github.com/jenkins-infra/shared-tools/tree/main/terraform#requirements[shared-tools/terraform#requirements] 12 | * The link:https://www.terraform.io/docs/language/settings/backends/s3.html[Terraform S3 Backend Configuration] on a local file named `backend-config`: 13 | ** The content can be retrieved from the outputs of the link:{private_repo_url}[(private) repository {private_repo_name}] 14 | ** This file (`backend-config`) is git-ignored 15 | 16 | * The git command line to allow cloning the repository and its submodule link:https://github.com/jenkins-infra/shared-tools[shared-tools] 17 | ** This repository has submodules. Once you cloned the repository, execute the following command to obtain the shared tools: 18 | 19 | [source,bash] 20 | ---- 21 | git submodule update --init --recursive 22 | ---- 23 | 24 | == HowTo 25 | 26 | IMPORTANT: Don't blindly execute the terraform code located in this repository on your own account as it may lead your account bill to significantly increase. 27 | 28 | Once you've fulfilled the <>, you may execute any command from https://github.com/jenkins-infra/shared-tools/blob/main/terraform/README.adoc#available-commands by adding the correct flag `--directory` pointing to `.shared-tools/terraform/`: 29 | 30 | [source,bash] 31 | ---- 32 | make --directory=.shared-tools/terraform help 33 | make --directory=.shared-tools/terraform lint 34 | # ... 35 | ---- 36 | 37 | 38 | A usual change to this repository looks like the following: 39 | 40 | * Fork the repository and clone it locally 41 | * Follow the <> steps to obtain the shared tools 42 | * Start by running a full `make --directory=.shared-tools/terraform validate` command to ensure that you work on a sane base (should generate a report TXT file with no changes to be applied) 43 | * Edit the Terraform project files 44 | * Run the command `make --directory=.shared-tools/terraform validate` again to ensure that your changes are OK 45 | * Commit, push and open a pull request to let the Jenkins pipeline run the test + plan (as per https://github.com/jenkins-infra/shared-tools/blob/main/terraform/README.adoc#jenkins-pipeline) 46 | 47 | == Troubleshoot 48 | 49 | == IAM: User Not Authorized 50 | 51 | Sometimes, the CI users are missing an authorization on a resource. You would see a message like the following: 52 | 53 | [source] 54 | ---- 55 | Error: error updating tags for IAM Policy (arn:aws:iam::XXXXXXXXXXX:policy/jenkins-YYYYYYYYYY): error tagging resource (arn:aws:iam::XXXXXXXXXXX:policy/jenkins-YYYYYYYYYY): AccessDenied: User: arn:aws:iam::ZZZZZZZZZZZZZ:user/production-terraform is not authorized to perform: XXXX:Yyyyyyy on resource: policy arn:aws:iam::XXXXXXXXXXX:policy/jenkins-YYYYYYYYYY 56 | status code: 403, request id: 57 | ---- 58 | 59 | To solve this issues, you have to update the IAM policies for the technical user, found in the link:{private_repo_url}[(private) repository {private_repo_name}]. 60 | -------------------------------------------------------------------------------- /network.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc" "default" { 2 | id = "vpc-70a4b915" 3 | } 4 | 5 | resource "aws_security_group" "restricted_ssh" { 6 | name = "restricted-ssh" 7 | description = "Allow inbound SSH only from trusted sources (admins or VPN)" 8 | vpc_id = data.aws_vpc.default.id 9 | 10 | tags = local.common_tags 11 | } 12 | 13 | resource "aws_security_group" "unrestricted_http" { 14 | name = "unrestricted-http" 15 | description = "Allow HTTP(S) from everywhere (public services)" 16 | vpc_id = data.aws_vpc.default.id 17 | 18 | tags = local.common_tags 19 | } 20 | 21 | resource "aws_vpc_security_group_ingress_rule" "allow_ssh_from_admins" { 22 | for_each = toset([ 23 | for ip in flatten(concat( 24 | split(" ", local.outbound_ips_trusted_ci_jenkins_io), # trusted.ci.jenkins.io (controller and all agents) for rsync data transfer 25 | split(" ", local.outbound_ips_infra_ci_jenkins_io), # infra.ci.jenkins.io (controller and all agents) for SSH management 26 | split(" ", local.outbound_ips_private_vpn_jenkins_io), # connections routed through the VPN 27 | )) : ip 28 | if can(cidrnetmask("${ip}/32")) 29 | ]) 30 | 31 | description = "Allow admin (or platform) IPv4 for inbound SSH" 32 | security_group_id = aws_security_group.restricted_ssh.id 33 | cidr_ipv4 = "${each.value}/32" 34 | from_port = 22 35 | ip_protocol = "tcp" 36 | to_port = 22 37 | } 38 | 39 | ## We WANT inbound from everywhere 40 | #trivy:ignore:avd-aws-0107 41 | resource "aws_vpc_security_group_ingress_rule" "allow_http_from_internet" { 42 | description = "Allow HTTP from everywhere (public Internet)" 43 | security_group_id = aws_security_group.unrestricted_http.id 44 | cidr_ipv4 = "0.0.0.0/0" 45 | from_port = 80 46 | ip_protocol = "tcp" 47 | to_port = 80 48 | } 49 | 50 | ## We WANT inbound from everywhere 51 | #trivy:ignore:avd-aws-0107 52 | resource "aws_vpc_security_group_ingress_rule" "allow_https_from_internet" { 53 | description = "Allow HTTP from everywhere (public Internet)" 54 | security_group_id = aws_security_group.unrestricted_http.id 55 | cidr_ipv4 = "0.0.0.0/0" 56 | from_port = 443 57 | ip_protocol = "tcp" 58 | to_port = 443 59 | } 60 | 61 | ## We WANT egress to internet (APT at least, but also outbound azcopy on some machines) 62 | #trivy:ignore:avd-aws-0104 63 | resource "aws_vpc_security_group_egress_rule" "allow_http_to_internet" { 64 | description = "Allow HTTP to everywhere (public Internet)" 65 | security_group_id = aws_security_group.unrestricted_http.id 66 | 67 | cidr_ipv4 = "0.0.0.0/0" 68 | from_port = 80 69 | ip_protocol = "tcp" 70 | to_port = 80 71 | } 72 | 73 | ## We WANT egress to internet (APT at least, but also outbound azcopy on some machines) 74 | #trivy:ignore:avd-aws-0104 75 | resource "aws_vpc_security_group_egress_rule" "allow_https_to_internet" { 76 | description = "Allow HTTPS to everywhere (public Internet)" 77 | security_group_id = aws_security_group.unrestricted_http.id 78 | 79 | cidr_ipv4 = "0.0.0.0/0" 80 | from_port = 443 81 | ip_protocol = "tcp" 82 | to_port = 443 83 | } 84 | 85 | ## We WANT egress to internet (rsync cases) 86 | #trivy:ignore:avd-aws-0104 87 | resource "aws_vpc_security_group_egress_rule" "allow_rsync_to_internet" { 88 | description = "Allow rsync to everywhere (public Internet)" 89 | security_group_id = aws_security_group.unrestricted_http.id 90 | 91 | cidr_ipv4 = "0.0.0.0/0" 92 | from_port = 873 93 | ip_protocol = "tcp" 94 | to_port = 873 95 | } 96 | 97 | resource "aws_vpc_security_group_egress_rule" "allow_puppet_to_puppetmaster" { 98 | description = "Allow Puppet protocol to the Puppet master" 99 | security_group_id = aws_security_group.unrestricted_http.id 100 | 101 | # Ref. https://github.com/jenkins-infra/azure/blob/main/puppet.jenkins.io.tf 102 | # TODO: automate retrieval of this IP with updatecli 103 | cidr_ipv4 = "20.12.27.65/32" 104 | from_port = 8140 105 | ip_protocol = "tcp" 106 | to_port = 8140 107 | } 108 | --------------------------------------------------------------------------------