Managing Secrets and Security
1. Storing Secrets in Contexts
| Property | Description | Usage |
|---|---|---|
| context.name | Name of the context | Used to reference the context in configuration |
| context.secrets | Map of secret names to values | Accessed via CIRCLECI_CONTEXT_SECRET |
Example: Defining a Context with Secrets
version: 2.1
contexts:
my-context:
secrets:
api_key:
description: "API key for external service"
database_url:
description: "Database connection string"
Example: Using Context Secrets in a Job
version: 2.1
jobs:
build:
docker:
- image: circleci/node:16
environment:
DB_URL: <value from context>
steps:
- run:
name: Use Secret
command: |
echo "Using API key: $API_KEY"
Note: Contexts are scoped to the project and can be shared across multiple workflows and jobs.
Warning: Never hardcode secrets in configuration files or commit them to version control.
2. Using Project Environment Variables
| Variable | Type | Description | Usage |
|---|---|---|---|
| CIRCLECI | boolean |
Indicates if the build is running on CircleCI | if [[ "$CIRCLECI" == "true" ]]; then echo "Running on CircleCI"; fi |
| CIRCLE_PROJECT_REPONAME | string |
Name of the repository | echo "Repository: $CIRCLE_PROJECT_REPONAME" |
| CIRCLE_BUILD_NUM | number |
Build number | echo "Build Number: $CIRCLE_BUILD_NUM" |
| CIRCLE_SHA1 | string |
Git commit SHA | echo "Commit: $CIRCLE_SHA1" |
| CIRCLE_BRANCH | string |
Branch name | echo "Branch: $CIRCLE_BRANCH" |
| CIRCLE_TAG | string |
Tag name if build is from a tag | echo "Tag: $CIRCLE_TAG" |
Example: Using Environment Variables in a Script
#!/bin/bash
echo "Build Number: $CIRCLE_BUILD_NUM"
echo "Repository: $CIRCLE_PROJECT_REPONAME"
echo "Branch: $CIRCLE_BRANCH"
Note: Environment variables set in the CircleCI project settings are automatically available to all jobs in that project.
3. Masking Secrets in Logs
CircleCI automatically masks secrets in build logs to prevent sensitive information from being exposed. This masking is applied to environment variables and other configuration values that match specific patterns.
| Secret Pattern | Masking Behavior | Notes |
|---|---|---|
| CIRCLE_TOKEN | ******** |
API token used for CircleCI API access |
| AWS_ACCESS_KEY_ID | ******** |
Amazon Web Services access key ID |
| AWS_SECRET_ACCESS_KEY | ******** |
Amazon Web Services secret access key |
| GCP_SERVICE_ACCOUNT_KEY | ******** |
Google Cloud Platform service account key |
| SLACK_WEBHOOK_URL | ******** |
Slack incoming webhook URL |
Example: Environment Variable Usage
export AWS_SECRET_ACCESS_KEY="secret-key-value"
echo "Using AWS secret: $AWS_SECRET_ACCESS_KEY"
In the build log, the value will be masked as:
Using AWS secret: ********
Note: The masking is applied only to environment variables and configuration values that match predefined patterns. Custom secrets must be handled carefully to avoid exposure in logs.
4. Avoiding Hardcoded Secrets
| Approach | Description | Best Practice |
|---|---|---|
| Environment Variables | Store secrets as environment variables in CircleCI project settings. | Use env in config to access them. |
| CircleCI Contexts | Group related environment variables into contexts for reuse across jobs. | Securely manage shared secrets with context in config. |
| Encrypted Files | Store sensitive files (e.g., SSH keys) as encrypted artifacts. | Use circleci encrypt to encrypt and store securely. |
Example: Using Environment Variables
version: 2.1
jobs:
build:
steps:
- run:
command: |
echo "API Key is: $API_KEY"
Note: Never commit secrets to version control or expose them in logs.
Warning: Hardcoded secrets in config files will be visible to anyone with access to the repository.
Key Takeaways
- Never hardcode secrets in
config.yml. - Use CircleCI's project settings to store secrets.
- Utilize contexts for managing shared secrets across jobs.
5. Rotating Credentials Regularly
| Credential Type | Rotation Method | Notes |
|---|---|---|
| API Tokens | Generate new token via CircleCI UI or CLI | Old tokens remain valid until explicitly revoked |
| SSH Keys | Regenerate key pair and update CircleCI project settings | Update deployment configurations to use new private key |
| Environment Variables | Update via CircleCI dashboard or API | Ensure all jobs referencing the variable are updated |
Note: Rotate credentials at least every 90 days or when a security incident occurs.
Warning: Never hardcode credentials in your configuration files or job scripts.
6. Implementing Least Privilege
Example: Setting up a job with minimal permissions
version: 2.1
jobs:
build:
docker:
- image: circleci/node:16
steps:
- checkout
- run:
name: Run tests
command: npm test
- run:
name: Deploy to staging
command: npm run deploy-staging
| Permission Level | Description | Use Case |
|---|---|---|
| Read-Only | Allows reading configuration and job details | Monitoring or CI status checks |
| Read-Write | Allows reading and modifying jobs, workflows | CI/CD pipeline configuration changes |
| Admin | Full access to all project resources and settings | Project owner or administrator tasks |
Best Practices for Least Privilege
- Assign the minimum level of access required for each role or user
- Use environment variables for sensitive data instead of hardcoding secrets
- Regularly review and audit permissions to ensure they are still necessary
- Implement role-based access control (RBAC) for better management of permissions
7. Using OIDC Tokens
| Property | Description | Value |
|---|---|---|
| oidc.token | Token used for authentication with external services | string |
| oidc.issuer | URL of the OpenID Connect provider | https://circleci.com/oidc |
| oidc.audience | Intended recipient of the token | circleci |
Example: Using OIDC Token in a Job
jobs:
deploy:
docker:
- image: circleci/python:3.9
steps:
- run:
name: Deploy with OIDC Token
command: |
curl -H "Authorization: Bearer $OIDC_TOKEN" \
https://api.circleci.com/deploy
Note: OIDC tokens are valid for 1 hour and must be refreshed before expiration.
Warning: Never expose OIDC tokens in logs or public repositories.
8. Restricting Context Access
| Option | Values | Notes |
|---|---|---|
| context.name | my-context |
Name of the context to be created or modified |
| context.description | Production environment variables |
Description for the context (optional) |
| context.variables | { "API_KEY": "secret-key" } |
Key-value pairs of environment variables |
| context.projects | [ "org/project1", "org/project2" ] |
List of projects that can access this context |
Example: Creating a Context with Restricted Access
version: 2.1
contexts:
production-context:
description: "Context for production secrets"
variables:
API_KEY: "secret-key"
DATABASE_URL: "postgres://user:pass@host/db"
projects:
- "my-org/my-app"
- "my-org/another-app"
Note: Contexts are scoped to the organization and can be shared across projects. Ensure that only necessary projects have access.
Warning: Never store sensitive data in plain text within configuration files. Use CircleCI's encrypted context variables or environment variables.
9. Managing SSH Keys Securely
| Property | Description | Usage |
|---|---|---|
| circleci ssh key add | Adds an SSH key to your CircleCI project | circleci ssh key add <project-slug> <key-path> |
| circleci ssh key list | Lists all SSH keys configured for a project | circleci ssh key list <project-slug> |
| circleci ssh key remove | Removes an SSH key from a project | circleci ssh key remove <project-slug> <key-id> |
Note: SSH keys must be added to the project's settings in CircleCI to allow secure access to external systems during builds.
Warning: Never store SSH keys in plain text within your configuration files or environment variables. Always use CircleCI's encrypted environment variables or project settings.
10. Auditing Secret Usage
| Field | Description | Example |
|---|---|---|
| secret.name | Name of the secret as configured in CircleCI | API_KEY |
| secret.value | Value of the secret (masked in logs) | ********** |
| project.name | Name of the project using the secret | myapp/prod |
| build.number | Build number where secret was used | 1234 |
| job.name | Name of the job using the secret | deploy-to-prod |
| timestamp | Time when secret was accessed | 2026-01-15T10:30:00Z |
Example: Secret Usage Audit Log
{
"secret.name": "API_KEY",
"project.name": "myapp/prod",
"build.number": 1234,
"job.name": "deploy-to-prod",
"timestamp": "2026-01-15T10:30:00Z"
}
Note: CircleCI logs all secret usage in the build output, but values are masked. You can audit these logs to track which secrets were accessed and by which jobs.
Warning: Ensure that secrets are not logged or printed in build steps, even if masked. Use
circleci env to inspect environment variables safely.