diff --git a/README.md b/README.md index 4afa6ae..867cc3a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ No modules. | [aws_iam_role.github_actions_channel_project_state](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.github_actions_sops_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.github_actions_twilio_state](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.github_actions_xnoto_dev_site_deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.opencode_mcp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.bedrock_batch_invoke](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.bedrock_batch_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | @@ -43,6 +44,7 @@ No modules. | [aws_iam_role_policy.github_actions_channel_project_state](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | 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_role_policy.github_actions_twilio_state](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.github_actions_xnoto_dev_site_deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.opencode_mcp_agent_pipe](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.opencode_mcp_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy_attachment.opencode_mcp_oauth](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | @@ -111,6 +113,7 @@ No inputs. | [twilio\_github\_actions\_state\_role\_arn](#output\_twilio\_github\_actions\_state\_role\_arn) | GitHub OIDC role for tfroot-twilio OpenTofu state and SOPS decrypt access | | [twilio\_state\_bucket\_name](#output\_twilio\_state\_bucket\_name) | Versioned private S3 bucket for tfroot-twilio OpenTofu state | | [web\_bucket\_endpoints](#output\_web\_bucket\_endpoints) | Website endpoints for public web S3 buckets | +| [xnoto\_dev\_site\_deploy\_role\_arn](#output\_xnoto\_dev\_site\_deploy\_role\_arn) | GitHub OIDC role for publishing xnoto.dev static-site assets from main | ## AWS Bedrock batch inference (50% discount) diff --git a/aws-github-oidc.tf b/aws-github-oidc.tf index c793498..cb73207 100644 --- a/aws-github-oidc.tf +++ b/aws-github-oidc.tf @@ -406,3 +406,63 @@ resource "aws_iam_role_policy" "github_actions_channel_project_site_deploy" { ] }) } + +# Site publication is separate from infrastructure management and is trusted +# only from xnoto.dev's main branch. It can publish assets but cannot manage +# state, infrastructure resources, or CloudFront distributions. +resource "aws_iam_role" "github_actions_xnoto_dev_site_deploy" { + name = "github-actions-xnoto-dev-site-deploy" + + 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" + "token.actions.githubusercontent.com:sub" = "repo:xnoto@121333299/xnoto.dev@1358381234:ref:refs/heads/main" + } + } + } + ] + }) + + tags = { + ManagedBy = "Terraform" + Purpose = "xnoto-dev-site-deployment" + } +} + +resource "aws_iam_role_policy" "github_actions_xnoto_dev_site_deploy" { + name = "xnoto-dev-site-deployment" + role = aws_iam_role.github_actions_xnoto_dev_site_deploy.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "ListSiteAssetBucket" + Effect = "Allow" + Action = ["s3:ListBucket"] + Resource = "arn:aws:s3:::xnoto.dev" + }, + { + Sid = "PublishSiteAssets" + Effect = "Allow" + Action = [ + "s3:AbortMultipartUpload", + "s3:DeleteObject", + "s3:GetObject", + "s3:ListMultipartUploadParts", + "s3:PutObject" + ] + Resource = "arn:aws:s3:::xnoto.dev/*" + } + ] + }) +} diff --git a/main.tf b/main.tf index 23d5a13..581ec3e 100644 --- a/main.tf +++ b/main.tf @@ -30,5 +30,6 @@ locals { "makeitwork.cloud", "onion.makeitwork.cloud", "orthodox.channel", + "xnoto.dev", ]) } diff --git a/outputs.tf b/outputs.tf index 7d97b37..2e69e88 100644 --- a/outputs.tf +++ b/outputs.tf @@ -54,6 +54,11 @@ output "channel_project_site_deploy_role_arn" { value = aws_iam_role.github_actions_channel_project_site_deploy.arn } +output "xnoto_dev_site_deploy_role_arn" { + description = "GitHub OIDC role for publishing xnoto.dev static-site assets from main" + value = aws_iam_role.github_actions_xnoto_dev_site_deploy.arn +} + output "twilio_state_bucket_name" { description = "Versioned private S3 bucket for tfroot-twilio OpenTofu state" value = aws_s3_bucket.private[local.twilio_state_bucket].bucket