← Back to list

Secrets Management: A Practical Guide from Cloud Native to GitOps

Effective secrets management is the discipline of controlling access to the digital keys — API tokens, database passwords and private…

A. B. M. Mahmudul Hasan in Infocyph · 2026-01-12 11:35 · 0 claps · 5.8 min read paywalled
#gitops #git #mozilla-sops #agp
Open on Medium ↗
Wiki topics: BIZ · Business Strategy 🔓 · Open Source 🚀 · Self Improvement

Secrets Management: A Practical Guide from Cloud Native to GitOps

Effective secrets management is the discipline of controlling access to the digital keys — API tokens, database passwords and private certificates — that unlock your infrastructure. As organizations adopt modern DevOps practices, the “old ways” of sharing .env files over chat or hardcoding passwords are no longer just bad practice; they are a critical vulnerability.

This guide provides a comprehensive strategy. We first explore the Cloud-Native approach (using AWS/Azure/GCP native vaults) and then detail the GitOps approach (managing encrypted secrets in code repositories), concluding with a hands-on technical walkthrough.

The Cloud-Native Approach (AWS, Azure, GCP)

If you prefer not to manage encryption keys yourself, you can use the managed vault provided by your cloud vendor. In this model, secrets are never stored in your code repository. Instead, they live in a secure, centralized vault and are injected into your application at runtime.

The workflow is uniform across all major providers: Store the secret in the vault, Authorize your application’s identity (IAM) to read it and Fetch it at runtime.

1. Storage (Creating the Secret)

Instead of writing a password into a file, you push it securely to the cloud provider’s vault using their CLI or Console.

  • AWS Secrets Manager:
aws secretsmanager create-secret --name production/db_pass --secret-string "CorrectHorseBatteryStaple"
  • Google Secret Manager:
printf "CorrectHorseBatteryStaple" | gcloud secrets create production-db-pass --data-file=-
  • Azure Key Vault:
az keyvault secret set --vault-name "MyProductionVault" --name "DbPassword" --value "CorrectHorseBatteryStaple"

2. Access (The “Secret Zero” Solution)

The most powerful feature of cloud vaults is Identity Access Management (IAM). You do not need a password to access the password.

  • On AWS: You assign an IAM Role to your EC2 instance or Lambda function.
  • On Azure: You assign a Managed Identity to your VM or App Service.
  • On GCP: You assign a Service Account to your Cloud Run or GKE workload.

You then create a policy that says: “Only this specific Role/Identity can read the production/db_pass secret."

3. Retrieval (Runtime Injection)

Your application retrieves the secret in one of two ways:

  • SDK Retrieval: Your code uses the cloud SDK (e.g., boto3 for Python) to call the vault API on startup.
  • Environment Injection: Container orchestrators (like ECS or Kubernetes) can fetch the secret and inject it as an environment variable (DB_PASS) when the container starts. The app sees a standard environment variable, unaware it came from a vault.

The GitOps Strategy (Age & PGP)

Many teams prefer to keep all configuration, including secrets, in their Git repository. This ensures that the “state” of the infrastructure is versioned and audit-able. However, you cannot commit plain text secrets.

The Tooling: Age vs. PGP

To store secrets in Git, we must encrypt the file.

  • PGP (Pretty Good Privacy): The legacy standard. While powerful (supporting signing, web-of-trust), it is complex to automate and prone to configuration errors.
  • Age (Actually Good Encryption): The modern standard. It is designed specifically for file encryption. It uses small, explicit keys (just text strings), has no config files, and is much easier to use in CI/CD pipelines.

Verdict: For modern development, Age combined with SOPS (Secrets OPerationS) is the industry-standard recommendation.

Technical Playbook (The “Secret-Ops” Flow)

This section details the “SOPS + Age” workflow covering the lifecycle: Local Development, Secure Backup in Git and Production Deployment.

Phase 1: Local Development

Goal: Keep secrets working on local computer but out of the Git history.

Step 1: Create the files In our project root, create two files:

  • .env (This holds our real local secrets).
  • .env.example (This holds fake/empty values for documentation).

Step 2: Ignore the real file Open (or create) our .gitignore file and add this immediately to prevent accidents:

.env
.env.local
*.enc.env

Step 3: Fill them out

  • .env: DB_PASS=correct_password_123
  • .env.example: DB_PASS=

Phase 2: Storing in Git (Encrypted)

Goal: Commit secrets to Git so we have a backup, but encrypt them so no one (not even Git Providers) can read them.

1. Install Tools (On local machine)

Age (The encryption backend):

## Install Age
# macOS
brew install age
# Debian/Ubuntu
sudo apt update && sudo apt install age

SOPS (The editor): Download the latest binary or .deb from the(https://github.com/getsops/sops/releases).

# Download the binary
curl -LO https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64

# Move the binary in to our PATH
mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops

# Make the binary executable
chmod +x /usr/local/bin/sops

2. Key Generation: Generate a key file. Keep this file safe! If we lose it, we lose our encrypted data.

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt

Output Example: # public key: age1... AGE-SECRET-KEY-1...

Note: This creates a private key on disk. We will need to copy this file to other computers or servers to decrypt data there.

3. Configure the Project: Create a file named .sops.yaml in project root. This tells SOPS which key to use for this specific project.

#.sops.yaml
creation_rules:
  - path_regex: \.env$
    key_groups:
      - age:
        - "age1ql3z7hjy54pw3..." # COPY THE PUBLIC KEY HERE (from keys.txt)

4. Encrypt and Commit: Now, we can encrypt our local .env file into a new file that is safe to commit.

sops --encrypt .env >.env.encrypted

We can now securely run:

git add .env.encrypted .sops.yaml
git commit -m "Add encrypted production secrets"
git push

Result: The file in Git looks like random garbage text, protecting our data.

5. Editing Secrets Later: To update secrets, we do not need to decrypt manually but edit the encrypted file directly using SOPS:

sops .env.encrypted

This opens the file in our default editor (vim/nano/…), shows it decrypted and re-encrypts it automatically when we save and close.

Phase 3: Production Server Setup (Debian/Ubuntu)

Goal: Get the secrets onto the server and readable ONLY by our application.

Method A: The Secure File (Recommended for PHP/Web Apps)

1. Transfer the Key (One-time setup): We need the keys.txt generated in Phase 2 on our server.

  • Manual: Copy the content of ~/.config/sops/age/keys.txt from our PC.
  • Server: Paste it into the same location: ~/.config/sops/age/keys.txt.

2. Decrypt on Server: Install sops and age on the server (same steps as Phase 2). Then, navigate to project folder and run:

sops --decrypt .env.encrypted >.env

Now we have the real .env file on server.

3. Lock Down Permissions (Critical Step): We don’t want other users on the server reading the file. Assuming we are using Nginx/Apache (which usually runs as www-data):

# 1. Set the owner to current user and the group to the Web Server
sudo chown $USER:www-data .env

# 2. Set permissions:
# - User ($USER): Read & Write (6)
# - Group (www-data): Read Only (4)
# - Others: No Access (0)
sudo chmod 640 .env

4. Verification: Test if it’s secure by trying to read it as a different user:

# Output should be: Permission denied (trying with example user: nobody)
sudo -u nobody cat.env

Method B: systemd Override (Recommended for Daemons/Workers)

If we are running a background worker (like a Laravel Queue, Node.js or Python script) managed by systemd, placing a file is less secure than injecting it directly into the process environment.

1. Edit the service configuration

sudo systemctl edit <our-service-name>

2. Add variables Add this block. Note that systemctl edit creates an override file, so it won't be overwritten by updates.

Environment="DB_PASSWORD=production_password_here"
Environment="API_KEY=another_secret"

Tip: If we have many variables, can use EnvironmentFile=/path/to/locked/down/.env instead.

3. Reload

sudo systemctl daemon-reload
sudo systemctl restart <our-service-name>

Phase 4: Emergency Response (Scrubbing Git History)

Accidentally committed a plain text .env file (not the encrypted one)? Simply deleting it, is not enough. It still lives in the .git history folder.

The Fix: Use git-filter-repo (The modern replacement for BFG/filter-branch).

  1. Rotate: Change the passwords immediately. The old ones are compromised.
  2. Install: pip install git-filter-repo
  3. Scrub: git filter-repo — invert-paths — path .env
  4. Force Push: git push --force --all

Warning: This is destructive. All team members must re-clone the repository after this step.

Summary of Daily Workflow

Once setup is complete, team’s daily workflow looks like this:

Change a secret locally:

  • Edit .env for our local app to work.
  • Run sops .env.encrypted and add the new key/value there too.
  • git commit .env.encrypted.

Deploy:

  • git pull on server.
  • Run sops --decrypt .env.encrypted >.env.
  • Restart web server (e.g., sudo systemctl reload php8.x-fpm).

Conclusion

Secrets management is not a “set it and forget it” task; it is an active operational discipline. By choosing one of the two paths outlined in this guide — Cloud Native for pure infrastructure teams or GitOps with Age for code-centric teams — you eliminate the most common attack vectors.

Whether you rely on Secrets Manager or an encrypted .env file, the golden rule remains the same: Plain text secrets must never enter your version control system. Implementing the workflows above will ensure your organization moves from "security by obscurity" to a true Zero Trust posture.


메타데이터
post_id
3a9c9c4072d0
slug
secrets-management-a-practical-guide-from-cloud-native-to-gitops-3a9c9c4072d0
url
https://blog.infocyph.com/secrets-management-a-practical-guide-from-cloud-native-to-gitops-3a9c9c4072d0
canonical_url
https://blog.infocyph.com/secrets-management-a-practical-guide-from-cloud-native-to-gitops-3a9c9c4072d0
author_url
https://medium.com/@abmmhasan
status
ok
fetched_at
2026-06-23 06:34:20