Stop Hardcoding Passwords: A Guide to Secret Management in CI/CD and Kubernetes

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

The 2:00 AM Security Alert

I still remember a morning a few years ago when my phone exploded with alerts. A developer had accidentally pushed a .env file containing production database credentials to a public GitHub repository. Within 60 seconds, automated bots had already scraped the keys and attempted to spin up unauthorized EC2 instances. We spent the next six hours rotating every single credential and auditing access logs for signs of a breach.

This isn’t a rare horror story. In 2023 alone, researchers found over 10 million secrets exposed in public repositories. Many teams start by hardcoding secrets or storing them as plain text in CI/CD environment variables. While this works for a quick MVP, it creates a massive security liability. Moving from a ‘scripter’ to a professional DevOps engineer requires mastering centralized secret management.

Why Secret Sprawl Happens

Secret sprawl occurs when sensitive data—API keys, database passwords, and SSH keys—ends up scattered across GitHub, Jira, Slack, and local developer machines. This rarely happens because of laziness. Usually, it’s because the ‘official’ way to get a secret is too slow. If a developer has to wait three days for a ticket to be approved to get a staging key, they will just copy-paste it from a teammate.

Standard Kubernetes Secrets don’t solve this problem. By default, Kubernetes stores secrets in etcd as Base64 encoded strings. Let’s be clear: Base64 is obfuscation, not encryption. Anyone with basic get secret permissions in a namespace can decode them in seconds.

# Decoding a K8s secret takes zero effort
echo "S3ViamVjdFBhc3N3b3Jk" | base64 --decode

When I’m debugging these native secrets, I often use the Base64 Encoder/Decoder from ToolCraft to verify values quickly. It runs 100% in the browser. This ensures that sensitive strings never leave your local environment, which is a small but vital security habit.

HashiCorp Vault vs. AWS Secrets Manager

Once you decide to stop using plain-text files, you’ll likely choose between a platform-agnostic tool like HashiCorp Vault or a cloud-native service like AWS Secrets Manager.

HashiCorp Vault

Vault is the industry benchmark for secret management. It provides a centralized way to store, access, and protect secrets. My favorite feature is Dynamic Secrets. Instead of giving an application a static, long-lived PostgreSQL password, Vault generates a temporary user that exists for only 15 minutes. If the application is compromised, the stolen credential becomes useless almost immediately.

  • Pros: Multi-cloud support, incredibly granular policies, and high-performance encryption as a service.
  • Cons: It has a steep learning curve. Self-hosting the open-source version requires significant operational effort.

AWS Secrets Manager

If your entire stack lives on AWS, this is the most frictionless choice. It integrates natively with IAM (Identity and Access Management) and handles the heavy lifting of rotating RDS database passwords automatically.

  • Pros: Zero maintenance and native integration with AWS services.
  • Cons: Cost. At $0.40 per secret per month, a microservices architecture with hundreds of secrets can quickly add up.

Injecting Secrets into Kubernetes

Storing secrets in a vault is only one part of the puzzle. The real challenge is delivering those secrets to your pods without the application even knowing the vault exists. I generally recommend two patterns: the Vault Agent Sidecar Injector or the External Secrets Operator (ESO).

The Sidecar Injector Pattern

You add specific annotations to your Kubernetes Deployment. Vault then injects a sidecar container that fetches the secret and writes it to a shared memory volume at /vault/secrets. Your application simply reads the secret as if it were a local file. This is excellent for legacy apps that can’t easily integrate with APIs.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "payment-role"
    vault.hashicorp.com/agent-inject-secret-config: "secrets/data/payment/config"
spec:
  template:
    spec:
      containers:
        - name: app
          image: payment-service:v2.1.0

External Secrets Operator (ESO)

I prefer ESO when I want to keep using standard Kubernetes Secret objects but want them synchronized from an external source. If you update a value in AWS Secrets Manager, ESO automatically pushes that change into your K8s cluster. It’s a cleaner approach for teams that want to stay ‘K8s native’.

A Secure CI/CD Workflow

Before writing a single line of YAML for a new pipeline, I generate high-entropy credentials. I use the Password Generator on ToolCraft to create 32-character strings that resist brute-force attacks. When testing API payloads for Vault, I also use their YAML to JSON Converter to ensure my configurations are valid before deployment.

1. Authenticate with OIDC

Never store long-lived IAM keys in GitHub Secrets. Use OIDC (OpenID Connect) instead. This allows GitHub Actions to request a temporary, short-lived token from AWS based on a trusted relationship. No keys are stored, so there is nothing for a hacker to steal from your CI settings.

2. Use Runtime Fetching

Instead of injecting the actual database password into your build environment, store a SECRET_ID. The pipeline uses that ID to fetch the real value from your secret manager only when it’s absolutely needed for a deployment step.

# Example GitHub Action using OIDC
- name: Fetch Production Secrets
  uses: aws-actions/aws-secretsmanager-get-secrets@v2
  with:
    secret-ids: |
      DB_PASSWORD, prod/billing/db-pass

3. Audit Everything

Implementation is just the first step. You must enable logging for all secret access. If an engineer accesses a production credential, there should be an immutable trail. Both Vault and AWS provide detailed logs that tell you exactly who accessed what and when.

Final Thoughts

Moving to a formal secret management system feels like a lot of overhead at first. However, the security and scalability it provides are worth the initial friction. You stop worrying about accidental leaks and start focusing on shipping features. If you’re just starting, pick your most sensitive database password and move it to a manager today. Small steps lead to a hardened infrastructure.

Share: