kics icon indicating copy to clipboard operation
kics copied to clipboard

Terraform SAST Scanner tells me: "API Gateway Deployment should have access log setting defined when connected to an API Gateway Stage." but I don't see a way to add access logs to this resource

Open adamkendall1 opened this issue 2 years ago • 1 comments

Apologies if I'm missing something here, but I've reviewed the documentation at Terraform's site and it seems to me that it's not possible to add access logging to an aws_api_gateway_deployment resource.

If I'm correct, then this would indicate there may be an issue with this particular check in the Terraform scanner.

adamkendall1 avatar Aug 08 '22 13:08 adamkendall1

Hi, @adamkendall1 hope you are doing great! You can add access_log_settings to aws_api_gateway_deployment by aws_api_gateway_stage resource as you can see in KICS negative sample for the query in question, that is located in assets/queries/terraform/aws/api_gateway_deployment_without_access_log_setting/test/negative1.tf

resource "aws_api_gateway_deployment" "example5" {
  rest_api_id   = "some rest api id"
  stage_name = "some name"
  stage_description = "some description"

  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "example0" {
  deployment_id = aws_api_gateway_deployment.example5.id
  rest_api_id   = aws_api_gateway_rest_api.example.id
  stage_name    = "example"

  access_log_settings {
    destination_arn = "dest"
    format = "format"
  }
}

Please, let me know if you have any other doubt!

cxMiguelSilva avatar Aug 09 '22 08:08 cxMiguelSilva

@cxMiguelSilva

In my code, I -do- have access_log_settings declared in my aws_api_gateway_stage associated with the aws_api_gateway_deployment. But the scanner still flags and says I need to add access logging to the deployment.

Maybe I'm doing something wrong here and it's expected behavior but I can't seem to figure out why it would be flagging my deployments...

See api_gw.tf file below:

variable "REDACTED-api-methods" {
  type = list
  default = ["alertmessages", "cardstatusupdate", "fraudalerts", "fundstransfer", "replacecard", "transactions", "unprovtransactions"]
}

resource "aws_api_gateway_rest_api" "REDACTED-REDACTED-dev-cs" {
  name = "TF-MANAGED-REDACTED-REDACTED-dev-cs"

  tags = merge(
    local.common_tags,
    {
      environment             = "dev"
      createdby               = "[email protected]"
      CreatorName             = "[email protected]"
      approvedby              = "REDACTED"
      owner                   = "REDACTED"
      product                 = "REDACTED"
    },
)
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-cs-resource" {
  parent_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.root_resource_id
  path_part   = "REDACTEDtestmcs"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-cs-resource-2" {
  parent_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-cs-resource.id
  path_part   = "v1"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-cs-resource-3" {
  parent_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-cs-resource-2.id
  for_each    = toset(var.REDACTED-api-methods)
  path_part   = "${each.key}"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
}

resource "aws_api_gateway_method" "REDACTED-REDACTED-dev-cs-method" {
  authorization = "NONE"
  http_method   = "POST"
  for_each      = toset(var.REDACTED-api-methods)
  resource_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-cs-resource-3[each.key].id
  rest_api_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
}

resource "aws_api_gateway_integration" "REDACTED-REDACTED-dev-cs-integration" {
  for_each    = toset(var.REDACTED-api-methods)
  http_method = "POST"
  resource_id = aws_api_gateway_resource.REDACTED-REDACTED-dev-cs-resource-3[each.key].id
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
  type        = "AWS"
  integration_http_method = "POST"
  uri         = "arn:aws:apigateway:us-east-1:sqs:path/REDACTED/TF-MANAGED-REDACTED-REDACTED-cs-dev-${each.key}"
  credentials = "arn:aws:iam::REDACTED:role/REDACTED-apigateway-sqs-role"
}

resource "aws_api_gateway_deployment" "REDACTED-REDACTED-dev-cs-deployment" {
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id

  #for_each    = toset(var.REDACTED-api-methods)

  triggers = {
    redeployment = sha1(jsonencode([
      aws_api_gateway_resource.REDACTED-REDACTED-dev-cs-resource-3,
      aws_api_gateway_method.REDACTED-REDACTED-dev-cs-method,
      aws_api_gateway_integration.REDACTED-REDACTED-dev-cs-integration,
    ]))
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "REDACTED-REDACTED-dev-cs-stage" {
  deployment_id = aws_api_gateway_deployment.REDACTED-REDACTED-dev-cs-deployment.id
  rest_api_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-cs.id
  stage_name    = "API-GW-STAGE-DEV"
  access_log_settings {
    destination_arn = "${aws_cloudwatch_log_group.REDACTED-api-gw-access-dev.arn}"
    format = "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] $context.httpMethod $context.resourcePath $context.protocol $context.status $context.responseLength $context.requestId $context.extendedRequestId"
  }

}

resource "aws_api_gateway_rest_api" "REDACTED-REDACTED-dev-is" {
  name = "TF-MANAGED-REDACTED-REDACTED-dev-is"

  tags = merge(
    local.common_tags,
    {
      environment             = "dev"
      createdby               = "[email protected]"
      CreatorName             = "[email protected]"
      approvedby              = "REDACTED"
      owner                   = "REDACTED"
      product                 = "REDACTED"
    },
)
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-is-resource" {
  parent_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.root_resource_id
  path_part   = "REDACTEDtestmis"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-is-resource-2" {
  parent_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-is-resource.id
  path_part   = "v1"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
}

resource "aws_api_gateway_resource" "REDACTED-REDACTED-dev-is-resource-3" {
  parent_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-is-resource-2.id
  for_each    = toset(var.REDACTED-api-methods)
  path_part   = "${each.key}"
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
}

resource "aws_api_gateway_method" "REDACTED-REDACTED-dev-is-method" {
  authorization = "NONE"
  http_method   = "POST"
  for_each      = toset(var.REDACTED-api-methods)
  resource_id   = aws_api_gateway_resource.REDACTED-REDACTED-dev-is-resource-3[each.key].id
  rest_api_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
}



resource "aws_api_gateway_integration" "REDACTED-REDACTED-dev-is-integration" {
  for_each    = toset(var.REDACTED-api-methods)
  http_method = "POST"
  resource_id = aws_api_gateway_resource.REDACTED-REDACTED-dev-is-resource-3[each.key].id
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
  type        = "AWS"
  integration_http_method = "POST"
  uri         = "arn:aws:apigateway:us-east-1:sqs:path/REDACTED/TF-MANAGED-REDACTED-REDACTED-is-dev-${each.key}"
  credentials = "arn:aws:iam::REDACTED:role/REDACTED-apigateway-sqs-role"
}

resource "aws_api_gateway_deployment" "REDACTED-REDACTED-dev-is-deployment" {
  rest_api_id = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id

  #for_each    = toset(var.REDACTED-api-methods)

  triggers = {
    redeployment = sha1(jsonencode([
      aws_api_gateway_resource.REDACTED-REDACTED-dev-is-resource-3,
      aws_api_gateway_method.REDACTED-REDACTED-dev-is-method,
      aws_api_gateway_integration.REDACTED-REDACTED-dev-is-integration,
    ]))
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "REDACTED-REDACTED-dev-is-stage" {
  deployment_id = aws_api_gateway_deployment.REDACTED-REDACTED-dev-is-deployment.id
  rest_api_id   = aws_api_gateway_rest_api.REDACTED-REDACTED-dev-is.id
  stage_name    = "API-GW-STAGE-DEV"
  access_log_settings {
    destination_arn = "${aws_cloudwatch_log_group.REDACTED-api-gw-access-dev.arn}"
    format = "$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] $context.httpMethod $context.resourcePath $context.protocol $context.status $context.responseLength $context.requestId $context.extendedRequestId"
  }
}

adamkendall1 avatar Aug 17 '22 15:08 adamkendall1

Hi @adamkendall1, I hope you are doing great!!! Upon further looking into your problem I found out that the results that you are getting are related to the 3rd policy present in the API Gateway Deployment should have access log setting defined when connected to an API Gateway Stage Security Query that you can see in the code snippet:

CxPolicy[result] {
	document := input.document[i]
	deployment = document.resource.aws_api_gateway_deployment[name]

	count({x | resource := input.document[_].resource[x]; x == "aws_api_gateway_stage"}) != 0

	settings_are_equal(name)

	not common_lib.valid_key(deployment, "stage_description")

	result := {
		"documentId": document.id,
		"resourceType": "aws_api_gateway_deployment",
		"resourceName": tf_lib.get_resource_name(deployment, name),
		"searchKey": sprintf("aws_api_gateway_deployment[%s]", [name]),
		"issueType": "MissingAttribute",
		"keyExpectedValue": sprintf("aws_api_gateway_deployment[%s].stage_description is set", [name]),
		"keyActualValue": sprintf("aws_api_gateway_deployment[%s].stage_description is undefined", [name]),
	}
}

This policy checks for the presence of stage_description inside the aws_api_gateway_deployment resource, something that is not occurring in the resources REDACTED-REDACTED-dev-cs-deployment and REDACTED-REDACTED-dev-is-deployment. Hope this solves your problem! In case of any other doubt please get in touch!

cxMiguelSilva avatar Aug 18 '22 08:08 cxMiguelSilva

@cxMiguelSilva I -am- doing great, thanks to you. Hope you are doing great too.

Your suggestion to add a stage_description to my aws_api_gateway_deployement fixed the issue. Good find!

adamkendall1 avatar Aug 18 '22 21:08 adamkendall1