engineering-handbook/secrets-and-config.md

69 lines
1.9 KiB
Markdown
Raw Normal View History

# Secrets and Configuration
## The Boundary
There is a strict separation between sensitive and non-sensitive configuration. No gray areas.
## AWS Secrets Manager
Use Secrets Manager for **all** sensitive values:
- API access tokens and keys
- Signing values used for request verification
- Webhook URLs that act as implicit authentication
- Database connection strings with embedded passwords
- Any value that would be dangerous if leaked
When in doubt about whether something qualifies as sensitive, treat it as sensitive.
### Naming Convention
```
stack-name/value-name
```
Examples:
- `my-stack/slack-signing`
- `my-stack/stripe-key`
## SSM Parameter Store
Use Parameter Store **only** for non-sensitive configuration:
- Feature flags
- Endpoint URLs (public, non-authenticated)
- Schedule expressions
- Channel IDs and non-sensitive identifiers
## Lambda Pattern
1. Store the sensitive value in Secrets Manager
2. Grant the function's IAM role `secretsmanager:GetSecretValue` scoped to only the values it needs
3. Read the value on cold start via the AWS SDK
4. Cache it in a module-level variable so subsequent invocations reuse it
```python
import boto3
import json
_client = boto3.client('secretsmanager')
_cached = None
def get_config():
global _cached
if _cached is None:
resp = _client.get_secret_value(SecretId='my-stack/config')
_cached = json.loads(resp['SecretString'])
return _cached
def handler(event, context):
config = get_config()
# use config values
```
## What NOT to Do
- **Never use Lambda environment variables for sensitive values.** Even with `NoEcho` CloudFormation parameters, the values end up as plaintext in the Lambda console and are readable by anyone with `GetFunctionConfiguration` access.
- **Never commit `.env` files** containing real values to a repository.
- **Never store sensitive values** in Notion, Slack messages, or other plaintext documents.