diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index 91f72ff..80400d0 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -10,6 +10,7 @@ on: permissions: contents: read + id-token: write pull-requests: write jobs: diff --git a/.sops.yaml b/.sops.yaml index 8967c45..44d4ef5 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,3 +1,4 @@ --- creation_rules: - - age: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l + - kms: arn:aws:kms:us-west-2:332355796717:key/0a45c0f6-71dc-4d54-ab33-9df4de1a9e91 + age: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l diff --git a/README.md b/README.md index 66d3d21..00ae4f7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ No modules. | Name | Type | | ---- | ---- | | [aws_iam_access_key.admin_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | +| [aws_iam_openid_connect_provider.github_actions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | +| [aws_iam_role.github_actions_sops_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.github_actions_sops_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_user.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | | [aws_iam_user_policy_attachment.admin_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy_attachment) | resource | | [aws_kms_alias.sops](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | @@ -45,6 +48,7 @@ No inputs. | Name | Description | | ---- | ----------- | | [admin\_access\_keys](#output\_admin\_access\_keys) | Admin IAM user access keys | +| [github\_actions\_sops\_kms\_role\_arn](#output\_github\_actions\_sops\_kms\_role\_arn) | IAM role ARN for GitHub Actions SOPS KMS access | | [sops\_kms\_key\_arn](#output\_sops\_kms\_key\_arn) | KMS key ARN for future SOPS AWS KMS recipients | | [web\_bucket\_endpoints](#output\_web\_bucket\_endpoints) | Website endpoints for public web S3 buckets | diff --git a/aws-github-oidc.tf b/aws-github-oidc.tf new file mode 100644 index 0000000..84a12f5 --- /dev/null +++ b/aws-github-oidc.tf @@ -0,0 +1,71 @@ +resource "aws_iam_openid_connect_provider" "github_actions" { + url = "https://token.actions.githubusercontent.com" + + client_id_list = [ + "sts.amazonaws.com" + ] + + thumbprint_list = [ + "6938fd4d98bab03faadb97b34396831e3780aea1" + ] + + tags = { + ManagedBy = "Terraform" + } +} + +resource "aws_iam_role" "github_actions_sops_kms" { + name = "github-actions-sops-kms" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Federated = aws_iam_openid_connect_provider.github_actions.arn + } + Action = "sts:AssumeRoleWithWebIdentity" + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" + } + StringLike = { + "token.actions.githubusercontent.com:sub" = [ + "repo:makeitworkcloud/tfroot-aws:*", + "repo:makeitworkcloud/tfroot-cloudflare:*", + "repo:makeitworkcloud/tfroot-github:*", + "repo:makeitworkcloud/tfroot-libvirt:*" + ] + } + } + } + ] + }) + + tags = { + ManagedBy = "Terraform" + } +} + +resource "aws_iam_role_policy" "github_actions_sops_kms" { + name = "sops-kms" + role = aws_iam_role.github_actions_sops_kms.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ] + Resource = aws_kms_key.sops.arn + } + ] + }) +} diff --git a/outputs.tf b/outputs.tf index 5b710fb..e0ab005 100644 --- a/outputs.tf +++ b/outputs.tf @@ -19,3 +19,8 @@ output "sops_kms_key_arn" { description = "KMS key ARN for future SOPS AWS KMS recipients" value = aws_kms_key.sops.arn } + +output "github_actions_sops_kms_role_arn" { + description = "IAM role ARN for GitHub Actions SOPS KMS access" + value = aws_iam_role.github_actions_sops_kms.arn +} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index 1a3d38e..1355baa 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -4,15 +4,20 @@ s3_region: ENC[AES256_GCM,data:eOyGm9ay1WsF,iv:wDUtASNnplZ76JJh54xlKKcXNKAtosepQ s3_access_key: ENC[AES256_GCM,data:wQL1zGnkAtWM1EWgooXMMq5dRHI=,iv:wmRjCt28jloov7zKNs4TaZ9GeKhO6/nqPqY1sxFmaQ4=,tag:ZDIeBorMgwkVrdv80SYiGw==,type:str] s3_secret_key: ENC[AES256_GCM,data:0EcLqSBTSCQ83LJ1RIuPjv7jzBm4KDeJVyUnGFVzxsCSCo1d+no7lQ==,iv:TuG8ATqDozZ5TsuBmHPNQC7pg1GXfrHgWxDIpjbFsWQ=,tag:46XhMpFqfNoPkteVcFZDeQ==,type:str] sops: + kms: + - arn: arn:aws:kms:us-west-2:332355796717:key/0a45c0f6-71dc-4d54-ab33-9df4de1a9e91 + created_at: "2026-06-19T03:45:00Z" + enc: AQICAHj1IggLFhM4nJnKEvmbEpk5E9RxZZoxpZYUW0taoyrz1AGxCPeJfC+xwLXrjLnKdN4HAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMo4N0BsmO7344VxgjAgEQgDuroFMxzwYxFsG1vHaITeOI1tFQ1iC2KC9RZRe/Mu3rPA3u0A25tpqYXtpi7CBcUPgCMfg4gsWfY68pDQ== + aws_profile: "" age: - recipient: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqa1V4WGpuWGVlY2xMcVk5 - QlJFeHk3ZjdrcVpnRDFRSmlRQ2FTZnE4WTFRCjQ5UmRONDV3a2xTc3Mya0wvekN6 - RmJrQ09nZzk4VkZxRGlremcrUWU1Z2MKLS0tIHpFZFVpV3lpOUJNSDFwdDRpazJK - d2pVcDh3cDNqY1gzSVR5Z2NXcld1Qm8KRdv8vKhMBi1R8fGIphdmY4pfHV1sAqSb - nAXWA6Ut5/KAPIluSnBtWFkcakulcXYT01XorziztVS0X4nJDzEvMg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMZ0xJeTIwSnZ4UzlKSDQ5 + M1VQSjJaOGZ1aFNUN2l5Y1l4L1dnbUtGUGhRCnhsWi9pV21WdGNNVERaQXFaVzR6 + TzhUWHllUUpQeTRaU2R3eU92S0hDVmcKLS0tIHhZeFZDRHdlU3Z2WERtdHJXM1dJ + MjBPNmJWWWpxY3pRd25IWldvUy9wS2sKHovTPwKIU1gsamMMlGxSvCiO6EwJ6pNO + UZF83FI1e+oEenynCmziC3BFaA0tpcYj12pEuiZAAroUPurEmJ+D0Q== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-09-27T23:45:19Z" mac: ENC[AES256_GCM,data:AcSRYq32GptizEXMujOIh4cC29BVNQj4lad85BWfHl6TGQA7oWN5A32xoXqCWqpWEuVDpndUAPZXvMRK8n53djXEXb/A6G9R8vEzaKOUB9zijItanUXkVK63d7KE9X8JrgtV7KQcWZx01qQitMzezMTkouvkfYpodPr1hkV2PIk=,iv:o5Q3VQG55jeCVHTNAAV1HgN91SU6jbx+IFZVGx2vN7o=,tag:GX/v69ggBNUpnLGFPBINdg==,type:str]