mirror of
https://github.com/Sea-Haven-Industries/forgejo.git
synced 2026-09-30 03:03:11 +00:00
* fix(terraform): allow HCP refresh of the log group and parameter The scoped apply role can create those resources, but CloudWatch and SSM list them on a wildcard ARN. DescribeLogGroups and DescribeParameters need that resource. * fix(terraform): let the plan role read bucket website config The S3 provider refreshes GetBucketWebsite. The plan role was denied on the three Forgejo buckets. * fix(terraform): let the plan role read backup object metadata HeadObject on the Lambda zip is s3:GetObject. The plan role only had the bucket ARNs. * fix(terraform): let the plan role read object tags and retention The S3 provider refreshes tagging, ACL, attributes, and Object Lock on the Lambda zip. * fix(terraform): scope plan object reads and restore on the data volume The plan role only needs object reads for the verification zip. Unpacking a dump in /tmp fills the root volume. * fix(terraform): accept dumps that already contain data/forgejo.db Today's archive has no gitea-db.sqlite3 at the root. Copy that file only when it is present. * docs(terraform): keep restore runbook on one bucket and fail closed Glacier and the download use the prod bucket. GCS unpacks on the data volume. Neither path deletes live repos until the dump has a database. * docs(terraform): keep optional restore copies from aborting under set -e if/fi matches user_data.sh. The file comment now says forgejo-services versions also go through the org-account role. * fix(terraform): restore the dump app.ini with the database INTERNAL_TOKEN, JWT_SECRET, and LFS_JWT_SECRET live in that file. A restore that keeps the generated file cannot decrypt the dumped secrets.
852 lines
23 KiB
HCL
852 lines
23 KiB
HCL
# HCP plan/apply roles for forgejo-prod (PLAT-80).
|
|
# Copy of seahaven-org-baseline/examples/hcptf-workspace-iam/hcp_iam.tf.example
|
|
# with the Forgejo EC2, ALB, S3 CRR, DLM, and Lambda service set. Create, do not import.
|
|
#
|
|
# First-apply sequence:
|
|
# 1. seahaven-org-baseline scripts/create-hcptf-bootstrap-roles.sh
|
|
# --account prod --allow-workspace forgejo-prod
|
|
# 2. Point this workspace's TFC_AWS_* at hcptf-bootstrap / hcptf-bootstrap-plan
|
|
# (workspace vars, never a project set).
|
|
# 3. One Manual apply. Bootstrap can write hcptf-* and policy/tf-managed/*.
|
|
# It cannot PassRole to ec2 or create the buckets, so non-IAM resources
|
|
# error and stay out of state.
|
|
# 4. Point TFC_AWS_* at hcptf-forgejo / hcptf-forgejo-plan.
|
|
# 5. Re-run the script without --allow-workspace.
|
|
# 6. Second Manual apply creates the instance, buckets, ALB, and Lambda.
|
|
# Later edits to hcptf-* inline policies and to policy/tf-managed/forgejo-services
|
|
# need the org-account role. The scoped role cannot PutRolePolicy or
|
|
# CreatePolicyVersion. Do not add StringLike on bootstrap trust.
|
|
# CreatePolicy stays on hcptf-bootstrap.
|
|
|
|
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}*",
|
|
]
|
|
}
|
|
}
|
|
|
|
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}*",
|
|
]
|
|
}
|
|
}
|
|
|
|
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 = "PassInstanceRoleToEc2"
|
|
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 = "PassDlmRole"
|
|
effect = "Allow"
|
|
actions = ["iam:PassRole"]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.dlm_role_name}",
|
|
]
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "iam:PassedToService"
|
|
values = ["dlm.amazonaws.com"]
|
|
}
|
|
}
|
|
|
|
statement {
|
|
sid = "PassReplicationRoleToS3"
|
|
effect = "Allow"
|
|
actions = ["iam:PassRole"]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.replication_role_name}",
|
|
]
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "iam:PassedToService"
|
|
values = ["s3.amazonaws.com"]
|
|
}
|
|
}
|
|
|
|
statement {
|
|
sid = "PassLambdaRole"
|
|
effect = "Allow"
|
|
actions = ["iam:PassRole"]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:role/tf-managed/${local.lambda_role_name}",
|
|
]
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "iam:PassedToService"
|
|
values = ["lambda.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 = "GcsTransferUser"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:CreateUser",
|
|
"iam:DeleteUser",
|
|
"iam:GetUser",
|
|
"iam:GetUserPolicy",
|
|
"iam:ListUserPolicies",
|
|
"iam:ListUserTags",
|
|
"iam:PutUserPermissionsBoundary",
|
|
"iam:PutUserPolicy",
|
|
"iam:DeleteUserPolicy",
|
|
"iam:TagUser",
|
|
"iam:UntagUser",
|
|
]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:user/tf-managed/${local.gcs_user_name}",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "IamReadOnly"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:GetPolicy",
|
|
"iam:GetPolicyVersion",
|
|
"iam:GetRole",
|
|
"iam:GetRolePolicy",
|
|
"iam:ListAttachedRolePolicies",
|
|
"iam:ListInstanceProfilesForRole",
|
|
"iam:ListPolicies",
|
|
"iam:ListPolicyVersions",
|
|
"iam:ListRolePolicies",
|
|
"iam:ListRoleTags",
|
|
"iam:ListRoles",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "TagExecBoundary"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:GetPolicy",
|
|
"iam:GetPolicyVersion",
|
|
"iam:ListPolicyTags",
|
|
"iam:ListPolicyVersions",
|
|
"iam:TagPolicy",
|
|
"iam:UntagPolicy",
|
|
]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
|
|
]
|
|
}
|
|
|
|
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-*",
|
|
"arn:aws:iam::${local.account_id}:policy/tf-managed/*",
|
|
]
|
|
}
|
|
}
|
|
|
|
data "aws_iam_policy_document" "hcptf_apply_services" {
|
|
statement {
|
|
sid = "BackupAndArtifactBuckets"
|
|
effect = "Allow"
|
|
actions = ["s3:*"]
|
|
resources = [
|
|
"arn:aws:s3:::${local.backup_bucket_name}",
|
|
"arn:aws:s3:::${local.backup_bucket_name}/*",
|
|
"arn:aws:s3:::${local.replica_bucket_name}",
|
|
"arn:aws:s3:::${local.replica_bucket_name}/*",
|
|
"arn:aws:s3:::${local.artifacts_bucket_name}",
|
|
"arn:aws:s3:::${local.artifacts_bucket_name}/*",
|
|
]
|
|
}
|
|
|
|
# RunInstances and CreateVolume do not support a useful resource ARN.
|
|
# This document is a managed policy so it is not counted against the
|
|
# apply role's 10,240-character inline quota. The plan role does not get it.
|
|
statement {
|
|
sid = "Ec2Host"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ec2:AssociateIamInstanceProfile",
|
|
"ec2:AttachVolume",
|
|
"ec2:AuthorizeSecurityGroupEgress",
|
|
"ec2:AuthorizeSecurityGroupIngress",
|
|
"ec2:CreateSecurityGroup",
|
|
"ec2:CreateTags",
|
|
"ec2:CreateVolume",
|
|
"ec2:DeleteSecurityGroup",
|
|
"ec2:DeleteTags",
|
|
"ec2:DeleteVolume",
|
|
"ec2:DescribeAccountAttributes",
|
|
"ec2:DescribeAvailabilityZones",
|
|
"ec2:DescribeIamInstanceProfileAssociations",
|
|
"ec2:DescribeImages",
|
|
"ec2:DescribeInstanceAttribute",
|
|
"ec2:DescribeInstanceCreditSpecifications",
|
|
"ec2:DescribeInstanceStatus",
|
|
"ec2:DescribeInstanceTypes",
|
|
"ec2:DescribeInstances",
|
|
"ec2:DescribeNetworkInterfaces",
|
|
"ec2:DescribeSecurityGroupRules",
|
|
"ec2:DescribeSecurityGroups",
|
|
"ec2:DescribeSubnets",
|
|
"ec2:DescribeTags",
|
|
"ec2:DescribeVolumeAttribute",
|
|
"ec2:DescribeVolumeStatus",
|
|
"ec2:DescribeVolumes",
|
|
"ec2:DescribeVpcAttribute",
|
|
"ec2:DescribeVpcs",
|
|
"ec2:DetachVolume",
|
|
"ec2:DisassociateIamInstanceProfile",
|
|
"ec2:ModifyInstanceAttribute",
|
|
"ec2:ModifySecurityGroupRules",
|
|
"ec2:ModifyVolume",
|
|
"ec2:ReplaceIamInstanceProfileAssociation",
|
|
"ec2:RevokeSecurityGroupEgress",
|
|
"ec2:RevokeSecurityGroupIngress",
|
|
"ec2:RunInstances",
|
|
"ec2:StartInstances",
|
|
"ec2:StopInstances",
|
|
"ec2:TerminateInstances",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
# Listener and rule ARNs are allocated at create time.
|
|
statement {
|
|
sid = "LoadBalancer"
|
|
effect = "Allow"
|
|
actions = ["elasticloadbalancing:*"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "Certificate"
|
|
effect = "Allow"
|
|
actions = ["acm:*"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "Snapshots"
|
|
effect = "Allow"
|
|
actions = ["dlm:*"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "VerificationFunction"
|
|
effect = "Allow"
|
|
actions = ["lambda:*"]
|
|
resources = [
|
|
"arn:aws:lambda:${var.aws_region}:${local.account_id}:function:forgejo-backup-verification",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "VerificationSchedules"
|
|
effect = "Allow"
|
|
actions = ["events:*"]
|
|
resources = [
|
|
"arn:aws:events:${var.aws_region}:${local.account_id}:rule/forgejo-backup-*",
|
|
]
|
|
}
|
|
|
|
# CreateLogGroup is not reliable on the log-group ARN before the group exists.
|
|
# DescribeLogGroups is a list API. Its resource is log-group::log-stream:, not the group ARN.
|
|
statement {
|
|
sid = "CreateVerificationLogGroup"
|
|
effect = "Allow"
|
|
actions = ["logs:CreateLogGroup"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "DescribeVerificationLogGroups"
|
|
effect = "Allow"
|
|
actions = ["logs:DescribeLogGroups"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "VerificationLogs"
|
|
effect = "Allow"
|
|
actions = [
|
|
"logs:DeleteLogGroup",
|
|
"logs:DeleteRetentionPolicy",
|
|
"logs:DescribeLogGroups",
|
|
"logs:ListTagsForResource",
|
|
"logs:PutRetentionPolicy",
|
|
"logs:TagResource",
|
|
"logs:UntagResource",
|
|
]
|
|
resources = [
|
|
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.verification_log_group}",
|
|
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.verification_log_group}:*",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "VerificationAlarms"
|
|
effect = "Allow"
|
|
actions = [
|
|
"cloudwatch:DeleteAlarms",
|
|
"cloudwatch:DescribeAlarms",
|
|
"cloudwatch:ListTagsForResource",
|
|
"cloudwatch:PutMetricAlarm",
|
|
"cloudwatch:TagResource",
|
|
"cloudwatch:UntagResource",
|
|
]
|
|
resources = [
|
|
"arn:aws:cloudwatch:${var.aws_region}:${local.account_id}:alarm:forgejo-backup-*",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "DescribeAlarms"
|
|
effect = "Allow"
|
|
actions = ["cloudwatch:DescribeAlarms"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "BackupPrefixParameter"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ssm:AddTagsToResource",
|
|
"ssm:DeleteParameter",
|
|
"ssm:GetParameter",
|
|
"ssm:ListTagsForResource",
|
|
"ssm:PutParameter",
|
|
"ssm:RemoveTagsFromResource",
|
|
]
|
|
resources = [
|
|
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter/forgejo/*",
|
|
]
|
|
}
|
|
|
|
# DescribeParameters does not accept a parameter ARN. The provider calls it on *.
|
|
statement {
|
|
sid = "DescribeBackupParameters"
|
|
effect = "Allow"
|
|
actions = ["ssm:DescribeParameters"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "AlertTopicRead"
|
|
effect = "Allow"
|
|
actions = [
|
|
"sns:GetTopicAttributes",
|
|
"sns:ListTagsForResource",
|
|
]
|
|
resources = [local.site_alerts_arn]
|
|
}
|
|
|
|
statement {
|
|
sid = "EbsEncryption"
|
|
effect = "Allow"
|
|
actions = [
|
|
"kms:CreateGrant",
|
|
"kms:Decrypt",
|
|
"kms:DescribeKey",
|
|
"kms:GenerateDataKeyWithoutPlaintext",
|
|
"kms:ReEncryptFrom",
|
|
"kms:ReEncryptTo",
|
|
]
|
|
resources = ["*"]
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "kms:ViaService"
|
|
values = ["ec2.${var.aws_region}.amazonaws.com"]
|
|
}
|
|
}
|
|
}
|
|
|
|
data "aws_iam_policy_document" "hcptf_plan_refresh" {
|
|
statement {
|
|
sid = "RefreshIamRoles"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:GetInstanceProfile",
|
|
"iam:GetRole",
|
|
"iam:GetRolePolicy",
|
|
"iam:GetUser",
|
|
"iam:GetUserPolicy",
|
|
"iam:ListAttachedRolePolicies",
|
|
"iam:ListInstanceProfilesForRole",
|
|
"iam:ListRolePolicies",
|
|
"iam:ListRoleTags",
|
|
"iam:ListUserPolicies",
|
|
"iam:ListUserTags",
|
|
]
|
|
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}:user/tf-managed/${local.gcs_user_name}",
|
|
"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 = "RefreshS3"
|
|
effect = "Allow"
|
|
actions = [
|
|
"s3:GetAccelerateConfiguration",
|
|
"s3:GetBucketAcl",
|
|
"s3:GetBucketCORS",
|
|
"s3:GetBucketLocation",
|
|
"s3:GetBucketLogging",
|
|
"s3:GetBucketNotification",
|
|
"s3:GetBucketObjectLockConfiguration",
|
|
"s3:GetBucketOwnershipControls",
|
|
"s3:GetBucketPolicy",
|
|
"s3:GetBucketPolicyStatus",
|
|
"s3:GetBucketPublicAccessBlock",
|
|
"s3:GetBucketRequestPayment",
|
|
"s3:GetBucketTagging",
|
|
"s3:GetBucketVersioning",
|
|
"s3:GetBucketWebsite",
|
|
"s3:GetEncryptionConfiguration",
|
|
"s3:GetLifecycleConfiguration",
|
|
"s3:GetReplicationConfiguration",
|
|
"s3:ListBucket",
|
|
]
|
|
resources = [
|
|
"arn:aws:s3:::${local.backup_bucket_name}",
|
|
"arn:aws:s3:::${local.replica_bucket_name}",
|
|
"arn:aws:s3:::${local.artifacts_bucket_name}",
|
|
]
|
|
}
|
|
|
|
# aws_s3_object refresh calls HeadObject and object metadata reads. Scope
|
|
# them to the verification package. Backup and replica objects stay unread.
|
|
statement {
|
|
sid = "RefreshLambdaPackage"
|
|
effect = "Allow"
|
|
actions = [
|
|
"s3:GetObject",
|
|
"s3:GetObjectAcl",
|
|
"s3:GetObjectAttributes",
|
|
"s3:GetObjectTagging",
|
|
]
|
|
resources = [
|
|
"arn:aws:s3:::${local.artifacts_bucket_name}/functions/forgejo-backup-verification.zip",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshEc2"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ec2:DescribeAccountAttributes",
|
|
"ec2:DescribeAvailabilityZones",
|
|
"ec2:DescribeIamInstanceProfileAssociations",
|
|
"ec2:DescribeImages",
|
|
"ec2:DescribeInstanceAttribute",
|
|
"ec2:DescribeInstanceCreditSpecifications",
|
|
"ec2:DescribeInstanceStatus",
|
|
"ec2:DescribeInstanceTypes",
|
|
"ec2:DescribeInstances",
|
|
"ec2:DescribeNetworkInterfaces",
|
|
"ec2:DescribeSecurityGroupRules",
|
|
"ec2:DescribeSecurityGroups",
|
|
"ec2:DescribeSubnets",
|
|
"ec2:DescribeTags",
|
|
"ec2:DescribeVolumeAttribute",
|
|
"ec2:DescribeVolumeStatus",
|
|
"ec2:DescribeVolumes",
|
|
"ec2:DescribeVpcAttribute",
|
|
"ec2:DescribeVpcs",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshLoadBalancer"
|
|
effect = "Allow"
|
|
actions = [
|
|
"elasticloadbalancing:DescribeListenerAttributes",
|
|
"elasticloadbalancing:DescribeListeners",
|
|
"elasticloadbalancing:DescribeLoadBalancerAttributes",
|
|
"elasticloadbalancing:DescribeLoadBalancers",
|
|
"elasticloadbalancing:DescribeRules",
|
|
"elasticloadbalancing:DescribeTags",
|
|
"elasticloadbalancing:DescribeTargetGroupAttributes",
|
|
"elasticloadbalancing:DescribeTargetGroups",
|
|
"elasticloadbalancing:DescribeTargetHealth",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshCertificate"
|
|
effect = "Allow"
|
|
actions = [
|
|
"acm:DescribeCertificate",
|
|
"acm:ListCertificates",
|
|
"acm:ListTagsForCertificate",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshLambda"
|
|
effect = "Allow"
|
|
actions = [
|
|
"lambda:GetFunction",
|
|
"lambda:GetFunctionCodeSigningConfig",
|
|
"lambda:GetFunctionConfiguration",
|
|
"lambda:GetPolicy",
|
|
"lambda:GetRuntimeManagementConfig",
|
|
"lambda:ListTags",
|
|
"lambda:ListVersionsByFunction",
|
|
]
|
|
resources = [
|
|
"arn:aws:lambda:${var.aws_region}:${local.account_id}:function:forgejo-backup-verification",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshSchedules"
|
|
effect = "Allow"
|
|
actions = [
|
|
"events:DescribeRule",
|
|
"events:ListTagsForResource",
|
|
"events:ListTargetsByRule",
|
|
]
|
|
resources = [
|
|
"arn:aws:events:${var.aws_region}:${local.account_id}:rule/forgejo-backup-*",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshLogs"
|
|
effect = "Allow"
|
|
actions = [
|
|
"logs:ListTagsForResource",
|
|
]
|
|
resources = [
|
|
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.verification_log_group}",
|
|
"arn:aws:logs:${var.aws_region}:${local.account_id}:log-group:${local.verification_log_group}:*",
|
|
]
|
|
}
|
|
|
|
# DescribeLogGroups is a list API. Its resource is log-group::log-stream:, not the group ARN.
|
|
statement {
|
|
sid = "DescribeVerificationLogGroups"
|
|
effect = "Allow"
|
|
actions = ["logs:DescribeLogGroups"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshAlarms"
|
|
effect = "Allow"
|
|
actions = [
|
|
"cloudwatch:DescribeAlarms",
|
|
"cloudwatch:ListTagsForResource",
|
|
]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshParameter"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ssm:GetParameter",
|
|
"ssm:ListTagsForResource",
|
|
]
|
|
resources = [
|
|
"arn:aws:ssm:${var.aws_region}:${local.account_id}:parameter/forgejo/*",
|
|
]
|
|
}
|
|
|
|
statement {
|
|
sid = "DescribeBackupParameters"
|
|
effect = "Allow"
|
|
actions = ["ssm:DescribeParameters"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshSnapshots"
|
|
effect = "Allow"
|
|
actions = [
|
|
"dlm:GetLifecyclePolicy",
|
|
"dlm:ListTagsForResource",
|
|
]
|
|
resources = ["arn:aws:dlm:${var.aws_region}:${local.account_id}:policy/*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "RefreshSns"
|
|
effect = "Allow"
|
|
actions = [
|
|
"sns:GetTopicAttributes",
|
|
"sns:ListTagsForResource",
|
|
]
|
|
resources = [local.site_alerts_arn]
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
# Managed, not inline: the enumerated EC2 list plus scoped-iam-management
|
|
# exceeds the 10,240-character inline quota. Bootstrap creates this on the
|
|
# first apply. Later document edits need that window (CreatePolicyVersion
|
|
# is denied on hcptf-forgejo).
|
|
resource "aws_iam_policy" "hcptf_apply_services" {
|
|
name = "forgejo-services"
|
|
path = "/tf-managed/"
|
|
description = "Forgejo HCP apply service permissions (PLAT-80)."
|
|
policy = data.aws_iam_policy_document.hcptf_apply_services.json
|
|
}
|
|
|
|
resource "aws_iam_role_policy" "hcptf_plan_refresh" {
|
|
name = "forgejo-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 = [
|
|
aws_iam_policy.hcptf_apply_services.arn,
|
|
]
|
|
}
|
|
|
|
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",
|
|
]
|
|
}
|