用Terraform创建lambda运行python定期自动更新AWS安全组

用Terraform创建lambda运行python定期自动更新AWS安全组

背景

基础设施即代码IaC,是一种更为优雅的管理云上基础设施的方式。

我这里使用开源的terraform,创建和管理AWS中的serverless运行代码的服务lambda,运行python脚本,获取最新的Cloudflare IP网段,并更新到目标安全组中;

然后通过CloudWatch的EventBridge,定期的触发该lambda。

这个过程中,还需要通过terraform创建必要的IAM role和IAM policy。

操作步骤

准备步骤

确保当前环境已安装terraform,并创建指定文件夹并进入,然后创建main.tf文件并准备写入配置文件。

mkdir terraform-experiment3
cd terraform-experiment3
vim main.tf

创建lambda

在main.tf中,通过如下代码创建lambda

provider "aws" {
  # access_key and secret_key can be excluded if you
  # have your creds setup in ~/.aws
  access_key = "AKIA********NU5"
  secret_key = "leVIwk********************dwx/a"
  region = "ap-east-1"
}

# 创建名为"terraform-managed-lambda_AutoUpdateSG4Cloudflare"的lambda
resource "aws_lambda_function" "terraform_lambda_AutoUpdateSG4Cloudflare" {
  function_name = "terraform-managed-lambda_AutoUpdateSG4Cloudflare"

  filename = "cf-security-group-update.zip"
  handler = "cf-security-group-update.lambda_handler"
  role    = "${aws_iam_role.iam_lambda_AutoUpdateSG4Cloudflare.arn}"
  runtime = "python3.9"
  timeout ="15"
  environment {
    variables = {
      PORTS_LIST = "443"
      SECURITY_GROUP_ID = "sg-06dc9f9cfd836b97f"
	  UPDATE_IPV6 = "0"
    }
  }  
}

这里cf-security-group-update.zip来自https://github.com/johnmccuk/cloudflare-ip-security-group-update/blob/master/cf-security-group-update.py 压缩为zip文件,并上传至当前terraform运行服务器的terraform-experiment3文件夹下。

环境变量中的SECURITY_GROUP_ID 是已经创建好的安全组,这部分安全组的创建尚未纳入terraform管理,等其他功能都验证完毕后,之后可以考虑用terraform创建该安全组。

handler = “cf-security-group-update.lambda_handler” 代表cf-security-group-update这个文件下的lambda_handler为该lambda函数的入口点,它告诉 Lambda 在哪里开始执行代码。

handler 这部分完成配置后,在AWS控制台对应配置位置如下。

创建IAM执行角色

在main.tf中,通过如下代码创建名为”iam_lambda_AutoUpdateSG4Cloudflare”的IAM执行角色,并创建信任关系

#创建名为"iam_lambda_AutoUpdateSG4Cloudflare"的IAM执行角色,并创建信任关系
resource "aws_iam_role" "iam_lambda_AutoUpdateSG4Cloudflare" {
  name = "iam_lambda_AutoUpdateSG4Cloudflare"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

上述指令生成的内容,在AWS控制台如下所示。

创建IAM Policy策略

在main.tf中,通过如下代码创建两个policy:

  • policy1,添加更新安全组必要的权限,并绑定到当前IAM iam_lambda_AutoUpdateSG4Cloudflare上;
  • policy2,添加lambda执行的基本权限,并绑定到当前IAM iam_lambda_AutoUpdateSG4Cloudflare上;
# 创建policy1,添加更新安全组必要的权限,并绑定到当前IAM iam_lambda_AutoUpdateSG4Cloudflare上;
data "aws_iam_policy_document" "lambda_policy_1" {
  statement {
    effect = "Allow"

    actions = [
      "ec2:AuthorizeSecurityGroupIngress",
      "ec2:RevokeSecurityGroupIngress",
      "ec2:DescribeSecurityGroups",
      "s3:GetBucketPolicyStatus",
      "s3:PutBucketPolicy",
      "s3:GetBucketPolicy"
    ]

    resources = [
      "*"
    ]
  }
}

resource "aws_iam_policy" "lambda_policy_1" {
  name        = "Policy-AutoUpdateSG4Cloudflare"
  description = "Policy for Lambda execution"
  policy      = data.aws_iam_policy_document.lambda_policy_1.json
}

resource "aws_iam_role_policy_attachment" "Policy-AutoUpdateSG4Cloudflare" {
  role       = aws_iam_role.iam_lambda_AutoUpdateSG4Cloudflare.name
  policy_arn = aws_iam_policy.lambda_policy_1.arn
}



# 创建policy2,添加lambda执行的基本权限,并绑定到当前IAM iam_lambda_AutoUpdateSG4Cloudflare上;
data "aws_iam_policy_document" "lambda_policy_2" {
  statement {
    effect = "Allow"

    actions = [
      "logs:CreateLogGroup"
    ]

    resources = [
      "arn:aws:logs:*"
    ]
  }

  statement {
    effect = "Allow"

    actions = [
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ]

    resources = [
      "arn:aws:logs:*"
    ]
  }
}

resource "aws_iam_policy" "lambda_policy_2" {
  name        = "Policy-AWSLambdaBasicExecutionRole"
  description = "Policy for Lambda execution"
  policy      = data.aws_iam_policy_document.lambda_policy_2.json
}

resource "aws_iam_role_policy_attachment" "Policy-AWSLambdaBasicExecutionRole" {
  role       = aws_iam_role.iam_lambda_AutoUpdateSG4Cloudflare.name
  policy_arn = aws_iam_policy.lambda_policy_2.arn
}

创建后,AWS控制台可见配置如下图。

创建lambda触发器

这部分配置,我参考了文章https://www.codenong.com/44287186/。

在main.tf中,通过如下代码创建触发lambda的触发器,用Cloudwatch的EventBridge周期性触发lambda运行。

这部分代码还配置了Cloudwatch Events运行Lambda所必需的权限。

#创建触发lambda的触发器,用Cloudwatch的EventBridge周期性触发lambda运行
resource"aws_cloudwatch_event_rule""EventBridge_Rule_1minOR1day" {
  name                ="Rule-CloudwatchEventTriggerLambda_1minOR1day"
  description         ="desc"
  schedule_expression ="cron(* * * * ? *)"
  # 每分钟一次 "cron(* * * * ? *)"
  # 每小时一次 "cron(0 * * * ? *)"
  # 每天一次 "cron(0 0 * * ? *)"
}

resource"aws_cloudwatch_event_target""daily_target" {
  rule  ="${aws_cloudwatch_event_rule.EventBridge_Rule_1minOR1day.name}"
  arn   ="${aws_lambda_function.terraform_lambda_AutoUpdateSG4Cloudflare.arn}"
}

data"aws_caller_identity""current" {
 # account_id = "118045904350"
}

resource"aws_lambda_permission""allow_cloudwatch" {
  statement_id   ="AllowExecutionFromCloudWatch"
  action         ="lambda:InvokeFunction"
  function_name  ="${aws_lambda_function.terraform_lambda_AutoUpdateSG4Cloudflare.function_name}"
  principal      ="events.amazonaws.com"
  source_account ="${data.aws_caller_identity.current.account_id}"
  source_arn     ="${aws_cloudwatch_event_rule.EventBridge_Rule_1minOR1day.arn}"
}

上述代码中,account_id不需要自己填写,terraform会自己读取账号参数,所以我这里注释掉了。

schedule_expression 的语法可参考如下链接

https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html

我这里添加了每分钟、每小时和每天的语法注释,可按需使用。

用terraform创建资源

上述所有代码都写入main.tf文件中后,就可以运行

terraform init
terraform apply

来创建必要的资源了。

运行printout截图如下。

功能验证

完成配置后,可登陆AWS控制台查看lambda等相关资源是否都正常创建了。

然后进入目标安全组,删除所有当前入站规则,等待1分钟后(当前测试环境,我配置的触发频率为1分钟一次),可以刷新页面,确认安全组策略可自动添加上。

还可到lambda页面的监控页中,点击“查看CloudWatch Logs”,查看相关log。

通过上述验证,可见自动更新安全组的功能,可以正常实现。

总结

通过上面的配置,我们成功实现了,通过terraform的声明式的方式,创建了lambda函数,并创建了必要的IAM role和policy,以及lambda的trigger,从而实现:

定期触发lambda,运行python脚本,获取最新的Cloudflare IP网段,并更新到目标安全组中。

后续计划

上面的配置中,安全组的创建是手动完成的,还没有纳入terraform的管理。

这部分等未来有时间,可以把安全组的创建也纳入terraform管理。

2023年11月23日22:06:41更新,完成了上述的后续计划。

在main.tf中,通过如下代码创建名为”SG-Cloudflare-AutoUpdate-terraform”的安全组,并设置出站规则为全部放通。

# 创建名为"SG-Cloudflare-AutoUpdate-terraform"的安全组
resource "aws_security_group" "sg-terraform" {
  name        = "SG-Cloudflare-AutoUpdate-terraform"
  description = "SG-Cloudflare-AutoUpdate managed by terraform"

  # 允许所有出站流量
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

然后把之前代码中 SECURITY_GROUP_ID 的部分,改为

SECURITY_GROUP_ID = aws_security_group.sg-terraform.id

即可完成:

新建安全组并纳入terraform管理;

通过上述lambda代码更新该安全组的入站规则。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注