CI platforms outside the GitHub Actions and GitLab mainstream hold the same production secrets but expose very different security controls, and teams often carry assumptions from one platform to another. CircleCI is SaaS-first with contexts as its secrets boundary, Buildkite splits a SaaS control plane from agents you run yourself, and JetBrains TeamCity is most often a self-hosted server that has appeared three times in CISA's Known Exploited Vulnerabilities catalogue. This guide compares how identity, privileged roles, tokens, logging and hardening work across the three, so a security team responsible for a mixed estate can set one baseline and know where each product needs a different control. Deep single-product baselines are in the CircleCI Security Guide and the JetBrains TeamCity Hardening Guide; this page goes further on Buildkite and on the comparison.
Key takeaways
- The trust boundary sits in a different place on each platform. CircleCI restricts secrets at the context, Buildkite at the agent and its queue, TeamCity at the project and agent pool, so a control that is enough on one leaves a gap on another.
- Federate cloud access on all three. CircleCI issues OIDC tokens with a one hour lifetime, Buildkite agents can request OIDC tokens with pipeline claims, and both remove the need for stored cloud keys; the CircleCI breach shows why stored keys are the prize.
- Buildkite agents are yours to harden. Signed pipelines, plugin allowlists,
no-command-eval,no-local-hooksand protected environment variables decide what a compromised pipeline definition can do. - Self-hosted TeamCity is a public attack surface. CVE-2023-42793, CVE-2024-27198 and CVE-2024-27199 are all in KEV with known ransomware use; keep servers off the internet and patch on a days, not weeks, timescale.
- Every platform has tokens that ignore SSO. Buildkite documents that API tokens are not subject to SSO requirements, and TeamCity notes users can create tokens after OAuth sign-in, so offboarding must revoke tokens, not just the IdP account.
How identity and access work in CircleCI, Buildkite and TeamCity
| Aspect | CircleCI | Buildkite | TeamCity |
|---|---|---|---|
| Deployment model | Cloud, plus self-hosted Server | SaaS control plane; agents self-hosted or Buildkite hosted | Mostly self-hosted On-Premises; TeamCity Cloud |
| Human identity | VCS-linked organisation (GitHub, Bitbucket, GitLab), SSO options | Organisation users, teams, optional SSO requirement | Built-in users, LDAP, VCS OAuth and other auth modules |
| Access grouping | Organisation roles; context security groups mapped to VCS teams | Teams with Full Access, Build & Read or Read Only per pipeline | Per-project authorisation with System Administrator, Project Administrator, Project Developer, Project Viewer roles |
| Secrets boundary | Contexts and project environment variables | Agent environment, hooks and external secret stores; Buildkite secrets | Password-type parameters per project; versioned settings tokens |
| Workload identity | CIRCLE_OIDC_TOKEN, _V2, _V3 | buildkite-agent oidc tokens | Through plugins and connections (check your version) |
| Build execution trust | CircleCI-managed executors or self-hosted runners | Your agents decide what to run (signed pipelines, allowlists) | Server pushes work and agent updates to agents |
CircleCI
Context restrictions are the core control. Per CircleCI's documentation there are three types, combined with logical AND: security group restrictions (GitHub organisations only, mapped to VCS teams), project restrictions (only listed projects can use the context) and expression restrictions. An expression such as pipeline.git.branch == "main" and not job.ssh.enabled and not (pipeline.config_source starts-with "api") keeps production secrets away from feature branches, SSH reruns and API-triggered config. Expressions fail closed on errors or unknown variables. One caution: GitHub team membership for OAuth users syncs every few hours, so removal can lag.
Buildkite
Buildkite organisation administrators manage teams, settings and audit logs; team maintainers set the permission level their team gets on pipelines. Where a user is in several teams, the highest permission applies. SSO can be required for membership, but API tokens are not subject to SSO, so Buildkite advises that removing a compromised user is the only fully safe response. Because jobs run on agents you operate, the agent configuration is as important as user roles.
TeamCity
JetBrains recommends enabling per-project authorisation, mapping users to groups with the predefined roles at the lowest level needed, and creating custom roles rather than granting Project Administrator. Restrict the "remove build" permission so history needed for audit cannot be deleted.
Admin roles and privileged access
| Privileged capability | CircleCI | Buildkite | TeamCity |
|---|---|---|---|
| Top-level admin | Organisation admin: sets context restrictions, unrestricted access to all contexts | Organisation administrator: teams, settings, integrations, audit log | System Administrator; the super user login |
| Secrets editing | Members of the context's groups add or remove variables in a restricted context | Whoever controls agent hosts, hooks and the secret store | Project Administrators of the project holding the parameters |
| Pipeline definition change | Anyone who can push config to a branch the project builds | Pipeline Full Access users; anyone who can upload steps | Users who can change build configuration or versioned settings |
| Hidden privilege | SSH rerun of a job sees unmasked secrets | Agent host access exposes the agent token and environment | File system or database access can reverse default secret scrambling |
- Limit admins to a named few: on every platform, admin can read or redirect every secret. Hold admin through a separate account with phishing-resistant MFA, and review membership quarterly.
- TeamCity super user: JetBrains advises setting
teamcity.superUser.disable=truewhere logs are shipped to external systems, because the super user token is written to the log. - Remember the build author: TeamCity's own notes say anyone who can change build code can reach agent resources, other projects on the same agent and password values. The same is true in practice of every CI system.
Integrations, API tokens and service accounts
CI platforms are dense non-human identity hubs: VCS OAuth tokens and apps, cloud credentials, registry tokens, deploy keys, and the platform's own API tokens. The CircleCI breach of January 2023 is the reference case. Malware on an engineer's laptop stole a 2FA-backed SSO session, the attacker reached production and exfiltrated customer environment variables, tokens and keys, and CircleCI told every customer to rotate every stored secret.
| Credential | CircleCI | Buildkite | TeamCity |
|---|---|---|---|
| Cloud access | OIDC: tokens expire one hour after issue; issuer https://oidc.circleci.com/org/<org-id>; restrict roles on oidc.circleci.com/project-id and, with V2 or V3, the Git reference | OIDC tokens assert organisation and pipeline UUIDs and slugs, branch, commit and agent ID; sub identifies the pipeline by default | Use short-lived cloud connections where your version supports them; otherwise vault-issued credentials |
| Agent or runner registration | Self-hosted runner resource class tokens | Agent tokens: long-lived, per cluster, never expire unless expires_at is set at creation (it cannot be changed later); CIDR allowed IPs | Agent authorisation on the server; agents must be authorised before running builds |
| Platform API | Personal and project API tokens | API access tokens (not subject to SSO) | Access tokens for REST; JetBrains recommends auto-expiring tokens and a separate limited user |
| Package publishing | npm trusted publishing supports CircleCI cloud | Store in secret store, inject per job | Password parameters or vault integration |
- Buildkite ephemeral agents: for stack-managed agents, use short-lived job acquisition tokens so the long-lived agent token stays in the stack controller and out of job workloads.
- Secret masking is partial: CircleCI masks values only in job output, not values under 4 characters, not shell tracing (
set -x), test results, artefacts or SSH sessions. Do not rely on masking as a control. - TeamCity secrets at rest: enable a custom encryption key, since default scrambling is reversible by anyone with file system or database access, and keep versioned settings in a separate restricted repository if developers can commit to the source repository.
The identity side of all of this is covered in the CI/CD Pipeline Identity Security Guide.
Logging and detection
- CircleCI: export organisation audit logs and review context changes, restriction changes, new API tokens and SSH reruns on jobs that use production contexts.
- Buildkite: organisation administrators can access audit logs. Monitor team permission changes, agent token creation, pipeline setting changes and signed pipeline verification failures from agent logs.
- TeamCity: enable access logs on the server machine and review them, archive server and agent logs to secured storage, and alert on new administrator role assignments, new access tokens and plugin uploads.
- Cloud side, for all three: watch the cloud provider's logs for role assumptions by CI OIDC identities from unexpected projects, pipelines or branches, and for any use of static keys that should no longer exist.
| Metric | Target |
|---|---|
| Production secrets reachable from non-protected branches | Zero |
| Static cloud keys stored in CI platforms | Zero, replaced by OIDC |
| Buildkite agent tokens without expiry | Trending to zero |
| Days from TeamCity security release to patch on internet-reachable servers | Under 3 |
| CI admin accounts reviewed in the last quarter | 100% |
Hardening baseline
Buildkite agents
Buildkite's securing guidance gives agent-side controls that shape what any job can do:
- Signed pipelines: pipelines are signed on upload with JWS and agents verify before running; a mismatch blocks the job. Commands, pipeline environment variables, plugins and their configuration, matrix setup and the repository are signed. Use separate queues for uploaders (holding the private key, or AWS or GCP KMS keys) and runners (holding public keys only), and keep
verification-failure-behaviorat its defaultblockafter awarntrial. Signed pipelines are a self-hosted agent feature. - Plugins:
allowed-pluginstakes regex patterns and refuses jobs with non-matching plugins;no-pluginsdisables them. - Commands and hooks:
no-command-evalallows only script execution;no-local-hooksdisables repository-defined hooks and fails the job if one is present. Apre-bootstraphook can enforce repository and command allowlists before checkout. - Environment:
protected-environment-variables(agent 4.5.0 or later) stops jobs setting listed variables;protect-dangerous-git-environment-variablesprotects risky Git and command execution variables. - Git:
no-ssh-keyscanenforces strict host key checking;BUILDKITE_CLEAN_CHECKOUT=trueforces a clean checkout for untrusted commits.
CircleCI
- Put every production secret in a context with project and expression restrictions; keep project-level variables for non-sensitive values.
- Restrict who can rerun jobs with SSH on projects that use production contexts, and include
not job.ssh.enabledin expressions. - Do not pass secrets to builds from forked pull requests.
- Use OIDC V2 or V3 tokens so cloud trust policies can include the repository and Git reference.
TeamCity
- Serve only HTTPS through a reverse proxy, disable guest login, and keep the server off the internet; JetBrains advises taking an unpatched internet-facing server offline if a critical fix cannot be applied.
- Set
teamcity.installation.completed=trueinteamcity-startup.properties, so a server with an empty database cannot grant access to the first visitor. - Keep the "Default" agent pool empty, separate pools for public and private projects, never run agents on the server, and use disposable agents for untrusted pull requests.
- Set
teamcity.buildAuth.enableStrictMode=trueto limit what build-level authentication can reach in other projects. - Install only plugins from trusted sources and subscribe to JetBrains security notifications.
Rollout order for a mixed estate
- Exposure first: confirm no TeamCity server is reachable from the internet and every server is on a fixed release; this closes the KEV-listed path.
- Secrets inventory: list every secret in CircleCI contexts and project variables, Buildkite agent environments and hooks, and TeamCity parameters, with owner and target system.
- OIDC for cloud: move one non-production pipeline per platform to OIDC, test the trust policy conditions (project, pipeline, branch), then migrate production and delete the static keys.
- Restrict and sign: add CircleCI context restrictions, and run Buildkite signed pipelines with
verification-failure-behavior=warnon a pilot queue for two weeks before switching toblock. - Roll back safely: context restrictions and agent flags are configuration; keep the previous values in version control so a broken release pipeline can be restored in minutes without re-adding static keys.
Small teams on a single platform can stop after steps 1 to 3. Large estates should standardise on one agent image per trust level and manage agent configuration as code.
Common misconfigurations
- Internet-facing, unpatched TeamCity: CVE-2024-27198 let an unauthenticated attacker with HTTP(S) access bypass authentication and take administrative control of servers up to 2023.11.3 (fixed in 2023.11.4). Russia's SVR exploited the earlier CVE-2023-42793 against unpatched servers, as covered in the TeamCity CVE-2023-42793 exploitation page.
- Unrestricted CircleCI contexts: the default
All membersgroup lets any organisation member's pipeline on any branch read production secrets. - Buildkite agents trusting whatever they are sent: without signed pipelines or allowlists, anyone able to upload pipeline steps can run arbitrary commands on agents that hold production credentials.
- Static cloud keys in CI after OIDC is available: the CircleCI breach forced mass rotation precisely because long-lived keys were stored on the platform.
- Offboarding at the IdP only: Buildkite API tokens and TeamCity tokens created after OAuth sign-in survive removal from the identity provider.
- Shared agents across trust levels: public and private projects, or pull request and deployment builds, on the same agents let one compromise reach both.
The OWASP view of these issues is in the OWASP CI/CD Top 10 CICD-SEC-7 Insecure System Configuration Guide.
Framework mapping
| Framework | Reference | Applies to |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-2, AC-6 | Admin and team roles, context and project restrictions |
| NIST SP 800-53 Rev. 5 | IA-5 | Agent tokens, API tokens, OIDC replacing static keys |
| NIST SP 800-53 Rev. 5 | CM-6, SR-11 | Agent hardening flags, signed pipelines |
| NIST SP 800-53 Rev. 5 | AU-2, AU-6 | Audit log export and review |
| NIST CSF 2.0 | PR.AA-01, PR.PS-01, ID.RA-01 | Credential management, configuration, vulnerability tracking (KEV) |
| MITRE ATT&CK v20 | T1190, T1195.002, T1552.004, T1078 | Server exploitation, build supply chain compromise, key theft, valid account abuse |
Practitioner checklist
- Inventory every CircleCI organisation, Buildkite cluster and TeamCity server, with owners and admin lists.
- Replace stored cloud keys with OIDC on CircleCI and Buildkite, with trust policies bound to project or pipeline and branch.
- Apply project and expression restrictions to every CircleCI context holding production secrets.
- Enable Buildkite signed pipelines, plugin allowlists and
no-local-hookson agents that deploy to production. - Set expiry on all Buildkite agent tokens and use job acquisition tokens for ephemeral agents.
- Move TeamCity behind a VPN or private network, enable per-project authorisation and a custom encryption key.
- Track TeamCity entries in the CISA KEV catalogue and patch within days.
- Revoke platform API tokens as part of offboarding on every CI platform.
- Export audit logs to the SIEM and alert on admin, context, token and plugin changes.
Standards and references
- CircleCI: Using Contexts
- CircleCI: Using OpenID Connect Tokens in Jobs
- Buildkite: Securing Your Buildkite Agent
- Buildkite: Signed Pipelines
- Buildkite: Agent Tokens
- JetBrains: TeamCity Security Notes
- JetBrains: Critical Security Issues Affecting TeamCity On-Premises (CVE-2024-27198, CVE-2024-27199)
- CISA: Known Exploited Vulnerabilities Catalog
Related NHI Mgmt Group resources: CircleCI Security Guide · JetBrains TeamCity Hardening Guide · CI/CD Security Guide · CI/CD Pipeline Compromise Playbook