Secrets Manager
Securely store and manage sensitive data using AWS Secrets Manager with Nimbus.
Overview
AWS Secrets Manager helps you protect secrets needed to access your applications, services, and IT resources. Nimbus integrates seamlessly with Secrets Manager to provide secure secret storage with automatic encryption, rotation capabilities, and fine-grained access control.
Basic Usage
Create a secret placeholder during deployment:
typescript
import { Nimbus } from 'nimbus-framework';
const nimbus = new Nimbus();
// ✅ CORRECT: Create secret without hardcoded values
const databaseCredentials = nimbus.Secret({
name: 'database-credentials',
description: 'Production database connection details'
// No value here - will be set securely at runtime
});
const apiKeys = nimbus.Secret({
name: 'external-api-keys',
description: 'Third-party service API keys'
});Configuration Options
Basic Secret
typescript
const secret = nimbus.Secret({
name: 'my-secret',
description: 'Description of what this secret contains'
});Advanced Configuration
typescript
const secret = nimbus.Secret({
name: 'advanced-secret',
description: 'Advanced secret configuration',
kmsKeyId: 'arn:aws:kms:region:account:key/key-id', // Custom KMS key
automaticRotation: true, // Enable automatic rotation
rotationLambdaArn: 'arn:aws:lambda:region:account:function:rotation-function'
});Runtime Usage
Use runtime helpers to access secrets in Lambda functions:
typescript
api.route('POST', '/auth/login', async (event) => {
const { runtime } = await import('nimbus-framework');
try {
// Get database credentials
const dbCreds = await runtime.secrets.getJson('database-credentials');
if (!dbCreds) {
return {
statusCode: 500,
body: JSON.stringify({ error: 'Database configuration not available' })
};
}
// Use credentials to authenticate user
const user = await authenticateUser(email, password, dbCreds);
return {
statusCode: 200,
body: JSON.stringify({ user })
};
} catch (error) {
console.error('Authentication error:', error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Authentication failed' })
};
}
});Runtime Methods
Getting Secrets
typescript
const { runtime } = await import('nimbus-framework');
// Get secret as JSON object
const dbCreds = await runtime.secrets.getJson('database-credentials');
// Returns: { host: "db.com", username: "user", password: "pass" }
// Get secret as string
const apiKey = await runtime.secrets.getString('stripe-api-key');
// Returns: "sk_live_1234567890abcdef"
// Get raw secret (auto-detects JSON vs string)
const secret = await runtime.secrets.get('my-secret');Setting Secrets (Admin Operations)
typescript
// Set secret as JSON object
await runtime.secrets.set('database-credentials', {
host: 'prod-db.example.com',
username: 'app_user',
password: 'secure-password'
});
// Set secret as string
await runtime.secrets.set('api-key', 'sk_live_new_key');Caching Options
typescript
// Default caching (5 minutes)
const secret1 = await runtime.secrets.get('my-secret');
// Custom cache TTL (1 minute)
const secret2 = await runtime.secrets.get('my-secret', { ttl: 60000 });
// Disable caching
const secret3 = await runtime.secrets.get('my-secret', { cache: false });Secret Management Patterns
Database Credentials
typescript
const dbSecret = nimbus.Secret({
name: 'database-credentials',
description: 'Database connection credentials'
});
// In Lambda function
const credentials = await runtime.secrets.getJson('database-credentials');
const connection = await createConnection({
host: credentials.host,
username: credentials.username,
password: credentials.password,
database: credentials.database
});API Keys
typescript
const apiKeysSecret = nimbus.Secret({
name: 'external-api-keys',
description: 'Third-party service API keys'
});
// In Lambda function
const apiKeys = await runtime.secrets.getJson('external-api-keys');
const stripe = new Stripe(apiKeys.stripe);
const sendgrid = new SendGrid(apiKeys.sendgrid);JWT Configuration
typescript
const jwtSecret = nimbus.Secret({
name: 'jwt-configuration',
description: 'JWT signing and encryption keys'
});
// In Lambda function
const jwtConfig = await runtime.secrets.getJson('jwt-configuration');
const token = jwt.sign(payload, jwtConfig.signing_key, {
issuer: jwtConfig.issuer,
audience: jwtConfig.audience
});Security Best Practices
1. Never Hardcode Secrets
typescript
// ❌ NEVER do this
const secret = nimbus.Secret({
name: 'my-secret',
value: 'hardcoded-secret-value' // BAD!
});
// ✅ DO this instead
const secret = nimbus.Secret({
name: 'my-secret',
description: 'My application secret'
// No value - set securely at runtime
});2. Use Descriptive Names
typescript
// ✅ Good - clear purpose
const databaseCredentials = nimbus.Secret({
name: 'prod-database-credentials',
description: 'Production database connection details'
});
// ❌ Avoid - unclear purpose
const secret1 = nimbus.Secret({
name: 'secret1'
});3. Implement Proper Error Handling
typescript
try {
const secret = await runtime.secrets.get('my-secret');
if (!secret) {
// Handle missing secret gracefully
return fallbackBehavior();
}
// Use secret
} catch (error) {
console.error('Failed to retrieve secret:', error);
// Implement fallback or fail gracefully
}4. Use Caching Appropriately
typescript
// ✅ Good - cache for performance
const secret = await runtime.secrets.get('my-secret');
// ✅ Good - fresh data for sensitive operations
const secret = await runtime.secrets.get('my-secret', { cache: false });Secret Rotation
Secrets can be rotated without code changes:
Manual Rotation
typescript
// Admin endpoint for secret rotation
api.route('PUT', '/admin/secrets/rotate', async (event) => {
const { runtime } = await import('nimbus-framework');
// Verify admin access
if (!await verifyAdminAccess(event)) {
return { statusCode: 403, body: 'Forbidden' };
}
const { secretName, newValue } = JSON.parse(event.body);
// Rotate the secret
await runtime.secrets.set(secretName, newValue);
return {
statusCode: 200,
body: JSON.stringify({ message: 'Secret rotated successfully' })
};
});Automatic Rotation
typescript
const secret = nimbus.Secret({
name: 'database-password',
description: 'Database password with automatic rotation',
automaticRotation: true,
rotationLambdaArn: 'arn:aws:lambda:region:account:function:rotate-db-password'
});Cost Considerations
- Secret Storage: $0.40 per secret per month
- API Calls: $0.05 per 10,000 requests
- Rotation: Additional Lambda execution costs if using automatic rotation
Example monthly cost:
- 5 secrets: $2.00
- 100K API calls: $0.50
- Total: ~$2.50/month
Monitoring and Compliance
CloudTrail Integration
All secret access is automatically logged to CloudTrail:
- Who accessed which secret
- When the access occurred
- Source IP and user agent
- Success/failure status
CloudWatch Metrics
Monitor secret usage with CloudWatch:
- Number of secret retrievals
- Failed access attempts
- Rotation events
Compliance Features
- Encryption: All secrets encrypted at rest with AWS KMS
- Access Control: Fine-grained IAM permissions
- Audit Trail: Complete access logging
- Rotation: Automated secret rotation capabilities
Error Handling
Common error scenarios and how to handle them:
typescript
try {
const secret = await runtime.secrets.get('my-secret');
} catch (error) {
if (error.name === 'ResourceNotFoundException') {
// Secret doesn't exist
console.log('Secret not found');
} else if (error.name === 'DecryptionFailureException') {
// KMS decryption failed
console.error('Failed to decrypt secret');
} else if (error.name === 'AccessDeniedException') {
// Insufficient permissions
console.error('Access denied to secret');
} else {
// Other AWS service errors
console.error('Unexpected error:', error);
}
}Integration with Other Services
Database Connections
typescript
const dbCreds = await runtime.secrets.getJson('database-credentials');
const pool = new Pool({
host: dbCreds.host,
port: dbCreds.port,
database: dbCreds.database,
user: dbCreds.username,
password: dbCreds.password,
ssl: { rejectUnauthorized: false }
});External APIs
typescript
const apiKeys = await runtime.secrets.getJson('api-keys');
// Stripe integration
const stripe = new Stripe(apiKeys.stripe);
// SendGrid integration
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(apiKeys.sendgrid);
// Custom API integration
const response = await fetch('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKeys.external_service}`,
'Content-Type': 'application/json'
}
});Related
- Runtime Helpers - Complete runtime API reference
- Parameter Store - Configuration management
- Examples: Secrets Manager - Complete example
- Security Best Practices - Overall security guidance