Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ export GITHUB_REPOSITORY=OWNER/PRIVATE_REPOSITORY
scripts/setup-quickstart.sh
```

The script creates the AWS resources and runner image, configures the
repository, creates a dedicated IAM user, rotates its static access key directly
into GitHub Actions secrets, and prompts for the classic PAT. It does not use an
infrastructure framework or write the AWS secret access key to disk.

The Quickstart IAM user deliberately includes bootstrap, image build, and runner
lifecycle permissions. Use it only with private repositories and trusted
workflow changes. See [advanced credentials](docs/advanced-credentials.md) to
replace both long-lived credentials with GitHub OIDC and a GitHub App.
The script uses your existing local AWS credentials to create the AWS resources,
runner image, roles, and a dedicated IAM user. It rotates that user's static
access key directly into GitHub Actions secrets and prompts for the classic PAT.
It does not use an infrastructure framework or write the AWS secret access key
to disk.

The stored IAM user is restricted to image building and runner lifecycle
operations. It cannot create or modify IAM identities, roles, policies, OIDC
providers, buckets, or log groups. Use it only with private repositories and
trusted workflow changes. See
[advanced credentials](docs/advanced-credentials.md) to replace both long-lived
credentials with GitHub OIDC and a GitHub App.

## Usage

Expand Down
12 changes: 7 additions & 5 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ GH_PERSONAL_ACCESS_TOKEN=TOKEN scripts/setup-quickstart.sh

The script:

1. creates or reconciles the S3 bucket, CloudWatch log groups, and image build
and runtime IAM roles;
1. uses the active local AWS credentials to create or reconcile the S3 bucket,
CloudWatch log groups, and image build and runtime IAM roles;
2. packages, uploads, validates, and activates the runner image;
3. configures the repository variables;
4. creates a dedicated `lambda-microvm-github-runner-quickstart` IAM user;
5. grants that user bootstrap, image build, and runner lifecycle permissions;
5. grants that user only image build and runner lifecycle permissions;
6. rotates its access key directly into the `AWS_ACCESS_KEY_ID` and
`AWS_SECRET_ACCESS_KEY` GitHub Actions secrets;
7. sets the PAT as `GH_PERSONAL_ACCESS_TOKEN`.
Expand All @@ -46,8 +46,10 @@ The secret access key is never written to the setup output or printed.
Re-running the script reconciles resources, builds a new image version, and
rotates the dedicated access key.

> **Quickstart security boundary:** These are long-lived credentials with broad
> product permissions. Use them only in private repositories where workflow
> **Quickstart security boundary:** The local credentials perform privileged
> setup. The stored long-lived credentials cannot mutate IAM resources and are
> limited to the configured image artifacts, exact build/runtime roles, and
> Lambda MicroVM lifecycle. Use them only in private repositories where workflow
> changes are trusted. Never expose them to untrusted `pull_request_target`
> workflows.

Expand Down
4 changes: 4 additions & 0 deletions docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export GITHUB_REPOSITORY=OWNER/REPOSITORY
scripts/configure-quickstart-credentials.sh
```

Run it with local AWS credentials allowed to manage that IAM user, its inline
policy, and its access keys. The stored Quickstart credentials intentionally
cannot rotate themselves or change IAM policy.

The helper installs the new secret pair before deleting the previous key. Rotate
the classic PAT separately with:

Expand Down
12 changes: 8 additions & 4 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ and trusted workflow changes only. Public fork pull requests are unsupported.

## Credentials

- Quickstart stores a classic PAT with `repo` scope and a dedicated IAM user's
access key as GitHub Actions secrets. The IAM user can reconcile this
product's bootstrap resources, build images, and manage runner MicroVMs.
- Quickstart uses the operator's active local AWS credentials to create or
reconcile IAM roles, the IAM user, S3, log groups, and other bootstrap
resources.
- Quickstart stores a classic PAT with `repo` scope and the dedicated IAM user's
access key as GitHub Actions secrets. That IAM user can use the configured
image bucket, pass only the exact build/runtime roles, build images, and
manage runner MicroVMs. It cannot create or modify IAM resources.
- Quickstart is limited to private repositories with trusted workflow changes.
Rotate or delete both credentials when they are no longer needed.
- Advanced setup uses a short-lived GitHub App installation token and obtains
Expand All @@ -24,7 +28,7 @@ and trusted workflow changes only. Public fork pull requests are unsupported.
`lambda:TerminateMicrovm`, because that API does not expose a per-instance IAM
resource ARN. No other Lambda or application action is granted by it.
- Deployment jobs should use a separate identity and must not inherit the
Quickstart IAM user's bootstrap permissions.
Quickstart IAM user's image-build or runner-lifecycle permissions.

The classic PAT, AWS secret access key, and GitHub App private key never enter
the MicroVM.
Expand Down
87 changes: 2 additions & 85 deletions scripts/configure-quickstart-credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ BUILD_ROLE_ARN="$(jq -er '.buildRoleArn' "${SETUP_FILE}")"
readonly BUILD_ROLE_ARN
EXECUTION_ROLE_ARN="$(jq -er '.executionRoleArn' "${SETUP_FILE}")"
readonly EXECUTION_ROLE_ARN
GITHUB_ROLE_ARN="$(jq -er '.githubLaunchRoleArn' "${SETUP_FILE}")"
readonly GITHUB_ROLE_ARN
BUILD_LOG_GROUP="$(jq -er '.buildLogGroup' "${SETUP_FILE}")"
readonly BUILD_LOG_GROUP
RUNTIME_LOG_GROUP="$(jq -er '.runtimeLogGroup' "${SETUP_FILE}")"
readonly RUNTIME_LOG_GROUP

export AWS_MAX_ATTEMPTS=6
export AWS_RETRY_MODE=standard
Expand All @@ -68,12 +62,7 @@ partition="${partition%%:*}"
readonly partition

readonly USER_ARN="arn:${partition}:iam::${account_id}:user/${USER_NAME}"
readonly OIDC_PROVIDER_ARN="arn:${partition}:iam::${account_id}:oidc-provider/token.actions.githubusercontent.com"
readonly BUCKET_ARN="arn:${partition}:s3:::${ARTIFACT_BUCKET}"
readonly BUILD_LOG_ARN="arn:${partition}:logs:${REGION}:${account_id}:log-group:${BUILD_LOG_GROUP}:*"
readonly RUNTIME_LOG_ARN="arn:${partition}:logs:${REGION}:${account_id}:log-group:${RUNTIME_LOG_GROUP}:*"
readonly BUILD_LOG_GROUP_ARN="arn:${partition}:logs:${REGION}:${account_id}:log-group:${BUILD_LOG_GROUP}"
readonly RUNTIME_LOG_GROUP_ARN="arn:${partition}:logs:${REGION}:${account_id}:log-group:${RUNTIME_LOG_GROUP}"
readonly INTERNET_EGRESS_ARN="arn:${partition}:lambda:${REGION}:aws:network-connector:aws-network-connector:INTERNET_EGRESS"
readonly NO_INGRESS_ARN="arn:${partition}:lambda:${REGION}:aws:network-connector:aws-network-connector:NO_INGRESS"

Expand All @@ -84,16 +73,9 @@ cleanup() {
trap cleanup EXIT

jq -n \
--arg userArn "${USER_ARN}" \
--arg bucketArn "${BUCKET_ARN}" \
--arg buildRoleArn "${BUILD_ROLE_ARN}" \
--arg executionRoleArn "${EXECUTION_ROLE_ARN}" \
--arg githubRoleArn "${GITHUB_ROLE_ARN}" \
--arg oidcProviderArn "${OIDC_PROVIDER_ARN}" \
--arg buildLogArn "${BUILD_LOG_ARN}" \
--arg runtimeLogArn "${RUNTIME_LOG_ARN}" \
--arg buildLogGroupArn "${BUILD_LOG_GROUP_ARN}" \
--arg runtimeLogGroupArn "${RUNTIME_LOG_GROUP_ARN}" \
--arg internetEgressArn "${INTERNET_EGRESS_ARN}" \
--arg noIngressArn "${NO_INGRESS_ARN}" \
'{
Expand All @@ -106,16 +88,11 @@ jq -n \
Resource: "*"
},
{
Sid: "ManageArtifactBucket",
Sid: "UseArtifactBucket",
Effect: "Allow",
Action: [
"s3:CreateBucket",
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:PutBucketEncryption",
"s3:PutBucketPublicAccessBlock",
"s3:PutBucketTagging",
"s3:PutBucketVersioning"
"s3:ListBucket"
],
Resource: $bucketArn
},
Expand All @@ -130,72 +107,12 @@ jq -n \
],
Resource: ($bucketArn + "/*")
},
{
Sid: "DiscoverLogGroups",
Effect: "Allow",
Action: "logs:DescribeLogGroups",
Resource: "*"
},
{
Sid: "ManageProjectLogs",
Effect: "Allow",
Action: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:PutRetentionPolicy",
"logs:TagResource"
],
Resource: [
$buildLogGroupArn,
$runtimeLogGroupArn,
$buildLogArn,
$runtimeLogArn
]
},
{
Sid: "ManageProjectRoles",
Effect: "Allow",
Action: [
"iam:CreateRole",
"iam:GetRole",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UpdateAssumeRolePolicy"
],
Resource: [$buildRoleArn, $executionRoleArn, $githubRoleArn]
},
{
Sid: "PassProjectRoles",
Effect: "Allow",
Action: "iam:PassRole",
Resource: [$buildRoleArn, $executionRoleArn]
},
{
Sid: "ManageGitHubOidcProvider",
Effect: "Allow",
Action: [
"iam:AddClientIDToOpenIDConnectProvider",
"iam:CreateOpenIDConnectProvider",
"iam:GetOpenIDConnectProvider",
"iam:TagOpenIDConnectProvider"
],
Resource: $oidcProviderArn
},
{
Sid: "ManageOwnQuickstartCredentials",
Effect: "Allow",
Action: [
"iam:CreateAccessKey",
"iam:CreateUser",
"iam:DeleteAccessKey",
"iam:GetUser",
"iam:ListAccessKeys",
"iam:PutUserPolicy",
"iam:TagUser"
],
Resource: $userArn
},
{
Sid: "ManageLambdaMicrovms",
Effect: "Allow",
Expand Down
23 changes: 11 additions & 12 deletions test/scripts/quickstart-credentials.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,17 @@ run_case() {
"arn:aws:iam::123456789012:user/runner-quickstart"
' "${case_directory}/setup.json" >/dev/null
jq -e '
any(.Statement[];
(.Action | type) == "array" and
(.Action | index("lambda:*Microvm*")) != null
) and
any(.Statement[];
(.Action | type) == "array" and
(.Action | index("iam:CreateRole")) != null
) and
any(.Statement[];
(.Action | type) == "array" and
(.Action | index("s3:PutObject")) != null
)
[.Statement[].Action] | flatten | . as $actions |
($actions | index("lambda:*Microvm*")) != null and
($actions | index("iam:PassRole")) != null and
($actions | index("s3:PutObject")) != null and
($actions | index("iam:CreateRole")) == null and
($actions | index("iam:PutRolePolicy")) == null and
($actions | index("iam:CreateUser")) == null and
($actions | index("iam:CreateAccessKey")) == null and
($actions | index("iam:CreateOpenIDConnectProvider")) == null and
($actions | index("s3:CreateBucket")) == null and
([$actions[] | select(startswith("logs:"))] | length) == 0
' "${case_directory}/policy.json" >/dev/null
grep -q "secret set.*--env-file -" \
"${case_directory}/gh.log"
Expand Down
Loading