Terraform API 网关多区域部署:拒绝访问

Terraform API Gateway multiple region deployment: Access Denied

我打算采用模块化方法跨多个区域(aws 和 aws.secondary 提供商)部署 APIgateway 和相关的 lambda。假设每个 var 变量都被赋值:

主网关:

module "test_api" {
  source      = "./modules/api-gateway"
  name        = "test-api"
  description = "primary API"
  vpc_id      = var.vpc_id #vpc in primary region
  region      = var.region
  stage_name  = ""

  policy = <<EOF
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Deny",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "execute-api:/*",
        "Condition": {
          "StringNotEquals": {
            "aws:sourceVpce": ["${data.aws_vpc_endpoint.execute_api_endpoint.id}"]
          }
        }
      },
      {
        "Effect": "Allow",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "execute-api:/*"
      },
      {
        "Effect": "Allow",
        "Principal": "*",
        "Action": "lambda:InvokeFunction",
        "Resource": ["${module.test_lambda.lambda_arn}"]
      }
    ]
  }
  EOF

}
data "aws_vpc_endpoint" "execute_api_endpoint" {
  vpc_id       = var.vpc_id
  service_name = "com.amazonaws.${var.region}.execute-api"
}


# --------------Endpoint and Authorization ------------------

module "post_endpoint" {
source = "./modules/api-gateway-endpoint"

rest_api_id      = module.test_api.api_id
root_resource_id = module.test_api.root_resource_id
path             = "test"
invoke_arn       = module.test_lambda.invoke_arn
lambda_name      = module.test_lambda.function_name
execution_arn    = module.test_api.execution_arn
http_method      = "POST"
authorizer_id    = aws_api_gateway_authorizer.gateway-authorizer.id
}
resource "aws_api_gateway_authorizer" "gateway-authorizer" {
  name                   = "test-gateway-authorizer"
  rest_api_id            = module.test_api.api_id
  authorizer_uri         = module.lambda_api_gateway_authorizer.invoke_arn
  authorizer_credentials = aws_iam_role.authorizer_invocation_role.arn
  identity_source        = "method.request.header.X-SF_AD_OIDC_TOKEN"
  type                   = "REQUEST"
}
module "lambda_api_gateway_authorizer" {
  source = "./modules/lambda"

  function_name        = "azureAuthorizer"
  policy_document_json = data.aws_iam_policy_document.lambda_api_gateway_authorizer.json
  account              = var.account  
  region               = var.region
    
  subnet_ids         = local.private_subnets
  security_group_ids = [local.https_outbound_sg_id]

  kms_key_arn          = var.kms_key_id
  permissions_boundary = var.permissions_boundary

}
data "aws_iam_policy_document" "lambda_api_gateway_authorizer" {
  statement {
    actions = "*"
    resources = ["arn:aws:logs:${var.region}:${var.account}:log-group:/aws/lambda/*:*"]
  }

  statement {
    actions   = ["kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey"]
    resources = [var.kms_key_id]
  }

  statement {
    actions = [
      "ec2:DescribeInstances",
      "ec2:CreateNetworkInterface",
      "ec2:AttachNetworkInterface",
      "ec2:DescribeNetworkInterfaces",
      "ec2:DeleteNetworkInterface",
    ]

    resources = ["*"]
  }
}
resource "aws_iam_role" "authorizer_invocation_role" {
  name                 = "test-api-gateway-auth-invocation"
  path                 = "/"
  permissions_boundary = var.permissions_boundary

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

# --------------Stages and Deployment ------------------

resource "aws_api_gateway_deployment" "test_api" {
  depends_on = [module.post_endpoint.endpoint_integration]

  rest_api_id = module.test_api.api_id
  triggers = {
    redeployment = sha1(join(
      "",
      [
        file("api-test-endpoint.tf") #endpoints are declared in this file
      ]
    ))
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "stage" {
  deployment_id = aws_api_gateway_deployment.test_api.id
  rest_api_id   = module.test_api.api_id
  stage_name    = "sandbox"
  access_log_settings {
    destination_arn = module.test_api.api_logs_arn
    format          = "{ \"requestId\":\"$context.requestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\" }"
  }
}


resource "aws_api_gateway_method_settings" "test_api" {
  rest_api_id = module.test_api.api_id
  stage_name  = aws_api_gateway_stage.stage.stage_name
  method_path = "*/*"
}
module "test_lambda" {
  source = "./modules/lambda"

  function_name        = "testAPILambda"
  policy_document_json = data.aws_iam_policy_document.test_policy.json
  tags                 = local.common_tags
  workspace            = terraform.workspace
  account              = var.account
  region               = var.region

  subnet_ids         = local.private_subnets
  security_group_ids = [local.https_outbound_sg_id]

  kms_key_arn     = var.kms_key_id
  permissions_boundary = var.permissions_boundary

}

二级网关:

module "test_secondary" {
  source      = "./modules/api-gateway"
  name        = "test-api"
  description = "Secondary API"
  vpc_id      = var.secondary_vpc_id
  region      = var.secondary_region
  stage_name  = ""
  providers    = { aws = aws.secondary }
  policy = <<EOF
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Deny",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "execute-api:/*",
        "Condition": {
          "StringNotEquals": {
            "aws:sourceVpce": ["${data.aws_vpc_endpoint.execute_api_endpoint_secondary.id}"]
          }
        }
      },
      {
        "Effect": "Allow",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "execute-api:/*"
      },
      {
        "Effect": "Allow",
        "Principal": "*",
        "Action": "lambda:InvokeFunction",
        "Resource": ["${module.test_lambda_secondary.lambda_arn}"]
      }
    ]
  }
  EOF

}

data "aws_vpc_endpoint" "execute_api_endpoint_secondary" {
  vpc_id       = var.secondary_vpc_id
  service_name = "com.amazonaws.${var.secondary_region}.execute-api"
  provider = aws.secondary
}

# --------------Endpoint and Authorization ------------------

module "test_endpoint_secondary" {
  source = "./modules/api-gateway-endpoint"

  rest_api_id      = module.test_api_secondary.api_id
  root_resource_id = module.test_api_secondary.root_resource_id
  path             = "test"
  invoke_arn       = module.test_lambda_secondary.invoke_arn
  lambda_name      = module.test_lambda_secondary.function_name
  execution_arn    = module.test_api_secondary.execution_arn
  http_method      = "POST"
  authorizer_id    = aws_api_gateway_authorizer.gateway-authorizer-secondary.id
}

resource "aws_api_gateway_authorizer" "gateway-authorizer-secondary" {
  name                   = "test-gateway-authorizer-secondary"
  rest_api_id            = module.test_api_secondary.api_id
  authorizer_uri         = module.lambda_api_gateway_authorizer_secondary.invoke_arn
  authorizer_credentials = aws_iam_role.authorizer_invocation_role.arn
  identity_source        = "method.request.header.X-SF_AD_OIDC_TOKEN"
  type                   = "REQUEST"
}

resource "aws_iam_role_policy" "authorizer_invocation_policy_secondary" {
  name = "test-api-gateway-auth-invocation-policy-secondary"
  role = aws_iam_role.authorizer_invocation_role.id

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "lambda:InvokeFunction",
      "Effect": "Allow",
      "Resource": "${module.lambda_api_gateway_authorizer_secondary.lambda_arn}"
    }
  ]
}
EOF
}
 
module "lambda_api_gateway_authorizer_secondary" {
  source = "./modules/lambda"

  function_name        = "azureAuthorizer"
  policy_document_json = data.aws_iam_policy_document.lambda_api_gateway_authorizer_secondary.json
  account              = var.account
  region               = var.secondary_region
  providers            = { aws = aws.secondary }

  subnet_ids         = local.secondary_private_subnets
  security_group_ids = [local.https_outbound_sg_id_secondary]

  kms_key_arn     = var.kms_secondary_key_id
  permissions_boundary = var.permissions_boundary

}

data "aws_iam_policy_document" "lambda_api_gateway_authorizer_secondary" {
  statement {
    actions = [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
      "logs:DescribeLogStreams",
      "logs:PutSubscriptionFilter"
    ]
    resources = ["arn:aws:logs:${var.secondary_region}:${var.account}:log-group:/aws/lambda/*:*"]
  }

  statement {
    actions   = ["kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey"]
    resources = [var.kms_secondary_key_id]
  }

  statement {
    actions = [
      "ec2:DescribeInstances",
      "ec2:CreateNetworkInterface",
      "ec2:AttachNetworkInterface",
      "ec2:DescribeNetworkInterfaces",
      "ec2:DeleteNetworkInterface",
    ]

    resources = ["*"]
  }
}

# --------------Stages and Deployment ------------------

resource "aws_api_gateway_deployment" "test_secondary" {
  depends_on = [module.test_endpoint_secondary.endpoint_integration]

  rest_api_id = module.test_secondary.api_id
  triggers = {
    redeployment = sha1(join(
      "",
      [
        file("api-test-endpoint.tf")
      ]
    ))
  }

  lifecycle {
    create_before_destroy = true
  }
  provider = aws.secondary
}

resource "aws_api_gateway_stage" "stage_secondary" {
  deployment_id = aws_api_gateway_deployment.test_secondary.id
  rest_api_id   = module.test_secondary.api_id
  stage_name    = "sandbox"
  access_log_settings {
    destination_arn = module.test_secondary.api_logs_arn
    format          = "{ \"requestId\":\"$context.requestId\", \"ip\": \"$context.identity.sourceIp\", \"caller\":\"$context.identity.caller\", \"user\":\"$context.identity.user\", \"requestTime\":\"$context.requestTime\", \"httpMethod\":\"$context.httpMethod\", \"resourcePath\":\"$context.resourcePath\", \"status\":\"$context.status\", \"protocol\":\"$context.protocol\", \"responseLength\":\"$context.responseLength\" }"
  }
  provider = aws.secondary
}


resource "aws_api_gateway_method_settings" "test_secondary" {
  rest_api_id = module.test_secondary.api_id
  stage_name  = aws_api_gateway_stage.stage_secondary.stage_name
  method_path = "*/*"
  provider = aws.secondary
}
resource "aws_lambda_permission" "test_permission_secondary" {
  statement_id  = "AllowAPIGatewayInvoke"
  action        = "lambda:InvokeFunction"
  function_name = module.test_lambda_secondary.function_name
  principal     = "apigateway.amazonaws.com"

  source_arn = "${module.test_api_secondary.execution_arn}/*/*/*"
  provider = aws.secondary
}
module "test_lambda_secondary" {
  source = "./modules/lambda"

  function_name        = "testAPILambda-secondary"
  policy_document_json = data.aws_iam_policy_document.test_policy_secondary.json
  workspace            = terraform.workspace
  account              = var.account
  region               = var.secondary_region
  providers            = { aws = aws.secondary }

  subnet_ids         = local.secondary_private_subnets
  security_group_ids = [local.https_outbound_sg_id_secondary]

  kms_key_arn     = var.kms_secondary_key_id
  permissions_boundary = var.permissions_boundary

}

这些几乎相同,只是为该地区提供了二级提供商。
我收到此错误:

Error creating API Gateway: AccessDeniedException: on modules\api-gateway\main.tf line 1, in resource "aws_api_gateway_rest_api" "api_gateway": 1: resource "aws_api_gateway_rest_api" "api_gateway" {

并且此错误在添加辅助资源后显示。

两个资源都引用了这个块模板:

resource "aws_api_gateway_rest_api" "api_gateway" {
  name        = var.name
  description = var.description

  endpoint_configuration {
    types = ["PRIVATE"]
  }


  policy = var.policy
}

#  ------------- API Cloudwatch Logs --------------

resource "aws_cloudwatch_log_group" "api_logs" {
  name              = "test-apigateway-${aws_api_gateway_rest_api.api_gateway.id}-access-logs"
  retention_in_days = 14
}

如果您能提供有关在哪里可以找到此错误或需要查看的内容的任何帮助,我们将不胜感激。

已采取的步骤:

我不确定我是否理解正确,但在我看来,您正在尝试在不同区域中创建到 API 网关的 VPC 接口端点。如果是这样,则不支持。来自 docs:

Endpoints are supported within the same Region only. You cannot create an endpoint between a VPC and a service in a different Region.