Join our Newsletter — 33% off our NHI Course
Home› Guides› AWS CodeBuild and CodePipeline Security: Lock down roles,…
Guide Software & AI Supply Chain

AWS CodeBuild and CodePipeline Security: Lock down roles, webhooks and build secrets

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 8 November 2026 15 min read
On this page

An AWS CodeBuild project runs whatever code its trigger lets in, with the full permissions of its IAM service role, so a loose webhook filter or an over-broad role turns a pull request into a route into your AWS account. Webhook filters, the service role and build secrets are customer-side settings, and AWS documents how to configure each one. This guide explains how identity works across CodeBuild and AWS CodePipeline, then sets out a hardening baseline: service role scoping, webhook actor and file path filters, pull request comment approval, privileged mode, secrets from Parameter Store and Secrets Manager, and manual approval actions. It is written for platform engineers, cloud security teams and anyone who owns AWS native CI/CD. For the wider discipline, see the CI/CD Security Guide.

This is an independent guide from NHI Mgmt Group, based on AWS's public documentation as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.

Key takeaways

  • The service role is the blast radius. Every build, including one triggered by a webhook, runs with the project's IAM service role, so scope it to named resources and split untrusted test builds from deploy builds.
  • Anchor every actor filter. ACTOR_ACCOUNT_ID filters are regular expressions; an unanchored pattern can match IDs you never meant to trust, so anchor each one with ^ and $.
  • Keep pull request comment approval on. New projects default to requiring comment approval for all pull requests from users outside the approver roles; do not switch it to DISABLED on a project that builds public code.
  • Secrets belong in Parameter Store or Secrets Manager. Plaintext environment variables are visible in the console and CLI, and CodePipeline action configuration values appear in logs.
  • Manual approvals are an IAM control. Scope codepipeline:PutApprovalResult to the specific pipeline, stage and action ARN instead of granting the account-wide approver managed policy.

How identity and access work in AWS CodeBuild and CodePipeline

Three kinds of identity meet in an AWS native pipeline, and most misconfigurations come from confusing them. Humans and automation call the CodeBuild and CodePipeline APIs with their own IAM principals. The services then act through service roles. Finally, the source provider (GitHub, GitHub Enterprise Server, GitLab or Bitbucket) decides, through webhook events, who can make a build start at all.

IdentityWhat it isWhat it can doMain control
CodeBuild service roleIAM role trusted by the service principal codebuild.amazonaws.comEverything the build commands do in AWS: pull source, write logs, push artefacts, deployLeast-privilege permissions policy, aws:SourceArn and aws:SourceAccount in the trust policy
CodePipeline service roleIAM role CodePipeline assumes to run stages and actionsStart builds, read and write the artefact bucket, invoke deploy actions, publish approval notificationsScoped permissions, customer managed KMS key on artefacts
Source credentialGitHub App, OAuth, personal access token, Secrets Manager secret or AWS CodeConnections connectionRead repositories, create webhooks, report build statusPrefer CodeConnections or an app connection over a long-lived personal token
Webhook actorThe repository user whose push or pull request fires the webhookDecides which code runs in the buildACTOR_ACCOUNT_ID filters, pull request build policy
Human approverIAM principal with codepipeline:PutApprovalResultReleases a paused pipeline to the next stageResource-scoped approval policy

The AWS documentation is direct about the service role: CodeBuild uses it for every operation it performs for you, and anyone who can use the role can indirectly exercise its permissions. A developer with permission to edit a project or its buildspec, but no deploy rights of their own, effectively inherits the deploy rights of the role. That is why project edit rights (codebuild:UpdateProject, codebuild:StartBuild with overrides) deserve the same review as the role itself. See the CI/CD Pipeline Identity Security Guide for the general model.

Confused deputy protection in the trust policy

The trust policy should not only name codebuild.amazonaws.com. AWS recommends adding the aws:SourceAccount condition to restrict the role to your account, or aws:SourceArn to restrict it to a single project such as arn:aws:codebuild:us-east-1:111122223333:project/MyProject. A wildcard project ARN is acceptable while a project is being created, but tighten it afterwards. One role per project, with a SourceArn condition, gives a clean answer to the question "which build can do this?".

Admin roles and privileged access

Treat these permissions as privileged, because each one lets a person change what code runs or what that code can reach:

  • Project edit: codebuild:UpdateProject can change the source, the buildspec, the environment image, privileged mode and the service role. Restrict it to the platform team and require change review.
  • Build overrides: codebuild:StartBuild accepts overrides such as environment variables and an alternative buildspec. A precedence rule matters here: environment variables set on the start-build call take priority over the project definition, which in turn beats the buildspec. Limit who can start builds with overrides on deploy projects.
  • Role passing: iam:PassRole on build roles decides which roles a project can be given. Scope it to the specific role ARNs per team.
  • Webhook management: codebuild:CreateWebhook and codebuild:UpdateWebhook can remove filters or set the pull request build policy to DISABLED.
  • Source credentials: codebuild:ImportSourceCredentials replaces the account's default credential for a provider, which every project using the default then inherits.
  • Pipeline edit and approval: codepipeline:UpdatePipeline can remove an approval stage; codepipeline:PutApprovalResult releases it. Keep these with different people.

Privileged mode

The privileged flag (privilegedMode in the API) exists so a build can run a Docker daemon and build container images. AWS documents it for that purpose only: set it to true only if the project builds Docker images. Note that the Docker daemon is already enabled by default for non-VPC builds; privileged mode is needed for Docker in VPC builds, and Windows build environments do not support it. Because privileged containers have far wider access to the host than normal containers, keep privileged projects separate from any project that builds untrusted pull requests, and never combine privileged mode with a broad service role on a public repository.

Run as a non-root user

By default buildspec commands run as root. The Linux-only run-as setting, at the top level or per phase, runs commands as a named user. Use it for phases that execute third-party code such as dependency installs and test suites.

Integrations, API tokens and service accounts

Source connections

The project source block takes an auth type of OAUTH, CODECONNECTIONS or SECRETS_MANAGER, with a resource that is either the CodeConnections ARN or the Secrets Manager ARN. For GitHub the console offers GitHub App, OAuth or personal access token; GitHub Enterprise Server and GitLab connect through AWS CodeConnections. A personal access token is a long-lived credential tied to one person: when that person leaves, builds break or, worse, the token outlives the account review. Prefer a connection, and use a custom source credential per project rather than the account default where projects have different trust levels.

Webhook filters

Filter groups decide which webhook events start a build. Within a group every filter must match; across groups any group can match. The AWS guidance for public repositories is to use filter groups, add ACTOR_ACCOUNT_ID filters that allow only specific users, and add FILE_PATH filters that exclude sensitive files.

Filter or settingPurposeHardening note
EVENTWhich event types build (push, pull request created, updated and so on)Separate push-to-main projects from pull request projects
ACTOR_ACCOUNT_IDWhich repository user IDs can triggerWrite the pattern as ^(12345|67890)$; without ^ and $ a pattern matches any ID that merely contains the digits
HEAD_REF and BASE_REFWhich branches or tagsAnchor patterns such as ^refs/heads/main$
FILE_PATH with excludeMatchedPatternBlock builds when certain files changeAWS's example is ^buildspec.yml$ so pull requests that edit the buildspec do not build
Inline or S3 buildspec (ProjectSource.buildspec)Keep the build definition out of the repositoryStops a pull request from rewriting what the build does
Pull request build policyRequire a trusted comment before untrusted pull requests buildDefault ALL_PULL_REQUESTS; FORK_PULL_REQUESTS is the most permissive setting worth keeping

The pull request build policy (pullRequestBuildPolicy) has two parts. requiresCommentApproval takes DISABLED, FORK_PULL_REQUESTS or ALL_PULL_REQUESTS, and ALL_PULL_REQUESTS is the default for new projects. approverRoles lists the repository roles whose pull requests build automatically and who may approve others; on GitHub the default is GITHUB_ADMIN, GITHUB_MAINTAIN and GITHUB_WRITE, with GitLab and Bitbucket equivalents. An approver releases a build by commenting /codebuild_run(<latest commit SHA>). Because approval is tied to a commit SHA, a contributor who pushes again needs a fresh approval. Trimming approverRoles to admins and maintainers on high-value repositories is a reasonable step.

The approach is one layer of the defence against poisoned pipeline execution; see the OWASP CI/CD Top 10 CICD-SEC-4 Poisoned Pipeline Execution Guide.

Build secrets

The buildspec env block supports four sources. Only two are suitable for secrets.

  • env/variables: plaintext key and value pairs. AWS strongly discourages sensitive values here because they can be displayed in plain text in the console and the CLI.
  • env/parameter-store: maps a local name to a Parameter Store parameter, for example LOGIN_PASSWORD: /CodeBuild/dockerLoginPassword. The service role needs ssm:GetParameters; scope it to the parameter path.
  • env/secrets-manager: uses the pattern key: secret-id:json-key:version-stage:version-id and defaults to AWSCURRENT. Use the ARN for a secret in another account.
  • env/exported-variables: passes values to later CodePipeline stages. Parameter Store and Secrets Manager values defined in the project cannot be exported, which is a useful guard; do not work round it by copying a secret into a plain variable.

CodeBuild masks AWS access key IDs and Parameter Store and Secrets Manager strings in logs, but only the exact stored value. If a command Base64-encodes or changes the case of a secret, the transformed output is not masked. CodePipeline has the same lesson: AWS says not to put tokens or passwords in action configuration, pipeline variable defaults or CloudFormation configuration, because the values appear in logs, and to reference Secrets Manager instead. The git-credential-helper setting is not supported for webhook builds of public repositories. See the Secrets Management Guide.

Manual approval actions

A CodePipeline manual approval pauses execution until an IAM principal with codepipeline:PutApprovalResult approves or rejects. The pipeline waits up to seven days; with no response the action fails and the execution stops. An optional Amazon SNS topic, which must be in the same Region as the pipeline, notifies approvers. The managed policy AWSCodePipelineApproverAccess grants approval on every pipeline and approval action in the account, so for production gates write a custom policy whose PutApprovalResult resource is the full arn:aws:codepipeline:region:account:pipeline/stage/action ARN.

Logging and detection

Management API calls to both services are recorded by AWS CloudTrail; build output goes to Amazon CloudWatch Logs or S3 under the logs permissions in the service role. Retain both beyond the default CloudTrail event history by sending them to a trail.

Signal (API or log)Why it mattersSuggested response
UpdateWebhook, CreateWebhookFilter groups or pull request build policy changedAlert on any change to production repositories; diff the filter patterns
UpdateProjectService role, privileged mode, buildspec or source changedRequire a linked change ticket; alert when privilegedMode becomes true
ImportSourceCredentials, DeleteSourceCredentialsAccount default source credential replacedPage the platform owner
StartBuild with overrides from a human principalPossible ad hoc change of buildspec or variables on a deploy projectReview against expected automation principals
UpdatePipeline, PutApprovalResultApproval stage removed or a release approvedCheck that approver and pipeline editor are different people
Service role activity outside its normal API setA build used the role for something new, such as iam: or sts: callsInvestigate the triggering commit and actor

Metrics that show the controls are working:

  • Percentage of webhook projects whose ACTOR_ACCOUNT_ID and ref patterns are fully anchored (target 100%).
  • Number of projects with requiresCommentApproval set to DISABLED on repositories that accept outside contributions (target zero).
  • Number of service roles with "Resource": "*" on write actions, and the trend over time.
  • Count of plaintext env/variables whose names suggest secrets (TOKEN, PASSWORD, KEY), found in project definitions.
  • Production approvals granted by a principal who also edited the pipeline in the same window (target zero).

Hardening baseline

Roll the baseline out in this order so each step can be tested without breaking delivery:

  1. Inventory: list every project, its source, webhook filter groups, pull request build policy, service role, privileged flag and environment variables. Flag projects that build public repositories first.
  2. Fix the trigger: anchor every ACTOR_ACCOUNT_ID, HEAD_REF and BASE_REF pattern, add a FILE_PATH exclusion for the buildspec or move the buildspec inline or to S3, and confirm requiresCommentApproval is ALL_PULL_REQUESTS or at least FORK_PULL_REQUESTS. Test by opening a pull request from a non-approver account and confirming it shows "Pull request approval required for starting a build".
  3. Split projects by trust: a test project that accepts pull request builds with no write permissions, and a deploy project that builds only from protected branches with a filter that allows only trusted users. This is the split AWS itself recommends.
  4. Scope roles: one service role per project, aws:SourceArn in the trust policy, named resources in place of "*", ssm:GetParameters and secretsmanager:GetSecretValue scoped to the paths the project needs. Use IAM Access Analyzer findings and last-accessed data to trim further.
  5. Move secrets: migrate plaintext variables to Parameter Store SecureString or Secrets Manager, then rotate every value that was ever plaintext, since it may already sit in logs or CloudTrail records.
  6. Reduce privilege inside the build: turn privileged mode off where no image is built, use run-as for third-party code, set a concurrent build limit, and use a VPC configuration with tight security groups for builds that reach private resources.
  7. Protect artefacts: keep artefact encryption on (do not select Remove artifacts encryption) and use a customer managed KMS key for pipeline artefact buckets, as AWS recommends for S3 sources.
  8. Gate production: add a manual approval before production deploys with a resource-scoped approver policy and SNS notification.

Rollback: keep the previous project definition (aws codebuild batch-get-projects output) under version control before each change, so a broken filter or role can be restored in one update. Infrastructure as code makes this simpler; see the Terraform and IaC Pipeline Credential Security Guide.

Small teams versus large estates: a small team with a handful of private repositories can rely on comment approval, anchored filters and one scoped role per project. A large estate should enforce the baseline centrally, for example by scanning project definitions on a schedule for privileged mode, DISABLED comment approval and unanchored filters, and should keep build and deploy in separate accounts.

Common misconfigurations

  • Unanchored actor ID regex: a pattern such as 12345|67890 matches any ID containing those digits. AWS recommends that customers audit their own ACTOR_ID filters, and apply AWS security updates promptly, as listed on the AWS security bulletins page.
  • Comment approval turned off for convenience: setting requiresCommentApproval to DISABLED on a public repository lets any pull request run code with the project role.
  • Buildspec in the repository on a pull request project: the contributor controls the build commands, so filters alone do not help.
  • One shared, broad service role: the console-generated policy uses "Resource": "*"; reusing it across test and deploy projects gives every pull request deploy rights.
  • Secrets in plaintext variables or pipeline configuration: visible to anyone with read access to the project and written to logs.
  • Account-wide approver policy: AWSCodePipelineApproverAccess lets an approver for a test pipeline release production too.

Framework mapping

Control areaNIST SP 800-53 Rev 5NIST CSF 2.0ATT&CK technique addressed
Service role scoping, PassRole limitsAC-6, AC-3PR.AA-05T1078.004 Cloud Accounts
Webhook filters, comment approval, inline buildspecCM-5, SA-10PR.PS-06T1195.002 Compromise Software Supply Chain
Secrets in Parameter Store and Secrets ManagerIA-5PR.AA-01T1552.001 Credentials In Files
Privileged mode off, run-as, VPCCM-7, SC-7PR.PS-01T1611 Escape to Host
Artefact encryption with KMSSC-28PR.DS-01Not applicable
CloudTrail and build log monitoringAU-2, AU-6DE.CM-09Not applicable

Practitioner checklist

  • Every ACTOR_ACCOUNT_ID, HEAD_REF and BASE_REF filter pattern starts with ^ and ends with $.
  • Pull request build policy is ALL_PULL_REQUESTS (or FORK_PULL_REQUESTS with a written reason) on every repository that accepts outside pull requests.
  • Buildspecs for pull request projects are inline or in S3, or a FILE_PATH filter excludes ^buildspec.yml$.
  • Each project has its own service role with aws:SourceArn in the trust policy and no "Resource": "*" on write actions.
  • Test projects that build pull requests have no deploy or write permissions.
  • No secrets in env/variables, StartBuild overrides or CodePipeline action configuration; all former plaintext values rotated.
  • Privileged mode enabled only on projects that build container images, and never on pull request projects.
  • Source connections use CodeConnections or an app connection rather than a personal access token where the provider allows.
  • Production approval actions use a custom policy scoped to the pipeline, stage and action ARN.
  • Alerts exist for UpdateWebhook, UpdateProject, ImportSourceCredentials and UpdatePipeline on production projects.

Standards and references

Related NHI Mgmt Group resources: CI/CD Security Guide · CI/CD Pipeline Identity Security Guide · OWASP CI/CD Top 10 CICD-SEC-4 Poisoned Pipeline Execution Guide · Top 10 CI/CD Security Risks

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 8 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org