In the world of DevOps, maintaining a secure and efficient workflow is crucial. One of the most important aspects of a DevOps pipeline is managing secrets and configurations. Whether it’s API keys, passwords, or database credentials, keeping sensitive data secure while ensuring it’s accessible to the appropriate systems is a challenge that every DevOps engineer faces.
In the context of DevOps, secrets refer to sensitive information such as:
These secrets are required for accessing various services, systems, or APIs during deployment and runtime. Without secure management, secrets can become a major vulnerability in your infrastructure.
Configuration refers to the non-sensitive settings that control how an application or system behaves. These include:
Configuration data is often environment-specific (development, staging, production) and needs to be stored in a way that is easily accessible and maintainable.
Effective management of secrets and configuration is essential for the following reasons:
Instead of hardcoding secrets in your application or storing them in environment files, it’s best to use specialized tools designed to store and manage secrets securely. Some popular tools include:
Here’s a basic example of how to store and retrieve a secret in HashiCorp Vault:
# Authenticate with Vault
vault login <your-token>
# Store a secret
vault kv put secret/myapp/db-password value="mySuperSecretPassword"
# Retrieve the secret
vault kv get secret/myapp/db-password
In this example, a secret (the database password) is stored under the path secret/myapp/db-password
, and Vault can securely retrieve it when needed.
For non-sensitive configurations (such as application settings), environment variables are a common approach. They allow you to inject configuration values directly into the application environment at runtime without having to hardcode them into the application code.
Here’s how to use environment variables to manage configurations for a Node.js application:
// Load environment variables
require('dotenv').config();
// Access environment variables
const dbHost = process.env.DB_HOST;
const dbUser = process.env.DB_USER;
const dbPassword = process.env.DB_PASSWORD;
console.log(`Connecting to database ${dbHost} with user ${dbUser}`);
To load the environment variables, you could store them in a .env
file (for development) or set them in the CI/CD pipeline during deployment.
Example .env
file:
DB_HOST=localhost
DB_USER=myuser
DB_PASSWORD=mypassword
Secrets should be encrypted both at rest (when stored) and in transit (when transmitted across networks). This ensures that even if someone gains unauthorized access to your storage or network, the secrets are protected.
Many secrets management tools, such as HashiCorp Vault and AWS Secrets Manager, automatically handle encryption at rest.
Access to secrets and configuration data should be restricted based on roles and responsibilities. By implementing Role-Based Access Control (RBAC), you can ensure that only the appropriate services or team members have access to specific secrets or configurations.
For instance, you may want your database password to be accessible only by the database service, but not by other microservices. Similarly, sensitive configurations for production environments should only be accessible to deployment pipelines.
Here’s an example of a Kubernetes Role and RoleBinding to restrict access to a secret:
# Define a Role with access to the secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
# Bind the Role to a specific service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-reader-binding
namespace: default
subjects:
- kind: ServiceAccount
name: my-app-service-account
namespace: default
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
This configuration ensures that only the service account my-app-service-account
can access the secrets in the default
namespace.
Secrets should be rotated periodically to minimize the risk of leaks. Automation tools like HashiCorp Vault and AWS Secrets Manager allow you to configure automatic secret rotation to avoid manual intervention.
To configure automatic secret rotation, Vault can be set up to periodically update credentials for services such as databases. Here’s an example of setting up a dynamic secret engine for database credentials:
# Enable the database secret engine
vault secrets enable database
# Configure a MySQL connection (example)
vault write database/config/mysql connection_url="mysql://root:@localhost:3306/mydb"
# Create a role to generate dynamic database credentials
vault write database/roles/my-role \
db_name=mysql \
creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT ALL PRIVILEGES ON mydb.* TO '{{name}}'@'%';" \
default_ttl="1h" \
max_ttl="24h"
# Generate a new set of credentials
vault read database/creds/my-role
This setup ensures that Vault generates new database credentials dynamically, and the old ones are automatically rotated according to the TTL (Time-to-Live) setting.
A powerful tool for managing secrets that supports multiple backends and integrates well with other infrastructure components.
A fully managed service that provides secret storage, retrieval, and automatic rotation within the AWS ecosystem.
Microsoft’s native secret management service, offering secure storage and access control in Azure.
Native secret management in Kubernetes clusters, ideal for containerized applications.