Terraform创建EC2后修改安全组规则:关闭出站流量求助
问题描述
使用Terraform创建EC2实例,通过cloud-init完成Docker、Vim、Git等软件安装后,需要停止实例的出站流量,但当前无法覆盖原有安全组,添加新安全组也未生效,相关代码如下:
主配置文件
module "airgap_instance" { source = "../../../tf-modules/ec2" instance_type = "t3.medium" instance_name = "test_instance" sg = module.airgap_secuirtygroups.sg_id user_data = data.cloudinit_config.cloudconfig.rendered } data "cloudinit_config" "cloudconfig" { gzip = false base64_encode = true part { filename = "install.sh" content_type = "text/x-shellscript" content = file("${path.module}/scripts/install.sh") } } resource "null_resource" "wait_for_instance" { depends_on = [ module.airgap_instance.ec2_id ] triggers = { always_run = "${timestamp()}" } provisioner "local-exec" { command = "${path.module}/scripts/dep_wait.sh ${module.airgap_instance.ec2_id}" } } resource "aws_network_interface_sg_attachment" "sg_init_attachment" { security_group_id = module.airgap_secuirtygroups.sg_id network_interface_id = module.airgap_instance.interface_id } resource "aws_network_interface_sg_attachment" "sg_final_attachment" { security_group_id = module.airgap_secuirtygroups_update.sg_id network_interface_id = module.airgap_instance.interface_id depends_on = [ null_resource.wait_for_instance ] }
security_groups.tf
module "airgap_secuirtygroups" { source = "../../../tf-modules/secuirtygroups" sg_name = "terraform-airgap-init-sg" sg_description = "terraform-airgap-init-sg-description" ingress_ports = [80, 443] egress_ports = [0] } module "airgap_secuirtygroups_update" { source = "../../../tf-modules/secuirtygroups" sg_name = "terraform-airgap-sg" sg_description = "terraform-airgap-sg-description" ingress_ports = [80, 443] egress_ports = [] }
EC2模块(ec2.tf)
resource "aws_instance" "ec2_instance" { ami = var.ami_id instance_type = var.instance_type key_name = var.key_pair_name iam_instance_profile = var.instance_profile availability_zone = var.availability_zone subnet_id = var.subnet_id security_groups = ["${var.sg}"] user_data = var.user_data tags = { Name = "${var.instance_name}" } metadata_options { http_endpoint = "enabled" http_tokens = "required" } }
安全组模块(secuirty_groups.tf)
resource "aws_security_group" "allow_traffic" { name = var.sg_name description = var.sg_description vpc_id = var.sg_vpc_id lifecycle { create_before_destroy = true } dynamic "ingress" { iterator = port for_each = var.ingress_ports content { from_port = port.value to_port = port.value protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } dynamic "egress" { iterator = port for_each = var.egress_ports content { from_port = port.value to_port = port.value protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } tags = { Name = "${var.sg_name}" } }
解决方案
1. 修复安全组替换逻辑
当前使用aws_network_interface_sg_attachment是添加安全组而非替换,最终实例会同时挂载两个安全组,出站流量仍被允许。同时EC2模块中已指定security_groups,会和后续的attachment操作产生状态冲突。
替换方案:移除两个aws_network_interface_sg_attachment资源,改用local-exec调用AWS CLI直接替换实例的安全组:
resource "null_resource" "replace_sg" { depends_on = [null_resource.wait_for_instance] provisioner "local-exec" { command = <<EOT aws ec2 modify-instance-attribute --instance-id ${module.airgap_instance.ec2_id} --groups ${module.airgap_secuirtygroups_update.sg_id} EOT } }
同时在EC2模块的aws_instance资源中添加生命周期规则,忽略安全组的变更,避免Terraform后续计划中恢复原有安全组:
resource "aws_instance" "ec2_instance" { # ... 原有配置 ... # VPC实例优先使用vpc_security_group_ids而非security_groups vpc_security_group_ids = [var.sg] lifecycle { ignore_changes = [vpc_security_group_ids] } }
2. 修正无出站规则的安全组配置
AWS安全组默认包含一条允许所有出站流量的规则,当egress_ports = []时,动态块不会生成任何规则,但默认规则仍会生效,导致出站流量未被禁止。
修改安全组模块,使用aws_security_group_rule单独管理出站规则,确保默认规则被覆盖:
# 安全组模块中添加 resource "aws_security_group_rule" "deny_all_egress" { count = length(var.egress_ports) == 0 ? 1 : 0 type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.allow_traffic.id } # 同时在aws_security_group资源中添加生命周期规则忽略默认出站规则 resource "aws_security_group" "allow_traffic" { # ... 原有配置 ... lifecycle { create_before_destroy = true ignore_changes = [egress] } }
3. 确保cloud-init真正完成
当前dep_wait.sh仅等待实例启动,未验证cloud-init是否完成软件安装。修改脚本,通过SSM检查cloud-init状态(需实例拥有SSM权限):
# dep_wait.sh内容 INSTANCE_ID=$1 aws ssm send-command --instance-ids $INSTANCE_ID --document-name "AWS-RunShellScript" --parameters commands="cloud-init status --wait" --output text > /dev/null
内容的提问来源于stack exchange,提问作者user3398900
相关产品推荐
相关产品推荐

