Skip to content

Expert · DevSecOps · topic 47 of 53 · 30 min

Secrets management

Explanation

A secret is anything that gives access: passwords, API keys, tokens and private keys. Secrets leak through Git history, logs, images, CI output and screenshots. Stop leaks with pre-commit scanning and push protection, store secrets in a vault or a cloud secret manager, prefer short-lived credentials such as OIDC from CI, and after a leak, revoke first.

What counts as a secret?

A secret is any value that lets someone access or change something. If a stranger who found it could log in, read data, spend money or pretend to be your service, it is a secret.

  • Passwords and database connection strings that contain a password
  • API keys and tokens (payment gateway keys, GitHub tokens, SMS provider keys)
  • Cloud access keys (for example an AWS access key ID with its secret access key)
  • Private keys: SSH keys, TLS certificate keys, code-signing keys
  • Webhook signing secrets, OAuth client secrets, kubeconfig files with credentials

Ordinary configuration, such as a port number or a feature flag, is not secret. Keep the two apart: configuration can live in Git, secrets cannot.

How secrets leak

WhereHow it happensPrevention
Git historyA .env file is committed, then deleted in the next commit. It is still in the history..gitignore, pre-commit scanning, push protection
LogsThe app logs full request headers or all environment variablesLog only what you need; mask tokens
Container imagesENV PASSWORD=... in a Dockerfile, or COPY . . copies .env.dockerignore, pass secrets at run time
CI outputecho $TOKEN, or set -x prints every command with its valuesUse the CI secret store; never print secrets
Screenshots and chatA terminal screenshot in a ticket or a key pasted in a team chatShare through a secret manager, not chat

Public repositories are scanned by automated bots within minutes of a push. If a cloud key is pushed to a public repository, assume it has already been copied.

Prevention: stop it before the push

A pre-commit hook is a small script that Git runs on your machine before it creates a commit. gitleaks is a free open-source scanner that looks for secret patterns. Run it as a hook so a secret never enters history, and again in CI as a safety net. On GitHub, secret scanning finds known token formats in repositories, and push protection blocks a push that contains one.

yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.24.2          # pin a release tag; update it from time to time
    hooks:
      - id: gitleaks
With the pre-commit tool installed, run "pre-commit install" once; every commit is then scanned.
bash
pip install pre-commit
pre-commit install            # add the hook to this repository
pre-commit run --all-files    # scan everything once
gitleaks git -v               # scan the whole Git history (older versions: gitleaks detect -v)
Scan the history once when you adopt the tool: old leaks are still leaks.

Where secrets should live

OptionWhat it isNotes
HashiCorp Vault / OpenBaoA central secret store with access policies, audit logs and dynamic secretsOpenBao is a community fork of Vault under the Linux Foundation
Cloud secret managersAWS Secrets Manager, Azure Key Vault, Google Secret ManagerAccess controlled by cloud IAM; rotation built in for some secrets
Kubernetes SecretsObjects that hold small secret values for podsOnly base64-encoded by default: turn on encryption at rest and restrict who can read them
External Secrets OperatorCopies secrets from a vault or cloud manager into Kubernetes SecretsThe source of truth stays in the secret manager
Sealed SecretsEncrypts a Secret so the encrypted version can be stored in GitOnly the controller inside the cluster can decrypt it
Careful: base64 is an encoding, not encryption. Anyone who can read a Kubernetes Secret can decode it with one command. Protect Secrets with RBAC and encryption at rest.

Short-lived credentials and OIDC from CI

A long-lived key stored in CI works until someone leaks it, and then it works for the attacker too. A short-lived credential expires on its own, often within an hour. OIDC (OpenID Connect) lets your CI job prove who it is to the cloud: GitHub Actions issues a signed token that says "this job runs in repository acme/canteen on branch main". The cloud checks the signature and a trust rule, then hands back temporary credentials. No cloud key is stored in GitHub at all.

yaml
permissions:
  id-token: write      # allow the job to request an OIDC token
  contents: read
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::123456789012:role/canteen-deploy
          aws-region: ap-south-1
      - run: aws sts get-caller-identity   # temporary credentials, no stored key
GitHub Actions to AWS with OIDC. The IAM role's trust policy decides which repository and branch may use it.

Rotation

Rotation means replacing a secret with a new one on a schedule, or straight away when someone leaves the team. Automate it: a manual rotation that happens "every year" usually never happens. To rotate without downtime, create the new secret, let the app accept both for a short overlap, switch the app over, then remove the old one. Vault can go further with dynamic secrets: it creates a unique database user for each app instance and deletes it when the lease ends.

After a leak: the order matters

  1. Revoke or rotate the secret immediately. This is the only step that stops an attacker.
  2. Check the logs for use during the exposure window: cloud audit logs (such as AWS CloudTrail), the provider's dashboard, unusual bills.
  3. Clean the history with git filter-repo or BFG Repo-Cleaner and force-push, after warning the team. Remember that forks, clones and caches may still hold the old commits.
  4. Fix the cause: add the pre-commit hook, turn on push protection, move the secret to a secret manager.
  5. Write a short blameless review so the team learns from it.
Careful: Common mistakes: deleting the file in a new commit and thinking it is gone (it is still in history); rewriting history but not revoking the key; printing secrets in CI "just to debug"; sharing one admin key across all services. Revoke first, then clean, then prevent.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Secrets management · 1/7

Secrets give access

A secret is anything that gives access: passwords, API keys, tokens and private keys.

Prefer a video by a teacher? Find free videos on “secrets management devops vault github oidc gitleaks” on YouTube ↗

In real life: An AWS key pushed to a public repository in Bengaluru

A student intern at a Bengaluru startup pushes a side project to a public GitHub repository. The .env file holds the company's AWS access key. Within the hour, unknown instances start in another region, and the billing alert fires that evening. The DevOps lead deactivates the key first, then checks CloudTrail to see what the key did, terminates the unknown instances, and opens a support case with AWS. Afterwards the team switches CI to OIDC so no long-lived AWS keys exist, adds gitleaks as a pre-commit hook, and turns on GitHub push protection for the organisation.

Practice (0/3 done)

Task 1 · Question

You notice you pushed a payment gateway API key to GitHub ten minutes ago. What should you do FIRST?

Task 2 · Code · Python

Write scan_secrets(text) that checks each line of text and returns a list of (line_number, kind) tuples, with line numbers starting at 1. Kinds, checked in this order: "aws_access_key" (AKIA followed by 16 capital letters or digits), "github_token" (ghp_, gho_, ghu_, ghs_ or ghr_ followed by 36 letters or digits), "private_key" (a line containing -----BEGIN PRIVATE KEY----- or with a word before PRIVATE, like -----BEGIN RSA PRIVATE KEY-----) and "password" (password = "..." or password = '...', any case, spaces allowed around =). Skip any line containing # nosecret.

Task 3 · Practice

Your canteen API reads its database password from a hard-coded line in config.py, and the GitHub Actions workflow deploys to AWS with an access key stored as a repository secret. Write a short plan (5 to 8 steps) to fix both, in the right order.

Quick quiz

Question 1

1.A Kubernetes Secret holds the value cGFzc3dvcmQ= (base64). How protected is it?

Question 2

2.What is the main benefit of OIDC from CI to a cloud provider?

Question 3

3.Which tool lets you keep an encrypted Kubernetes Secret in Git that only the cluster can decrypt?

Question 4

4.A developer deletes .env in a new commit after pushing it. Is the secret gone?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A Kubernetes Secret holds the value cGFzc3dvcmQ= (base64). How protected is it?

Answer: Not at all against anyone who can read the Secret: base64 is just an encoding

base64 decodes with one command. Use RBAC, encryption at rest and keep Secrets out of Git.

What is the main benefit of OIDC from CI to a cloud provider?

Answer: No long-lived cloud key is stored in CI; credentials are temporary

The job exchanges a signed token for credentials that expire on their own.

Which tool lets you keep an encrypted Kubernetes Secret in Git that only the cluster can decrypt?

Answer: Sealed Secrets

Sealed Secrets encrypts with the controller's public key; only the controller in the cluster has the private key.

A developer deletes .env in a new commit after pushing it. Is the secret gone?

Answer: No, it is still in the earlier commit in history

Git keeps every earlier commit. Revoke the secret; cleaning history is a later step.

What does GitHub push protection do?Related

Answer: Blocks a push that contains a detected secret

Push protection scans the changes being pushed and blocks them if a known secret format is found.

From Keeping secrets safe

You pushed an access key to a public GitHub repository. What should you do FIRST?Related

Answer: Deactivate or delete the key in the cloud console, then check logs and clean up

The key may already be copied, so revoke it at once. Cleaning Git history comes after.

From The command line on all three clouds

A session secret was pushed to a public GitHub repo. What fixes the problem?Related

Answer: Rotate: make a new secret, deploy it, and stop using the old one

The old value stays in git history and may already be copied. Only replacing it makes it useless.

From Environment variables and secrets

A database password was committed to a public GitHub repository an hour ago. What is the first thing to do?Related

Answer: Rotate (change) the password immediately

Assume it is already copied. Rotate it first, then clean the history and add secret scanning.

From Encryption, keys and secrets

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “DevSecOps” before its exam: Software supply chain security, Kubernetes security.
  2. Module 11 exam: DevSecOps · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, GitOps, platform engineering and DevOps careers. 53 topics left in the course before the final exam.

← Previous: DevSecOps: shifting security left