Environment variable management and secret rotation
// Configuration management with validation
const Joi = require('joi');
// Define schema for all environment variables
const envSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'staging', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3000),
HOST: Joi.string().default('0.0.0.0'),
// Database
DATABASE_URL: Joi.string().uri().required(),
DB_POOL_MIN: Joi.number().default(2),
DB_POOL_MAX: Joi.number().default(10),
// Redis
REDIS_URL: Joi.string().uri().required(),
// Auth
JWT_SECRET: Joi.string().min(32).required(),
JWT_EXPIRY: Joi.string().default('24h'),
// AWS
AWS_REGION: Joi.string().default('us-east-1'),
S3_BUCKET: Joi.string().required(),
// External APIs
STRIPE_SECRET_KEY: Joi.string().when('NODE_ENV', {
is: 'production',
then: Joi.required(),
otherwise: Joi.optional(),
}),
// Feature flags
ENABLE_CACHE: Joi.boolean().default(true),
ENABLE_RATE_LIMIT: Joi.boolean().default(true),
// Logging
LOG_LEVEL: Joi.string()
.valid('error', 'warn', 'info', 'debug')
.default('info'),
})
.unknown(true); // Allow other env vars
// Validate and export
const { error, value: config } = envSchema.validate(process.env, {
abortEarly: false,
});
if (error) {
const missing = error.details.map((d) => d.message).join('\n ');
console.error(`Configuration validation error:\n ${missing}`);
process.exit(1);
}
module.exports = {
env: config.NODE_ENV,
isProduction: config.NODE_ENV === 'production',
isDevelopment: config.NODE_ENV === 'development',
server: {
port: config.PORT,
host: config.HOST,
},
database: {
url: config.DATABASE_URL,
pool: {
min: config.DB_POOL_MIN,
max: config.DB_POOL_MAX,
},
},
redis: {
url: config.REDIS_URL,
},
auth: {
jwtSecret: config.JWT_SECRET,
jwtExpiry: config.JWT_EXPIRY,
},
aws: {
region: config.AWS_REGION,
s3Bucket: config.S3_BUCKET,
},
features: {
cache: config.ENABLE_CACHE,
rateLimit: config.ENABLE_RATE_LIMIT,
},
logging: {
level: config.LOG_LEVEL,
},
};
#!/bin/bash
set -euo pipefail
# Secret rotation script for AWS Secrets Manager
SECRET_NAME="myapp/production/database"
DB_HOST="mydb.cluster-abc123.us-east-1.rds.amazonaws.com"
DB_NAME="myapp"
DB_USER="app_user"
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"; }
rotate_database_password() {
log "Starting database password rotation..."
# 1. Generate new password
NEW_PASSWORD=$(openssl rand -base64 32 | tr -d '/+=')
log "New password generated"
# 2. Update password in database
CURRENT_SECRET=$(aws secretsmanager get-secret-value --secret-id "$SECRET_NAME" --query 'SecretString' --output text)
CURRENT_PASSWORD=$(echo "$CURRENT_SECRET" | jq -r '.password')
PGPASSWORD="$CURRENT_PASSWORD" psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c "ALTER USER $DB_USER PASSWORD '$NEW_PASSWORD';"
log "Database password updated"
# 3. Update secret in Secrets Manager
NEW_SECRET=$(echo "$CURRENT_SECRET" | jq --arg pw "$NEW_PASSWORD" '.password = $pw')
aws secretsmanager put-secret-value --secret-id "$SECRET_NAME" --secret-string "$NEW_SECRET"
log "Secret Manager updated"
# 4. Restart application to pick up new credentials
kubectl rollout restart deployment/web-app -n production
kubectl rollout status deployment/web-app -n production --timeout=300s
log "Application restarted with new credentials"
# 5. Verify connectivity
sleep 10
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' https://app.example.com/health)
if [[ "$HTTP_CODE" == "200" ]]; then
log "Health check passed - rotation complete!"
else
log "ERROR: Health check failed (HTTP $HTTP_CODE)"
log "Rolling back..."
# Rollback database password
PGPASSWORD="$NEW_PASSWORD" psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c "ALTER USER $DB_USER PASSWORD '$CURRENT_PASSWORD';"
aws secretsmanager put-secret-value --secret-id "$SECRET_NAME" --secret-string "$CURRENT_SECRET"
kubectl rollout restart deployment/web-app -n production
log "Rollback complete"
exit 1
fi
}
rotate_database_password
Environment variables configure applications without code changes. The twelve-factor app methodology stores config in the environment. .env files provide local defaults—never commit them to Git. dotenv libraries load .env files in development. Production secrets live in AWS Secrets Manager, HashiCorp Vault, or Kubernetes Secrets. Secret rotation updates credentials without downtime using dual-read patterns. AWS Secrets Manager supports automatic rotation with Lambda functions. Connection pooling libraries handle credential refresh transparently. Config validation at startup fails fast on missing variables. Environment-specific config files (production.env, staging.env) separate concerns. envsubst substitutes variables in templates. Secret scanning in CI prevents accidental credential commits.