v0.2.0-draft AI Drafted

CircleCI Hardening Guide

DevOps Last updated: 2026-08-08

CI/CD pipeline security including contexts, secrets, and runner hardening

View:

Overview

CircleCI serves 200,000+ DevOps teams with integrated CI/CD pipelines. The January 2023 breach demonstrated how session cookie theft bypasses MFA—attackers exfiltrated customer OAuth tokens, SSH keys, and environment variables from production systems via infostealer malware on an engineer’s laptop. All customers were forced to immediately rotate secrets across all connected services.

Intended Audience

  • Security engineers hardening CI/CD infrastructure
  • DevOps engineers configuring secure pipelines
  • GRC professionals assessing supply chain security
  • Platform teams managing CircleCI deployments

How to Use This Guide

  • L1 (Crawl): Essential controls for all organizations
  • L2 (Walk): Enhanced controls for security-sensitive environments
  • L3 (Run): Strictest controls for regulated industries

Scope

This guide covers CircleCI security configurations including authentication, context security, secrets management, and lessons learned from the January 2023 breach.


Table of Contents

  1. Authentication & Access Controls
  2. Context & Secrets Security
  3. Pipeline Security
  4. Runner Security
  5. Monitoring & Detection
  6. Incident Response
  7. Compliance Quick Reference

1. Authentication & Access Controls

1.1 Enforce SSO with MFA

Profile Level: L1 (Crawl) CIS Controls: 6.3, 6.5 NIST 800-53: IA-2(1)

Description

Require SAML SSO with MFA for all CircleCI access. The January 2023 breach demonstrated that SSO session tokens remain high-value targets.

Rationale

Why This Matters:

  • CircleCI stores OAuth tokens, SSH keys, and environment secrets
  • Session cookie theft bypassed 2FA in the 2023 breach
  • SSO enables centralized access control and session management

Attack Prevented: Credential stuffing, password reuse, unauthorized organization access, and orphaned access after an employee is deprovisioned in the IdP

Real-World Incidents:

  • January 2023 CircleCI Breach: Infostealer malware on engineer laptop led to 2FA bypass via SSO session hijacking. Customer OAuth tokens, SSH keys, and environment variables exfiltrated.

Prerequisites

  • SAML SSO is a Scale Plan feature (also available on CircleCI Server). One domain per organization, and each user must already have a CircleCI login before SSO is configured.

ClickOps Implementation

Step 1: Configure SAML SSO

  1. Navigate to: app.circleci.com → select your organization → Org → Single sign-on (SSO)
  2. Select Set up SSO
  3. Enter the values generated by your identity provider:
    • SSO URL: IdP login endpoint
    • x509 signing certificate: IdP signing certificate
    • Domain: the email domain your users sign in with
  4. Copy the TXT record CircleCI provides into your DNS to verify domain ownership — enforcement does not activate until the domain is verified

Step 2: Enrol every account in MFA and set session lifetime in the IdP

  1. Navigate to: Profile (upper right) → User Settings → Password & authentication
  2. Under Multi-factor authentication, select Add authenticator app, enter your password, scan the QR code, and verify the generated code
  3. Store the recovery code — CircleCI displays it only once

Correction (2026-08): CircleCI documents no org-level session-timeout setting and no step-up re-authentication for sensitive operations. MFA is verified only at login and when changing an account’s own authentication settings (email or password). Session lifetime for SSO organizations is governed by your identity provider — CircleCI states that when a user’s session expires they “will be prompted to re-authenticate.” Set the timeout in the IdP, not in CircleCI. Only authenticator apps are supported as a factor.

Step 3: Route job egress through CircleCI IP ranges (L2)

Correction (2026-08): CircleCI’s IP ranges feature is not an inbound allowlist restricting who may reach your CircleCI organization. It routes outbound job traffic through CircleCI’s published IP ranges so that your infrastructure can allowlist those addresses. CircleCI documents no inbound org-access IP allowlist — do not treat this control as an access restriction on the CircleCI console.

  1. Enable per job by adding the circleci_ip_ranges: true job key in .circleci/config.yml
  2. Docker executor only — jobs using the machine executor or setup_remote_docker fail when this key is set
  3. Available on the Performance and Scale plans; billed at 450 credits per 1 GB of data transferred by jobs with IP ranges enabled (Docker image pulls are not charged), visible under Plan → Usage
  4. Allowlist the ranges on your side — CircleCI publishes machine-consumable lists as DNS records at jobs.knownips.circleci.com, core.knownips.circleci.com, and all.knownips.circleci.com

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.1 Logical access controls
NIST 800-53 IA-2(1) MFA for network access

1.2 Implement Role-Based Access Control

Profile Level: L1 (Crawl) NIST 800-53: AC-3, AC-6

Description

Use CircleCI’s organization and project roles to limit access based on job function. CircleCI roles exist at both the organization level and the project level, and they are not derived from your VCS permissions — CircleCI states that “your CircleCI roles and associated permissions are not derived from the permissions set in your VCS,” and that CircleCI roles do not let a user bypass VCS permissions. Granting someone write access in GitHub does not grant them a CircleCI role, and revoking it does not remove one.

Rationale

Why This Matters:

  • Broad default access lets any member modify contexts, secrets, or pipeline configuration that hold production credentials
  • Limiting Admin to a handful of users reduces the number of accounts that can alter org-wide security settings
  • Scoped roles enforce least privilege so a single compromised contributor account cannot escalate to full control
  • Project-level permissions prevent unauthorized users from triggering builds that consume sensitive secrets

Attack Prevented: Privilege escalation, insider misuse, lateral movement, unauthorized pipeline triggering

ClickOps Implementation

Step 1: Configure Organization Roles

  1. Navigate to: Organization Settings → People
  2. Assign appropriate roles:
Role Permissions
Admin Full organization control (limit to 2-3 users)
Contributor Trigger pipelines, view builds
Viewer Read-only access to builds

Step 2: Configure Project-Level Permissions

  1. Navigate to: Project Settings → Permissions
  2. Assign the same three roles (Admin, Contributor, Viewer) per project so that project access is scoped independently of organization membership
  3. Restrict who can trigger pipelines and view build logs to project members

Step 3: Account for role precedence when using groups

  • CircleCI resolves conflicting assignments by taking the highest role: a user who is a project Admin individually and a Contributor via a group keeps Admin. Removing a user from a group does not downgrade them if an individual assignment still grants more.
  • Audit individual assignments as well as group assignments — reviewing only groups will understate effective access.
  • If your organization uses SSO group mapping (documented with Okta), IdP group membership is synchronised into CircleCI groups at authentication time, so group definitions belong in the IdP; role assignment still happens in CircleCI.

1.3 Rotate Personal API Tokens

Profile Level: L1 (Crawl) NIST 800-53: IA-5

Description

Implement regular rotation of personal API tokens and audit existing tokens.

Rationale

Why This Matters:

  • API tokens grant programmatic access that bypasses interactive SSO and MFA entirely
  • Long-lived, forgotten tokens become standing credentials that attackers reuse after a leak
  • Scoping tokens to the minimum required permissions limits the blast radius of any single leaked token
  • Regular rotation invalidates credentials that may have been silently exfiltrated, as seen in the 2023 breach

Attack Prevented: Credential theft, token reuse, persistent unauthorized access, MFA bypass

ClickOps Implementation

Step 1: Audit Existing Tokens

  1. Navigate to: User Settings → Personal API Tokens
  2. Review all active tokens
  3. Delete unused tokens

Step 2: Create Scoped Tokens

  • Create tokens with minimum required scopes
  • Document purpose for each token
  • Set calendar reminder for 90-day rotation

Step 3: Organization Token Audit

  1. Navigate to: Organization Settings → API Tokens
  2. Review project tokens
  3. Rotate tokens quarterly

2. Context & Secrets Security

2.1 Implement Secure Contexts

Profile Level: L1 (Crawl) - CRITICAL NIST 800-53: SC-28, AC-3

Description

Use CircleCI contexts with security restrictions to limit secret exposure. Contexts were the primary target in the 2023 breach.

Rationale

Why This Matters:

  • Contexts contain environment variables with secrets
  • 2023 breach exfiltrated context secrets
  • Unrestricted contexts expose secrets to all projects

Attack Prevented: Secret exfiltration via an unauthorized project or branch, lateral movement from a low-trust pipeline into production credentials, and blast-radius expansion when any single project is compromised

ClickOps Implementation

Step 1: Create Restricted Contexts

  1. Navigate to: Organization Settings → Contexts
  2. Create environment-specific contexts:
    • production-secrets (most restricted)
    • staging-secrets
    • shared-tools

Step 2: Configure Security Groups

  1. For each context, click Security Group
  2. Configure restrictions:
    • All members: ❌ (Never for production)
    • Security Groups: Select specific teams
    • Add restrictions: Enable

Group restrictions limit a context to members of a specified VCS team, and are documented for GitHub-type organizations. Organization admins can additionally enforce a setting requiring every new context to be group-restricted before it can be used.

Step 3: Add expression restrictions (the vendor-native branch restriction)

CircleCI’s supported mechanism for restricting a context to particular branches or pipeline conditions is an expression restriction evaluated against pipeline values. Documented operators are and, or (short-circuiting), ==, !=, starts-with, matches, >=, >, <=, <, and not. Documented example expressions include pipeline.git.branch == "main", pipeline.git.branch == "main" or pipeline.git.branch starts-with "integration-test", and pipeline.git.branch == "main" and not job.ssh.enabled and not (pipeline.config_source starts-with "api").

Prefer these over config-only conventions: an expression restriction is enforced by CircleCI when the context is requested, whereas a filter written in config.yml is only as trustworthy as the config that requested it. Combining not job.ssh.enabled with a branch match also blocks the “rerun with SSH” path that would otherwise hand an interactive shell the context’s secrets.

Secrets masking is not a boundary. CircleCI states that masking “will only prevent values from appearing in your job output,” does not apply to values under four characters or to the booleans true/false, and can be bypassed by invoking Bash with -x or -o xtrace. Treat masking as hygiene, not as a control.

Step 4: Limit Context to Specific Branches

Code Pack: Config
hth-circleci-2.01-restrict-context-branches.yml View source on GitHub ↗
# .circleci/config.yml
workflows:
  deploy:
    jobs:
      - deploy_production:
          context:
            - production-secrets
          filters:
            branches:
              only: main  # Only main branch can access production context

2.2 Environment Variable Security

Profile Level: L1 (Crawl) NIST 800-53: SC-28

Description

Configure environment variables with appropriate protection.

Rationale

Why This Matters:

  • Project environment variables are accessible to every job that runs and to anyone with project access
  • Secrets echoed into build logs or printed to stdout are exposed to all viewers of the build
  • Centralizing secrets in restricted contexts limits their distribution and enables consistent rotation
  • The 2023 breach exfiltrated environment variables, demonstrating the cost of broadly scattered secrets

Attack Prevented: Secret leakage, build-log exposure, over-broad secret access, credential exfiltration

ClickOps Implementation

Step 1: Use Contexts Over Project Variables

  • Store secrets in contexts (centralized management)
  • Reserve project variables for non-sensitive config
  • Never store secrets in repository

Step 2: Minimize Environment Variable Exposure

Code Pack: Config
hth-circleci-2.02-minimize-env-var-exposure.yml View source on GitHub ↗
# .circleci/config.yml
jobs:
  build:
    docker:
      - image: cimg/base:2024.01
    steps:
      - run:
          name: Build
          # Only use environment variables when needed
          command: |
            # Don't echo secrets
            # Don't pass secrets as command arguments (visible in ps)
            ./build.sh

Step 3: Apply CircleCI’s job-level secrets hygiene

CircleCI’s security recommendations cover the ways a secret escapes a job even when it was stored correctly:

  • Keep secrets out of shell history. Wrap sensitive commands in set +o historyset -o history, or where the shell supports it set HISTCONTROL to ignoreboth or ignorespace and prefix the command with a space.
  • Lock down credential files. Where a .netrc holds credentials, chmod 0600 it before writing the secret into it.
  • Never pass secrets as command arguments — arguments are visible to anything reading the process table. Point curl at a file instead, using the @ directive (curl --data @myfile, curl --header @your_filename).
  • Prefer the CircleCI CLI over curl. CircleCI states the CLI “takes extra precautions to avoid leaking secrets”; when scripting, feed the value in with a here-string (circleci context store-secret --org-id <org-id> <context-name> <secret-name> <<< "$MY_SECRET") rather than spawning an extra process.
  • Never run env or printenv in a job — a single debugging line dumps every context variable into the build log.

Step 4: Rotate All Secrets Post-Breach Following the 2023 breach, CircleCI recommended:

  1. Rotate ALL secrets stored in contexts
  2. Rotate ALL project environment variables
  3. Review audit logs for unauthorized access
  4. Rotate OAuth tokens for connected services

2.3 OIDC Token Authentication

Profile Level: L2 (Walk) NIST 800-53: IA-5

Description

Use OIDC tokens instead of static credentials for cloud provider authentication.

Rationale

Why This Matters:

  • Static cloud credentials stored in CircleCI are long-lived secrets that remain usable if exfiltrated
  • OIDC issues short-lived, job-scoped tokens that expire automatically and cannot be replayed later
  • Federated trust ties cloud access to the specific pipeline identity rather than a shared static key
  • Eliminating stored cloud keys removes the highest-value target exposed in the 2023 breach

Attack Prevented: Static credential theft, long-lived key reuse, cloud account takeover, lateral movement to cloud

Implementation

Code Pack: Config
hth-circleci-2.03-configure-oidc-authentication.yml View source on GitHub ↗
# .circleci/config.yml - AWS OIDC authentication
jobs:
  deploy_to_aws:
    docker:
      - image: cimg/aws:2024.01
    steps:
      - run:
          name: Configure AWS credentials via OIDC
          command: |
            # No static credentials needed
            # CircleCI OIDC token is automatically available
            aws sts assume-role-with-web-identity \
              --role-arn arn:aws:iam::123456789:role/CircleCI-Deploy \
              --role-session-name circleci-${CIRCLE_BUILD_NUM} \
              --web-identity-token ${CIRCLE_OIDC_TOKEN} \
              --duration-seconds 3600
Code Pack: API Script
hth-circleci-2.03-oidc-iam-trust-policy.sh View source on GitHub ↗
# AWS IAM Trust Policy for CircleCI OIDC
cat <<'POLICY'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789:oidc-provider/oidc.circleci.com/org/ORG_ID"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.circleci.com/org/ORG_ID:aud": "ORG_ID"
        },
        "StringLike": {
          "oidc.circleci.com/org/ORG_ID:sub": "org/ORG_ID/project/*/user/*"
        }
      }
    }
  ]
}
POLICY

3. Pipeline Security

3.1 Restrict Pipeline Triggers

Profile Level: L1 (Crawl) NIST 800-53: CM-7

Description

Control who can trigger pipelines and from which sources.

Rationale

Why This Matters:

  • Forked pull requests can run attacker-supplied configuration with access to project secrets
  • Unrestricted triggers allow untrusted contributors to execute arbitrary code inside the pipeline
  • Limiting who and what can start a build prevents poisoned-pipeline execution
  • Branch restrictions ensure only trusted code paths reach jobs that hold production secrets

Attack Prevented: Poisoned pipeline execution, secret exfiltration via forked PRs, unauthorized code execution

ClickOps Implementation

Step 1: Configure Trigger Permissions

  1. Navigate to: Project Settings → Advanced
  2. Configure:
    • Only build pull requests: Enable for public repos
    • Build forked pull requests: Disable or restrict

Step 2: Implement Branch Restrictions

Code Pack: Config
hth-circleci-3.01-restrict-pipeline-triggers.yml View source on GitHub ↗
# .circleci/config.yml
workflows:
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build

  deploy:
    jobs:
      - deploy_staging:
          filters:
            branches:
              only: develop
      - approve_production:
          type: approval
          requires:
            - deploy_staging
          filters:
            branches:
              only: main
      - deploy_production:
          requires:
            - approve_production
          filters:
            branches:
              only: main

3.2 Implement Pipeline Security Scanning

Profile Level: L1 (Crawl) NIST 800-53: RA-5

Description

Include security scanning in CI/CD pipeline.

Rationale

Why This Matters:

  • Vulnerable dependencies and hardcoded secrets reach production when builds skip scanning
  • Automated SAST, SCA, and secret scanning catch supply-chain risks before deployment
  • Failing the build on critical findings prevents known-vulnerable artifacts from shipping
  • In-pipeline scanning gives consistent, enforceable coverage that manual review routinely misses

Attack Prevented: Vulnerable dependency introduction, leaked secrets, supply chain compromise, shipping known CVEs

Code Pack: Config
hth-circleci-3.02-pipeline-security-scanning.yml View source on GitHub ↗
# .circleci/config.yml
jobs:
  security_scan:
    docker:
      - image: cimg/base:2024.01
    steps:
      - checkout
      - run:
          name: Dependency vulnerability scan
          command: |
            # Install and run dependency scanner
            npm audit --audit-level=high
            # Or use Snyk, OWASP Dependency-Check, etc.
      - run:
          name: Secret detection
          command: |
            # Detect secrets in code
            trufflehog filesystem . --only-verified
      - run:
          name: SAST scan
          command: |
            # Static analysis
            semgrep --config=auto .

workflows:
  security:
    jobs:
      - security_scan:
          # Run on all branches
          filters:
            branches:
              ignore: []

3.3 Secure Docker Image Usage

Profile Level: L1 (Crawl) NIST 800-53: CM-7

Description

Use verified, pinned Docker images in CI/CD pipelines.

Rationale

Why This Matters:

  • Mutable image tags such as latest can be repointed to a malicious image without warning
  • Pinning by digest guarantees the exact, audited image runs on every build
  • Unverified base images may carry backdoors or vulnerable components that execute with build privileges
  • A compromised CI image can read every secret the job touches because the build runs as trusted code

Attack Prevented: Supply chain attacks, image substitution, malicious base images, tag-mutation tampering

Code Pack: Config
hth-circleci-3.03-secure-docker-images.yml View source on GitHub ↗
# .circleci/config.yml
jobs:
  build:
    docker:
      # Good: Use CircleCI convenience images with specific version
      - image: cimg/node:20.10.0
      # Better: Pin to digest for immutability
      - image: cimg/node@sha256:abc123...

      # Bad: Never use :latest
      # - image: node:latest

    steps:
      - checkout
      - run: npm ci  # Use ci instead of install for reproducibility

4. Runner Security

4.1 Self-Hosted Runner Hardening

Profile Level: L2 (Walk) NIST 800-53: SC-7

Description

Secure self-hosted runners for sensitive workloads.

Rationale

Why This Matters:

  • Self-hosted runners execute pipeline code directly on infrastructure you own and must isolate
  • Persistent runners retain state between jobs, letting a malicious build poison later runs
  • Unrestricted network egress from runners can reach internal systems and pivot deeper
  • Ephemeral, least-privilege runners limit the blast radius of any single compromised job

Attack Prevented: Cross-job contamination, runner persistence, lateral movement to internal networks, privilege abuse

ClickOps Implementation

Keep the resource-class token off the runner VM. CircleCI’s runner provisioner (v0.1.2, 2026-07-22) introduces a token-proxy sidecar that holds the resource-class token and attaches it only on the task-claim call, so — in CircleCI’s words — “the token never reaches the VM” and “a job can’t read it from the machine it runs on.” This matters because a resource-class token is a standing credential: any job that can read it can register a rogue runner and claim other teams’ jobs.

  1. Provision pool VMs through the runner provisioner rather than baking the resource-class token into an image or user-data script
  2. Leave jobs running as the unprivileged circleci user, which is the default on provisioned VMs
  3. Where a workload genuinely needs elevation, use the opt-in grantSudo option rather than running the agent as root — CircleCI notes grantSudo “relies on the proxy so even a root task still can’t reach the token”
  4. Audit any existing runner images for a persisted token and re-provision them

Code Implementation

Code Pack: Config
hth-circleci-4.01-runner-isolation.yml View source on GitHub ↗
# Runner configuration
resource_class: company/secure-runner

# Use dedicated runners for production deployments
jobs:
  deploy_production:
    machine:
      image: ubuntu-2204:current
    resource_class: company/production-runner
Code Pack: API Script
hth-circleci-4.01-runner-security-setup.sh View source on GitHub ↗
# Runner machine hardening
# - Run as non-root user
# - Enable audit logging
# - Network isolation
# - Ephemeral storage

# Example Docker runner setup
docker run -d \
  --name circleci-runner \
  --security-opt no-new-privileges \
  --read-only \
  --tmpfs /tmp \
  -e CIRCLECI_RESOURCE_CLASS="company/secure-runner" \
  -e CIRCLECI_RUNNER_API_AUTH_TOKEN="${RUNNER_TOKEN}" \
  circleci/runner:latest

4.2 Enforce Config Policies on Self-Hosted Runner Resource Classes

Profile Level: L3 (Run) NIST 800-53: CM-7, AC-3

Description

Config policies let an organization evaluate every pipeline configuration against Rego (Open Policy Agent) rules before it runs, and reject configurations that violate them. The documented use case is restricting which projects can run jobs on a given self-hosted runner resource class — an org-level control that the project’s own config.yml cannot override. Available on the Scale Plan (Cloud) and CircleCI Server v4.2+.

Rationale

Why This Matters:

  • Without a policy, any project in the organization that knows a resource-class name can schedule work onto that runner — including a low-trust or newly created project
  • Self-hosted runners usually sit inside a trusted network with access to internal systems, so runner scheduling is effectively a network-access decision
  • Policy is evaluated server-side on the configuration, so it holds even when the pipeline config itself is attacker-modified
  • Rego bundles are version-controlled and reviewable, which turns an implicit convention into an auditable control

Attack Prevented: Unauthorized scheduling of jobs onto privileged self-hosted runners, pipeline-config tampering that redirects work to an internal-network runner, and lateral movement from a low-trust project into trusted infrastructure

Prerequisites

  • Scale Plan (Cloud) or CircleCI Server v4.2 or later
  • Organization admin, your organization ID, and the CircleCI CLI installed and authenticated
  • The project UUIDs you intend to allow for each resource class

ClickOps Implementation

Config policies are authored and deployed through the CLI rather than the web console:

  1. Create a policy directory (for example ./config-policies) containing a Rego policy — the runner policy maps each "namespace/resource_class" to the set of approved project UUIDs
  2. Push the bundle with circleci policy push ./config-policies --owner-id <your-organization-ID> (on Server, add --policy-base-url <your-circleci-server-domain>)
  3. Turn evaluation on with circleci policy settings --enabled=true --owner-id <your-organization-ID> — pushing a bundle does not enforce it until evaluation is enabled
  4. Store the policy directory in a reviewed repository and push it from a pipeline, so policy changes go through the same review as code

Validation & Testing

  • Attempt to run a job on the restricted resource class from a project not in the allowed UUID set — the pipeline should fail policy evaluation rather than starting
  • Confirm an approved project still runs successfully on the same resource class
  • Re-run circleci policy settings --owner-id <your-organization-ID> and confirm evaluation reports as enabled
  • Re-verify after adding any new project: a project created after the policy was written is denied by default, which is the intended behaviour but will surface as a build failure

Compliance Mappings

Framework Control ID Control Description
NIST 800-53 CM-7 Least functionality
NIST 800-53 AC-3 Access enforcement
SOC 2 CC6.1 Logical access controls
SOC 2 CC8.1 Change authorization

5. Monitoring & Detection

5.1 Enable Audit Logging

Profile Level: L1 (Crawl) NIST 800-53: AU-2, AU-3

Description

Configure and monitor CircleCI audit logs for security events.

Rationale

Why This Matters:

  • Without audit logs, context changes, secret edits, and permission grants happen invisibly
  • Exporting logs to a SIEM enables alerting on anomalous access and configuration tampering
  • During the 2023 breach, audit trails were essential to scope the extent of unauthorized access
  • Retained logs provide the forensic evidence and compliance record needed after an incident

Attack Prevented: Undetected tampering, slow breach detection, unauthorized configuration changes, audit gaps

ClickOps Implementation

Correction (2026-08): Audit logs are not a Scale-only feature. CircleCI states that “all CircleCI organizations can request audit logs” — you need to be an organization admin. Free plans get 1 request per day; all paid plans get 3 requests per day. Only audit log streaming is Scale-exclusive. If your organization skipped audit logging because it was believed to be plan-gated, request a log now.

Step 1: Request Audit Logs (all plans)

  1. Navigate to: Organization Settings → Security → Audit Logs
  2. Select the date range — logs can be retrieved for up to 12 months of history
  3. Select Request audit logs; the resulting CSV stays available for download for 30 days after processing
  4. Review events for:
    • Context modifications
    • Environment variable changes
    • User permission changes
    • API token creation

Step 2: Enable Audit Log Streaming (L2 — Scale plan)

Streaming removes the request-per-day ceiling and is the only way to get audit data into a SIEM continuously. CircleCI states that “audit log streaming is only available to CircleCI Scale Plan customers. Free and Performance Plan customers do not have access to this feature.”

  1. Navigate to: Organization Settings → Security → Audit Logs and configure streaming
  2. Choose a destination:
    • AWS S3, authenticated with OIDC — CircleCI assumes an IAM role you create, so no long-lived AWS access key is stored in CircleCI (this is the preferred option; it removes the exact class of static credential that was exfiltrated in 2023)
    • Other S3-compatible service — any S3-compatible endpoint, for teams keeping audit data off AWS
  3. Scope the IAM role’s trust policy to CircleCI’s OIDC issuer and your organization, and grant it write-only access to a dedicated, versioned, object-locked bucket
  4. Map the delivered records into your SIEM. CircleCI emits a common_fields block alongside the event payload containing outcome (success/failure), source_ip (from request.ip_address), status, and timestamp (from occurred_at) — parse against these rather than against per-event schemas, since CircleCI documents 90+ distinct event types spanning invitations, builds, contexts, webhooks, and workflows
  5. Alert on the high-signal subset first: context creation and secret writes, permission and role changes, API token creation, and SSO/authentication setting changes

Step 3: Export to SIEM via the API

Code Pack: API Script
hth-circleci-5.01-export-audit-logs.sh View source on GitHub ↗
# CircleCI API - Export audit logs
curl -X GET "https://circleci.com/api/v2/organization/${ORG_ID}/audit-log?start-time=${START}" \
  -H "Circle-Token: ${API_TOKEN}" \
  | jq '.items[]'

6. Incident Response

6.1 Post-Breach Checklist (Based on 2023 Incident)

If you suspect CircleCI compromise:

Immediate Actions (0-4 hours)

  1. Rotate ALL secrets stored in CircleCI contexts
  2. Rotate ALL project environment variables
  3. Rotate OAuth tokens for connected services (GitHub, AWS, etc.)
  4. Rotate SSH keys stored in CircleCI
  5. Review audit logs for unauthorized access

Short-term Actions (24-48 hours)

  1. Review all pipeline runs during suspected window
  2. Audit all API token usage
  3. Check for unauthorized context or project changes
  4. Rotate personal API tokens
  5. Review connected service audit logs

Long-term Actions

  1. Implement OIDC for cloud provider auth (eliminate static creds)
  2. Enable context restrictions by security group
  3. Implement IP allowlisting
  4. Configure session timeout policies
  5. Establish regular secret rotation schedule

7. Compliance Quick Reference

SOC 2 Mapping

Control ID CircleCI Control Guide Section
CC6.1 SSO enforcement 1.1
CC6.2 Context restrictions 2.1
CC7.2 Audit logging 5.1

NIST 800-53 Mapping

Control CircleCI Control Guide Section
IA-2(1) SSO with MFA 1.1
SC-28 Context security 2.1
AU-2 Audit logging 5.1

Appendix A: Plan Compatibility

Control Free Performance Scale Server
SSO
Contexts
Context Restrictions
IP Ranges (job egress, not inbound allowlisting)
Audit Log Requests (1/day free, 3/day paid)
Audit Log Streaming
Config Policies ✅ (v4.2+)
Self-Hosted Runners

Notes:

  • IP Ranges routes outbound job traffic through CircleCI’s published ranges so your systems can allowlist them. It is a Cloud feature and does not restrict who can access your CircleCI organization — see 1.1 Step 3.
  • Audit Log Requests are available to every organization; the plan difference is request rate, not access. Only streaming is Scale-exclusive — see 5.1.

Appendix B: References

Official CircleCI Documentation:

API Documentation:

Compliance Frameworks:

  • CircleCI publishes its certification and authorization status (SOC 2, FedRAMP) on its trust and compliance marketing pages. Those pages are not hardening documentation and are excluded from this guide’s reference list under the repository’s source standard; verify current attestation status directly with CircleCI under NDA or via your procurement process rather than relying on a marketing claim reproduced here. (Attestation scope not independently verified as of 2026-08.)

Third-Party Benchmarks:

  • No CIS Benchmark, DISA STIG, or CISA SCuBA baseline exists for CircleCI as of 2026-08. Compliance mappings in this guide are to NIST 800-53 and SOC 2 criteria by name.
  • Tier 3/4 independent security research specific to CircleCI was not surveyed in the 2026-08 currency pass beyond the January 2023 incident already documented below.

Security Incidents:

  • January 2023 — Session cookie theft led to customer secrets exfiltration. Infostealer malware on an engineer’s laptop captured an SSO session cookie, bypassing MFA. Attackers accessed production systems and exfiltrated customer OAuth tokens, SSH keys, and environment variables stored in CircleCI contexts. All customers were advised to immediately rotate all secrets. (CircleCI Incident Report)

Changelog

Date Version Maturity Changes Author
2026-08-08 0.2.0 draft Correct 1.1 IP allowlisting (IP ranges is job egress, not inbound access control) and remove the non-existent session-timeout/step-up re-auth path; correct 5.1 (audit log requests are available to all plans — only streaming is Scale-only) and add native OIDC-authenticated S3 streaming; add 4.2 Config Policies; add context expression restrictions and secrets-masking caveat to 2.1; add job-level secrets hygiene to 2.2; add runner token-proxy guidance to 4.1; enrich 1.2 role model (org + project, not VCS-derived, highest role wins); add missing Attack Prevented to 1.1 and 2.1; repair rotted Appendix B links and remove Trust Center / marketing security references Claude Code (Opus 4.8)
2026-06-29 0.1.1 draft Add cheat-sheet Description and Rationale for all controls Claude Code (Opus 4.8)
2025-12-14 0.1.0 draft Initial CircleCI hardening guide with 2023 breach lessons Claude Code (Opus 4.5)

Contributing

Found an issue or want to improve this guide?