From 43946a29996adc72394cab538dc72cb81401e308 Mon Sep 17 00:00:00 2001 From: Matt Trachier Date: Fri, 8 Mar 2024 09:40:14 -0600 Subject: [PATCH] feat: add ability to skip specific ip sg Signed-off-by: Matt Trachier --- README.md | 10 ++++++++++ examples/skipip/main.tf | 26 ++++++++++++++++++++++++++ examples/skipip/outputs.tf | 15 +++++++++++++++ examples/skipip/variables.tf | 3 +++ examples/skipip/versions.tf | 17 +++++++++++++++++ main.tf | 27 ++++++++++++++------------- modules/security_group/main.tf | 24 +++++++++++++----------- modules/security_group/variables.tf | 4 ++++ tests/skip_test.go | 17 +++++++++++++++++ variables.tf | 5 +++++ 10 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 examples/skipip/main.tf create mode 100644 examples/skipip/outputs.tf create mode 100644 examples/skipip/variables.tf create mode 100644 examples/skipip/versions.tf diff --git a/README.md b/README.md index 55ec536..c158514 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,21 @@ # Terraform AWS Access +## Recent Changes + +- Skip Runner IP + + By default this module will create a security group which allows the ip of the client running terraform ingress and egress access. + This enables users to configure servers after they are created, but is not always wanted. + The new variable "skip_runner_ip" will prevent the creation of that security group, further securing the project. + This functionality enables air-gapped production projects. + ## AWS Access The first step to using the AWS modules is having an AWS account, [here](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-creating.html) is a document describing this process. You will need an API access key id and API secret key, you can get the API keys [following this tutorial](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey). The Terraform AWS provider uses the AWS Go SDK, which allows the use of either environment variables or config files for authentication. https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-settings + You do not need the AWS cli to generate the files, just place them in the proper place and Terraform will find and read them. We use environment variables to configure the AWS provider and load them by sourcing an RC file. diff --git a/examples/skipip/main.tf b/examples/skipip/main.tf new file mode 100644 index 0000000..6e86a13 --- /dev/null +++ b/examples/skipip/main.tf @@ -0,0 +1,26 @@ +provider "aws" { + default_tags { + tags = { + Id = local.identifier + } + } +} + +locals { + identifier = var.identifier + name = "tf-skipip-${local.identifier}" + +} + +module "this" { + source = "../../" + owner = "terraform-ci@suse.com" + vpc_name = local.name + vpc_cidr = "10.0.255.0/24" # gives 256 usable addresses from .1 to .254, but AWS reserves .1 to .4 and .255, leaving .5 to .254 + subnet_name = local.name + subnet_cidr = "10.0.255.224/28" # gives 14 usable addresses from .225 to .238, but AWS reserves .225 to .227 and .238, leaving .227 to .237 + security_group_name = local.name + security_group_type = "internal" # air gapped, internal only and no holes + skip_runner_ip = true # air gapped, internal only and no holes + skip_ssh = true +} diff --git a/examples/skipip/outputs.tf b/examples/skipip/outputs.tf new file mode 100644 index 0000000..a8a8e2e --- /dev/null +++ b/examples/skipip/outputs.tf @@ -0,0 +1,15 @@ +output "vpc" { + value = module.this.vpc +} + +output "subnet" { + value = module.this.subnet +} + +output "security_group" { + value = module.this.security_group +} + +output "ssh_key" { + value = module.this.ssh_key +} diff --git a/examples/skipip/variables.tf b/examples/skipip/variables.tf new file mode 100644 index 0000000..e960ce9 --- /dev/null +++ b/examples/skipip/variables.tf @@ -0,0 +1,3 @@ +variable "identifier" { + type = string +} diff --git a/examples/skipip/versions.tf b/examples/skipip/versions.tf new file mode 100644 index 0000000..7c63b5d --- /dev/null +++ b/examples/skipip/versions.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 1.5.0, < 1.6" + required_providers { + local = { + source = "hashicorp/local" + version = ">= 2.4" + } + aws = { + source = "hashicorp/aws" + version = ">= 5.11" + } + http = { + source = "hashicorp/http" + version = ">= 3.4" + } + } +} \ No newline at end of file diff --git a/main.tf b/main.tf index 257e001..b9de076 100644 --- a/main.tf +++ b/main.tf @@ -18,10 +18,10 @@ locals { ipinfo_ip = chomp(can(data.http.my_public_ip[0].response_body) ? data.http.my_public_ip[0].response_body : "127.0.0.1") ip = (local.security_group_ip == "" ? local.ipinfo_ip : local.security_group_ip) skip_security_group = var.skip_security_group # no objects in this module depend on security group being created, skip if wanted - - ssh_key_name = var.ssh_key_name - public_ssh_key = var.public_ssh_key # create when public key is given, otherwise select with name - skip_ssh = var.skip_ssh # no objects in this module depend on ssh key being created, skip if wanted + skip_runner_ip = var.skip_runner_ip + ssh_key_name = var.ssh_key_name + public_ssh_key = var.public_ssh_key # create when public key is given, otherwise select with name + skip_ssh = var.skip_ssh # no objects in this module depend on ssh key being created, skip if wanted } data "http" "my_public_ip" { @@ -48,15 +48,16 @@ module "subnet" { } module "security_group" { - count = (local.skip_security_group ? 0 : 1) - source = "./modules/security_group" - name = local.security_group_name - ip = local.ip - cidr = (can(module.subnet[0].cidr) ? module.subnet[0].cidr : "") - owner = local.owner - type = local.security_group_type - vpc_id = module.vpc[0].id - vpc_cidr = module.vpc[0].vpc.cidr_block + count = (local.skip_security_group ? 0 : 1) + source = "./modules/security_group" + name = local.security_group_name + ip = local.ip + cidr = (can(module.subnet[0].cidr) ? module.subnet[0].cidr : "") + owner = local.owner + type = local.security_group_type + vpc_id = module.vpc[0].id + vpc_cidr = module.vpc[0].vpc.cidr_block + skip_runner_ip = local.skip_runner_ip } module "ssh_key" { diff --git a/modules/security_group/main.tf b/modules/security_group/main.tf index 29109f1..88ffffe 100644 --- a/modules/security_group/main.tf +++ b/modules/security_group/main.tf @@ -1,13 +1,15 @@ locals { - name = var.name - select = (var.type == "" ? 1 : 0) # select if no type given - create = (var.type != "" ? 1 : 0) # create if given a type - type = (local.types[(var.type == "" ? "none" : var.type)]) - owner = var.owner - ip = chomp(var.ip) - cidr = var.cidr - vpc_id = var.vpc_id - vpc_cidr = var.vpc_cidr + name = var.name + select = (var.type == "" ? 1 : 0) # select if no type given + create = (var.type != "" ? 1 : 0) # create if given a type + type = (local.types[(var.type == "" ? "none" : var.type)]) + owner = var.owner + ip = chomp(var.ip) + cidr = var.cidr + vpc_id = var.vpc_id + vpc_cidr = var.vpc_cidr + skip_runner_ip = var.skip_runner_ip + allow_runner = (local.skip_runner_ip ? false : true) # opposite of skip_runner_ip } data "aws_security_group" "selected" { @@ -37,14 +39,14 @@ resource "aws_security_group" "new" { # this rule allows ingress on any port from the ip specified resource "aws_vpc_security_group_ingress_rule" "from_ip" { - count = (local.type.specific_ip_ingress ? 1 : 0) + count = (local.type.specific_ip_ingress && local.allow_runner ? 1 : 0) ip_protocol = "-1" cidr_ipv4 = "${local.ip}/32" security_group_id = aws_security_group.new[0].id } # this rule allows egress on any port to the ip specified resource "aws_vpc_security_group_egress_rule" "to_ip" { - count = (local.type.specific_ip_egress ? 1 : 0) + count = (local.type.specific_ip_egress && local.allow_runner ? 1 : 0) ip_protocol = "-1" cidr_ipv4 = "${local.ip}/32" security_group_id = aws_security_group.new[0].id diff --git a/modules/security_group/variables.tf b/modules/security_group/variables.tf index 8fd28e1..93ee960 100644 --- a/modules/security_group/variables.tf +++ b/modules/security_group/variables.tf @@ -56,4 +56,8 @@ variable "vpc_cidr" { Not necessary if the security group is being found. EOT default = "" +} +variable "skip_runner_ip" { + type = bool + description = "Skip generating ingress security group for the runner's ip" } \ No newline at end of file diff --git a/tests/skip_test.go b/tests/skip_test.go index 2699574..88c69bb 100644 --- a/tests/skip_test.go +++ b/tests/skip_test.go @@ -91,3 +91,20 @@ func TestSkipSsh(t *testing.T) { defer terraform.Destroy(t, terraformOptions) terraform.InitAndApply(t, terraformOptions) } +func TestSkipIp(t *testing.T) { + t.Parallel() + uniqueID := os.Getenv("IDENTIFIER") + if uniqueID == "" { + uniqueID = random.UniqueId() + } + directory := "skipip" + region := "us-west-1" + + terraformVars := map[string]interface{}{ + "identifier": uniqueID, + } + terraformOptions := setup(t, directory, region, terraformVars) + defer teardown(t, directory) + defer terraform.Destroy(t, terraformOptions) + terraform.InitAndApply(t, terraformOptions) +} diff --git a/variables.tf b/variables.tf index e3e9bd1..77824a1 100644 --- a/variables.tf +++ b/variables.tf @@ -125,6 +125,11 @@ variable "skip_security_group" { description = "Skip security group generation, use with care." default = false } +variable "skip_runner_ip" { + type = bool + description = "Skip generating ingress security group for the runner's ip" + default = false +} # ssh key variable "ssh_key_name" {