4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
287 lines
9.9 KiB
Terraform
287 lines
9.9 KiB
Terraform
# GitHub Actions OIDC trust — lets CI assume an AWS role without long-lived
|
|
# access keys. The role grants only the operations needed to launch the bench
|
|
# task and fetch its results.
|
|
#
|
|
# Trust policy is scoped to var.github_repository. Tighten the `sub` condition
|
|
# below if you want to restrict by branch / environment (recommended for
|
|
# production runs). For v1 we accept any ref/branch from the repo.
|
|
|
|
# OIDC provider — one per AWS account. If it already exists, import it:
|
|
# terraform import aws_iam_openid_connect_provider.github \
|
|
# arn:aws:iam::<acct>:oidc-provider/token.actions.githubusercontent.com
|
|
resource "aws_iam_openid_connect_provider" "github" {
|
|
url = "https://token.actions.githubusercontent.com"
|
|
client_id_list = ["sts.amazonaws.com"]
|
|
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
|
|
}
|
|
|
|
data "aws_iam_policy_document" "github_actions_trust" {
|
|
statement {
|
|
effect = "Allow"
|
|
actions = ["sts:AssumeRoleWithWebIdentity"]
|
|
|
|
principals {
|
|
type = "Federated"
|
|
identifiers = [aws_iam_openid_connect_provider.github.arn]
|
|
}
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "token.actions.githubusercontent.com:aud"
|
|
values = ["sts.amazonaws.com"]
|
|
}
|
|
|
|
condition {
|
|
test = "StringLike"
|
|
variable = "token.actions.githubusercontent.com:sub"
|
|
values = ["repo:${var.github_repository}:*"]
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "aws_iam_role" "github_actions" {
|
|
name = "${local.name_prefix}-github-actions"
|
|
description = "Assumed by GitHub Actions in ${var.github_repository} to launch bench tasks AND seed secret values."
|
|
assume_role_policy = data.aws_iam_policy_document.github_actions_trust.json
|
|
}
|
|
|
|
# Permissions the CI workflow needs:
|
|
# - Launch the bench Fargate task
|
|
# - Read its status (poll until done)
|
|
# - Pass the task + execution roles to ECS (RunTask requires PassRole)
|
|
# - Read results from S3 (artifact upload)
|
|
# - Read CloudWatch logs (tail during run)
|
|
data "aws_iam_policy_document" "github_actions_run_bench" {
|
|
statement {
|
|
sid = "RunBenchTask"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ecs:RunTask",
|
|
"ecs:DescribeTasks",
|
|
"ecs:StopTask",
|
|
"ecs:ListTasks",
|
|
]
|
|
resources = ["*"]
|
|
condition {
|
|
test = "ArnEquals"
|
|
variable = "ecs:cluster"
|
|
values = [aws_ecs_cluster.bench.arn]
|
|
}
|
|
}
|
|
|
|
statement {
|
|
sid = "DescribeTaskDefinition"
|
|
effect = "Allow"
|
|
actions = ["ecs:DescribeTaskDefinition"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "PassRolesToEcs"
|
|
effect = "Allow"
|
|
actions = ["iam:PassRole"]
|
|
resources = [aws_iam_role.task.arn, aws_iam_role.execution.arn]
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "iam:PassedToService"
|
|
values = ["ecs-tasks.amazonaws.com"]
|
|
}
|
|
}
|
|
|
|
statement {
|
|
sid = "ReadResults"
|
|
effect = "Allow"
|
|
actions = ["s3:GetObject", "s3:ListBucket"]
|
|
resources = [aws_s3_bucket.results.arn, "${aws_s3_bucket.results.arn}/*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "ReadLogs"
|
|
effect = "Allow"
|
|
actions = [
|
|
"logs:GetLogEvents",
|
|
"logs:DescribeLogStreams",
|
|
"logs:FilterLogEvents",
|
|
]
|
|
resources = ["${aws_cloudwatch_log_group.bench.arn}:*"]
|
|
}
|
|
|
|
statement {
|
|
sid = "SeedSecretValues"
|
|
effect = "Allow"
|
|
actions = [
|
|
"secretsmanager:PutSecretValue",
|
|
"secretsmanager:DescribeSecret",
|
|
]
|
|
# Per-ARN - workflow can seed only these four LLM-key secrets.
|
|
# No GetSecretValue here (the seed workflow writes values; it never
|
|
# reads them back).
|
|
resources = [
|
|
aws_secretsmanager_secret.anthropic_api_key.arn,
|
|
aws_secretsmanager_secret.openai_api_key.arn,
|
|
aws_secretsmanager_secret.deepseek_api_key.arn,
|
|
aws_secretsmanager_secret.hf_token.arn,
|
|
]
|
|
}
|
|
|
|
# ECR authorization. GetAuthorizationToken does not accept a resource ARN
|
|
# (must be "*") - tfsec flags this; suppression in .tfsec/config.yml
|
|
# under aws-iam-no-policy-wildcards already covers IAM patterns that
|
|
# cannot be ARN-scoped.
|
|
statement {
|
|
sid = "EcrAuth"
|
|
effect = "Allow"
|
|
actions = ["ecr:GetAuthorizationToken"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
# ECR push - scoped to the bench repo only.
|
|
statement {
|
|
sid = "EcrPushBenchImage"
|
|
effect = "Allow"
|
|
actions = [
|
|
"ecr:BatchCheckLayerAvailability",
|
|
"ecr:InitiateLayerUpload",
|
|
"ecr:UploadLayerPart",
|
|
"ecr:CompleteLayerUpload",
|
|
"ecr:PutImage",
|
|
"ecr:BatchGetImage",
|
|
"ecr:DescribeImages",
|
|
"ecr:DescribeRepositories",
|
|
]
|
|
resources = [aws_ecr_repository.bench.arn]
|
|
}
|
|
|
|
# RegisterTaskDefinition - required by benchmark-promote-image.yml to
|
|
# register a new task definition revision pointing at the chosen ECR tag.
|
|
# AWS does not support resource-level permissions for RegisterTaskDefinition,
|
|
# so it must be "*"; tfsec aws-iam-no-policy-wildcards suppression covers
|
|
# this pattern.
|
|
statement {
|
|
sid = "RegisterTaskDefinition"
|
|
effect = "Allow"
|
|
actions = ["ecs:RegisterTaskDefinition"]
|
|
resources = ["*"]
|
|
}
|
|
|
|
# Tag the task definition on Register. The AWS provider's default_tags
|
|
# block in providers.tf attaches tags to every taggable resource, so
|
|
# RegisterTaskDefinition implicitly calls ecs:TagResource. Scoped to the
|
|
# bench family. (No UntagResource: skip_destroy = true on the resource
|
|
# means Terraform never deregisters / untags old revisions.)
|
|
statement {
|
|
sid = "TagBenchTaskDefinition"
|
|
effect = "Allow"
|
|
actions = ["ecs:TagResource"]
|
|
resources = ["arn:aws:ecs:${var.region}:${data.aws_caller_identity.current.account_id}:task-definition/${local.name_prefix}:*"]
|
|
}
|
|
|
|
# Terraform state bucket - read+write on the opensre-bench/ prefix only,
|
|
# so this role can run `terraform apply` from the promote-image workflow.
|
|
# ListBucket is scoped to the bucket but conditioned on s3:prefix to
|
|
# prevent enumerating other modules' state keys; object actions are
|
|
# scoped to the prefix directly.
|
|
statement {
|
|
sid = "TerraformStateBucketList"
|
|
effect = "Allow"
|
|
actions = ["s3:ListBucket"]
|
|
resources = ["arn:aws:s3:::tracer-cloud-tfstate-${data.aws_caller_identity.current.account_id}"]
|
|
condition {
|
|
test = "StringLike"
|
|
variable = "s3:prefix"
|
|
values = ["opensre-bench/*"]
|
|
}
|
|
}
|
|
|
|
statement {
|
|
sid = "TerraformStateObject"
|
|
effect = "Allow"
|
|
actions = [
|
|
"s3:GetObject",
|
|
"s3:PutObject",
|
|
"s3:DeleteObject",
|
|
]
|
|
resources = ["arn:aws:s3:::tracer-cloud-tfstate-${data.aws_caller_identity.current.account_id}/opensre-bench/*"]
|
|
}
|
|
|
|
# Terraform state lock table - terraform apply takes/releases a lock on
|
|
# every run. Scoped to the single tflock table.
|
|
statement {
|
|
sid = "TerraformStateLock"
|
|
effect = "Allow"
|
|
actions = [
|
|
"dynamodb:GetItem",
|
|
"dynamodb:PutItem",
|
|
"dynamodb:DeleteItem",
|
|
]
|
|
resources = ["arn:aws:dynamodb:${var.region}:${data.aws_caller_identity.current.account_id}:table/tracer-cloud-tflock"]
|
|
}
|
|
}
|
|
|
|
resource "aws_iam_role_policy" "github_actions_run_bench" {
|
|
name = "run-bench"
|
|
role = aws_iam_role.github_actions.id
|
|
policy = data.aws_iam_policy_document.github_actions_run_bench.json
|
|
}
|
|
|
|
# Broad read across all services this module touches — needed because
|
|
# benchmark-promote-image.yml runs `terraform apply`, and apply's refresh
|
|
# phase reads every resource currently in state to compute the diff.
|
|
# ReadOnlyAccess does NOT include kms:Decrypt, so secret values remain
|
|
# protected (GetSecretValue returns encrypted blobs that this role cannot
|
|
# decrypt).
|
|
resource "aws_iam_role_policy_attachment" "github_actions_readonly" {
|
|
role = aws_iam_role.github_actions.name
|
|
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Second OIDC-assumed role: terraform-plan #
|
|
# #
|
|
# Used by the terraform-bench.yml workflow on PRs to run `terraform plan`. #
|
|
# Separate from `github_actions` so PR plan-only runs don't carry the write #
|
|
# permissions that the promote/run workflows need. Both roles get #
|
|
# AWS-managed `ReadOnlyAccess` because plan and apply both need broad read #
|
|
# across every resource type in this module (ECS, ECR, IAM, S3, CloudWatch). #
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
resource "aws_iam_role" "terraform_plan" {
|
|
name = "${local.name_prefix}-terraform-plan"
|
|
description = "Assumed by GitHub Actions on PRs to run terraform plan against ${local.name_prefix} state."
|
|
assume_role_policy = data.aws_iam_policy_document.github_actions_trust.json
|
|
}
|
|
|
|
# Broad read across all services this module touches — needed for plan diff.
|
|
resource "aws_iam_role_policy_attachment" "terraform_plan_readonly" {
|
|
role = aws_iam_role.terraform_plan.name
|
|
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
|
|
}
|
|
|
|
# Explicit state-bucket read (ListBucket gives clean 404 instead of 403 on
|
|
# missing state file; GetObject reads the state itself).
|
|
data "aws_iam_policy_document" "terraform_plan_state_read" {
|
|
statement {
|
|
sid = "ListStateBucket"
|
|
effect = "Allow"
|
|
actions = ["s3:ListBucket"]
|
|
resources = ["arn:aws:s3:::tracer-cloud-tfstate-${data.aws_caller_identity.current.account_id}"]
|
|
}
|
|
|
|
statement {
|
|
sid = "ReadStateObject"
|
|
effect = "Allow"
|
|
actions = ["s3:GetObject"]
|
|
resources = ["arn:aws:s3:::tracer-cloud-tfstate-${data.aws_caller_identity.current.account_id}/opensre-bench/*"]
|
|
}
|
|
}
|
|
|
|
resource "aws_iam_role_policy" "terraform_plan_state_read" {
|
|
name = "state-read"
|
|
role = aws_iam_role.terraform_plan.id
|
|
policy = data.aws_iam_policy_document.terraform_plan_state_read.json
|
|
}
|
|
|
|
# Bring back the data source dropped earlier — needed for the state bucket
|
|
# ARN computed from account id.
|
|
data "aws_caller_identity" "current" {}
|