syslog-server/terraform/hcp_iam.tf

591 lines
17 KiB
Terraform
Raw Normal View History

# HCP plan/apply roles for syslog-server-prod (PLAT-78 / PLAT-144).
# Copy of seahaven-org-baseline/examples/hcptf-workspace-iam/hcp_iam.tf.example
# with the syslog-server EC2 service set. Create, do not import.
#
# Live seahaven-hcptf-iam-management DenySelfMutation blocks DetachRolePolicy
# and PutRolePolicy on hcptf-* (including this role). First-apply sequence:
# 1. seahaven-org-baseline scripts/create-hcptf-bootstrap-roles.sh
# --account prod --allow-workspace syslog-server-prod
# 2. Point this workspace's TFC_AWS_* at hcptf-bootstrap /
# hcptf-bootstrap-plan (workspace vars, never a project set).
# 3. One Manual apply (create roles + scoped inline + boundary + stack).
# 4. Point TFC_AWS_* back at hcptf-syslog-server / hcptf-syslog-server-plan.
# 5. Re-run the script without --allow-workspace to pin trust back to
# iam-bootstrap-prod only.
# Later apply-role IAM edits use the same window. Do not add StringLike
# on bootstrap trust. CreatePolicy stays on hcptf-bootstrap only; boundary
# document changes after seal also need that window.
data "aws_iam_policy_document" "hcptf_apply_trust" {
statement {
sid = "HcpApply"
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = ["arn:aws:iam::${local.account_id}:oidc-provider/app.terraform.io"]
}
condition {
test = "StringEquals"
variable = "app.terraform.io:aud"
values = ["aws.workload.identity"]
}
condition {
test = "StringEquals"
variable = "app.terraform.io:sub"
values = [
"organization:seahaven:project:${local.hcp_project}:workspace:${local.hcp_workspace}:run_phase:apply",
]
}
}
}
data "aws_iam_policy_document" "hcptf_plan_trust" {
statement {
sid = "HcpPlan"
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = ["arn:aws:iam::${local.account_id}:oidc-provider/app.terraform.io"]
}
condition {
test = "StringEquals"
variable = "app.terraform.io:aud"
values = ["aws.workload.identity"]
}
condition {
test = "StringEquals"
variable = "app.terraform.io:sub"
values = [
"organization:seahaven:project:${local.hcp_project}:workspace:${local.hcp_workspace}:run_phase:plan",
]
}
}
}
data "aws_iam_policy_document" "hcptf_scoped_iam" {
statement {
sid = "DenyCreatePolicy"
effect = "Deny"
actions = [
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:SetDefaultPolicyVersion",
]
resources = ["*"]
}
statement {
sid = "CreateExecRoleWithBoundary"
effect = "Allow"
actions = ["iam:CreateRole"]
resources = [
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*",
]
condition {
test = "StringLike"
variable = "iam:PermissionsBoundary"
values = [
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary",
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary-${local.stack_name}",
]
}
}
statement {
sid = "MutateExecRoleWithBoundary"
effect = "Allow"
actions = [
"iam:AttachRolePolicy",
"iam:PutRolePolicy",
"iam:PutRolePermissionsBoundary",
]
resources = [
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*",
]
condition {
test = "StringLike"
variable = "iam:PermissionsBoundary"
values = [
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary",
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary-${local.stack_name}",
]
}
}
statement {
sid = "WriteExecRoles"
effect = "Allow"
actions = [
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription",
]
resources = [
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*",
]
}
statement {
sid = "PassExecRolesToEc2"
effect = "Allow"
actions = ["iam:PassRole"]
resources = ["arn:aws:iam::${local.account_id}:role/tf-managed/${local.instance_role_name}"]
condition {
test = "StringEquals"
variable = "iam:PassedToService"
values = ["ec2.amazonaws.com"]
}
}
statement {
sid = "InstanceProfiles"
effect = "Allow"
actions = [
"iam:AddRoleToInstanceProfile",
"iam:CreateInstanceProfile",
"iam:DeleteInstanceProfile",
"iam:GetInstanceProfile",
"iam:ListInstanceProfileTags",
"iam:RemoveRoleFromInstanceProfile",
"iam:TagInstanceProfile",
"iam:UntagInstanceProfile",
]
resources = [
"arn:aws:iam::${local.account_id}:instance-profile/tf-managed/${local.instance_profile_name}",
]
}
statement {
sid = "IamReadOnly"
effect = "Allow"
actions = [
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetInstanceProfile",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfiles",
"iam:ListInstanceProfilesForRole",
"iam:ListPolicies",
"iam:ListPolicyVersions",
"iam:ListRolePolicies",
"iam:ListRoleTags",
"iam:ListRoles",
]
resources = ["*"]
}
statement {
sid = "DenySelfMutation"
effect = "Deny"
actions = [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteRolePermissionsBoundary",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription",
]
resources = [
"arn:aws:iam::${local.account_id}:role/hcptf-*",
"arn:aws:iam::${local.account_id}:role/github-cfn-execution-role",
"arn:aws:iam::${local.account_id}:role/githubdeploy-*",
"arn:aws:iam::${local.account_id}:role/cdk-hnb659fds-*",
"arn:aws:iam::${local.account_id}:role/OrganizationAccountAccessRole",
"arn:aws:iam::${local.account_id}:role/seahaven-*",
]
}
statement {
sid = "DenyBoundaryTampering"
effect = "Deny"
actions = [
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteUserPermissionsBoundary",
]
resources = [
"arn:aws:iam::${local.account_id}:role/*",
"arn:aws:iam::${local.account_id}:user/*",
]
}
statement {
sid = "DenyBoundaryPolicyEdit"
effect = "Deny"
actions = [
"iam:CreatePolicyVersion",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:SetDefaultPolicyVersion",
]
resources = ["arn:aws:iam::${local.account_id}:policy/seahaven-*"]
}
}
data "aws_iam_policy_document" "hcptf_apply_services" {
# checkov:skip=CKV_AWS_111: EC2 VPC/instance lifecycle and describe APIs require Resource=*. Log group, alarm, and SNS writes are ARN-prefixed.
statement {
sid = "CloudWatchLogs"
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:DeleteLogGroup",
"logs:PutRetentionPolicy",
"logs:DeleteRetentionPolicy",
"logs:TagResource",
"logs:UntagResource",
"logs:ListTagsForResource",
"logs:AssociateKmsKey",
"logs:DisassociateKmsKey",
]
resources = [
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.log_group_name}",
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.log_group_name}:*",
]
}
statement {
sid = "CloudWatchLogsDescribe"
effect = "Allow"
actions = ["logs:DescribeLogGroups"]
resources = ["*"]
}
statement {
sid = "CloudWatchAlarms"
effect = "Allow"
actions = [
"cloudwatch:PutMetricAlarm",
"cloudwatch:DeleteAlarms",
"cloudwatch:DescribeAlarms",
"cloudwatch:TagResource",
"cloudwatch:UntagResource",
"cloudwatch:ListTagsForResource",
]
resources = [
"arn:aws:cloudwatch:${var.aws_region}:${local.account_id}:alarm:Syslog-*",
"arn:aws:cloudwatch:${var.aws_region}:${local.account_id}:alarm:EC2-StatusCheck*syslog*",
]
}
statement {
sid = "CloudWatchDescribeAlarms"
effect = "Allow"
actions = ["cloudwatch:DescribeAlarms"]
resources = ["*"]
}
statement {
sid = "SnsPublishSiteAlerts"
effect = "Allow"
actions = [
"sns:Publish",
"sns:GetTopicAttributes",
"sns:ListTagsForResource",
]
resources = [local.site_alerts_arn]
}
statement {
sid = "ManageTfManagedBoundary"
effect = "Allow"
actions = [
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:ListPolicyVersions",
"iam:ListPolicyTags",
"iam:TagPolicy",
"iam:UntagPolicy",
]
resources = [
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
]
}
statement {
sid = "Ec2VpcManagement"
effect = "Allow"
actions = [
"ec2:AllocateAddress",
"ec2:AssociateAddress",
"ec2:AssociateRouteTable",
"ec2:AttachInternetGateway",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateInternetGateway",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:DeleteInternetGateway",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAddressesAttribute",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcs",
"ec2:DescribePrefixLists",
"ec2:DetachInternetGateway",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ReleaseAddress",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress",
]
resources = ["*"]
}
statement {
sid = "Ec2InstanceManagement"
effect = "Allow"
actions = [
"ec2:AssociateIamInstanceProfile",
"ec2:AttachVolume",
"ec2:CreateVolume",
"ec2:DeleteVolume",
"ec2:DescribeIamInstanceProfileAssociations",
"ec2:DescribeImages",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInstanceRecoveryAttribute",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstances",
"ec2:DescribeVolumes",
"ec2:DescribeVolumeAttribute",
"ec2:DescribeVolumeStatus",
"ec2:DetachVolume",
"ec2:DisassociateIamInstanceProfile",
"ec2:GetConsoleOutput",
"ec2:ModifyInstanceAttribute",
"ec2:ModifyVolume",
"ec2:MonitorInstances",
"ec2:RebootInstances",
"ec2:RecoverInstances",
"ec2:ReplaceIamInstanceProfileAssociation",
"ec2:RunInstances",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:TerminateInstances",
"ec2:UnmonitorInstances",
]
resources = ["*"]
}
statement {
sid = "CloudWatchEc2RecoverServiceLinkedRole"
effect = "Allow"
actions = [
"iam:CreateServiceLinkedRole",
]
resources = [
"arn:aws:iam::${local.account_id}:role/aws-service-role/events.amazonaws.com/AWSServiceRoleForCloudWatchEvents",
]
condition {
test = "StringEquals"
variable = "iam:AWSServiceName"
values = ["events.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "hcptf_plan_refresh" {
statement {
sid = "RefreshIamRoles"
effect = "Allow"
actions = [
"iam:GetRole",
"iam:GetRolePolicy",
"iam:GetInstanceProfile",
"iam:ListRolePolicies",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:ListRoleTags",
]
resources = [
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*",
"arn:aws:iam::${local.account_id}:instance-profile/tf-managed/${local.stack_prefix}*",
"arn:aws:iam::${local.account_id}:role/${local.apply_role}",
"arn:aws:iam::${local.account_id}:role/${local.plan_role}",
]
}
statement {
sid = "RefreshManagedPolicies"
effect = "Allow"
actions = [
"iam:GetPolicy",
"iam:GetPolicyVersion",
]
resources = ["*"]
}
statement {
sid = "RefreshLogs"
effect = "Allow"
actions = [
"logs:DescribeLogGroups",
"logs:ListTagsForResource",
]
resources = ["*"]
}
statement {
sid = "RefreshAlarms"
effect = "Allow"
actions = [
"cloudwatch:DescribeAlarms",
"cloudwatch:ListTagsForResource",
]
resources = ["*"]
}
statement {
sid = "RefreshSns"
effect = "Allow"
actions = [
"sns:GetTopicAttributes",
"sns:ListTagsForResource",
]
resources = [local.site_alerts_arn]
}
statement {
sid = "RefreshEc2"
effect = "Allow"
actions = [
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAddressesAttribute",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeIamInstanceProfileAssociations",
"ec2:DescribeImages",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInstanceRecoveryAttribute",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVolumeAttribute",
"ec2:DescribeVolumeStatus",
"ec2:DescribeVolumes",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcs",
"ec2:GetConsoleOutput",
]
resources = ["*"]
}
}
resource "aws_iam_role" "hcptf_apply" {
name = local.apply_role
assume_role_policy = data.aws_iam_policy_document.hcptf_apply_trust.json
max_session_duration = 3600
tags = {
Owner = "adam@seahavenind.com"
ManagedBy = "terraform"
}
}
resource "aws_iam_role" "hcptf_plan" {
name = local.plan_role
assume_role_policy = data.aws_iam_policy_document.hcptf_plan_trust.json
max_session_duration = 3600
tags = {
Owner = "adam@seahavenind.com"
ManagedBy = "terraform"
}
}
resource "aws_iam_role_policy" "hcptf_scoped_iam" {
name = "scoped-iam-management"
role = aws_iam_role.hcptf_apply.id
policy = data.aws_iam_policy_document.hcptf_scoped_iam.json
}
resource "aws_iam_role_policy" "hcptf_apply_services" {
# checkov:skip=CKV_AWS_111: EC2 VPC/instance lifecycle and describe APIs require Resource=*. Log group, alarm, and SNS writes are ARN-prefixed.
name = "syslog-server-services"
role = aws_iam_role.hcptf_apply.id
policy = data.aws_iam_policy_document.hcptf_apply_services.json
}
resource "aws_iam_role_policy" "hcptf_plan_refresh" {
# checkov:skip=CKV_AWS_107: ViewOnlyAccess plus this sidecar is the org HCP plan-role pattern (PLAT-144). Sidecar Get* is named (GetRole, GetPolicy, GetInstanceProfile, GetConsoleOutput, GetTopicAttributes) and scoped to this stack. It does not add iam:CreateAccessKey, secretsmanager:GetSecretValue, or ssm:GetParameter on *.
name = "syslog-server-plan-refresh"
role = aws_iam_role.hcptf_plan.id
policy = data.aws_iam_policy_document.hcptf_plan_refresh.json
}
resource "aws_iam_role_policy_attachment" "hcptf_plan_view_only" {
role = aws_iam_role.hcptf_plan.name
policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
}
resource "aws_iam_role_policy_attachments_exclusive" "hcptf_apply" {
role_name = aws_iam_role.hcptf_apply.name
policy_arns = []
}
resource "aws_iam_role_policy_attachments_exclusive" "hcptf_plan" {
role_name = aws_iam_role.hcptf_plan.name
policy_arns = [
"arn:aws:iam::aws:policy/job-function/ViewOnlyAccess",
]
}