# Secrets Management in Attune Worker Service ## Overview The Attune Worker Service includes a robust secrets management system that securely stores, retrieves, and injects secrets into action execution environments. Secrets are encrypted at rest in the database and decrypted on-demand during execution. ## Architecture ### Components 1. **SecretManager** (`crates/worker/src/secrets.rs`) - Core component responsible for secret operations - Handles fetching, decryption, and environment variable preparation - Integrated into `ActionExecutor` for seamless secret injection 2. **Database Storage** (`attune.key` table) - Stores secrets with ownership scoping (system, pack, action, sensor, identity) - Supports both encrypted and plaintext values - Tracks encryption key hash for validation 3. **Encryption System** - Uses **AES-256-GCM** for authenticated encryption - Derives encryption key from configured password using SHA-256 - Generates random nonces for each encryption operation ## Secret Ownership Hierarchy Secrets are organized in a hierarchical ownership model with increasing specificity: ### 1. System-Level Secrets - **Owner Type**: `system` - **Scope**: Available to all actions across all packs - **Use Case**: Global configuration (API endpoints, common credentials) ### 2. Pack-Level Secrets - **Owner Type**: `pack` - **Scope**: Available to all actions within a specific pack - **Use Case**: Pack-specific credentials, service endpoints ### 3. Action-Level Secrets - **Owner Type**: `action` - **Scope**: Available only to a specific action - **Use Case**: Action-specific credentials, sensitive parameters ### Override Behavior When an action is executed, secrets are fetched in the following order: 1. System secrets 2. Pack secrets (override system secrets with same name) 3. Action secrets (override pack/system secrets with same name) This allows for flexible secret management where more specific secrets override less specific ones. ## Encryption Format ### Encrypted Value Format ``` nonce:ciphertext ``` Both components are Base64-encoded: - **Nonce**: 12-byte random value (96 bits) for AES-GCM - **Ciphertext**: Encrypted payload with authentication tag Example: ``` Xk3mP9qRsT6uVwYz:SGVsbG8gV29ybGQhIFRoaXMgaXMgYW4gZW5jcnlwdGVkIG1lc3NhZ2U= ``` ### Encryption Key Derivation The encryption key is derived from the configured password using SHA-256: ``` encryption_key = SHA256(password) ``` This produces a 32-byte (256-bit) key suitable for AES-256. ### Key Hash Validation Each encrypted secret can optionally store the hash of the encryption key used to encrypt it: ``` key_hash = SHA256(encryption_key) ``` This allows validation that the correct key is being used for decryption. ## Configuration ### Security Configuration Add to your `config.yaml`: ```yaml security: # Encryption key for secrets (REQUIRED for encrypted secrets) encryption_key: "your-secret-encryption-password-here" # Or use environment variable # ATTUNE__SECURITY__ENCRYPTION_KEY=your-secret-encryption-password-here ``` ⚠️ **Important Security Notes:** - The encryption key should be a strong, random password (minimum 32 characters recommended) - Store the encryption key securely (e.g., using a secrets manager, not in version control) - If the encryption key is lost, encrypted secrets cannot be recovered - Changing the encryption key requires re-encrypting all secrets ### Environment Variables Override configuration via environment variables: ```bash export ATTUNE__SECURITY__ENCRYPTION_KEY="your-encryption-key" ``` ## Usage Examples ### Storing Secrets (via API) #### System-Level Secret ```bash curl -X POST http://localhost:8080/api/v1/keys \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ref": "system.api_endpoint", "owner_type": "system", "name": "api_endpoint", "value": "https://api.example.com", "encrypted": false }' ``` #### Pack-Level Secret (Encrypted) ```bash curl -X POST http://localhost:8080/api/v1/keys \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ref": "mypack.api_key", "owner_type": "pack", "owner_pack": 1, "name": "api_key", "value": "sk_live_abc123def456", "encrypted": true }' ``` #### Action-Level Secret ```bash curl -X POST http://localhost:8080/api/v1/keys \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "ref": "mypack.myaction.oauth_token", "owner_type": "action", "owner_action": 42, "name": "oauth_token", "value": "ya29.a0AfH6SMBx...", "encrypted": true }' ``` ### Accessing Secrets in Actions Secrets are automatically injected as environment variables during execution. The secret name is converted to uppercase and prefixed with `SECRET_`. #### Python Action Example ```python #!/usr/bin/env python3 import os # Access secrets via environment variables api_key = os.environ.get('SECRET_API_KEY') db_password = os.environ.get('SECRET_DB_PASSWORD') oauth_token = os.environ.get('SECRET_OAUTH_TOKEN') if not api_key: print("Error: SECRET_API_KEY not found") exit(1) # Use the secrets print(f"Connecting to API with key: {api_key[:8]}...") ``` #### Shell Action Example ```bash #!/bin/bash # Access secrets echo "API Key: ${SECRET_API_KEY:0:8}..." echo "Database: ${SECRET_DB_HOST}" # Use in commands curl -H "Authorization: Bearer $SECRET_API_TOKEN" \ https://api.example.com/data ``` ### Environment Variable Naming Rules Secret names are transformed as follows: - Prefix: `SECRET_` - Convert to uppercase - Replace hyphens with underscores Examples: - `api_key` → `SECRET_API_KEY` - `db-password` → `SECRET_DB_PASSWORD` - `oauth_token` → `SECRET_OAUTH_TOKEN` ## Security Best Practices ### 1. Encryption Key Management - **Generate Strong Keys**: Use at least 32 random characters - **Secure Storage**: Store in a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) - **Rotation**: Plan for key rotation (requires re-encrypting all secrets) - **Backup**: Keep encrypted backup of the encryption key ### 2. Secret Storage - **Always Encrypt Sensitive Data**: Use `encrypted: true` for passwords, tokens, API keys - **Plaintext for Non-Sensitive**: Use `encrypted: false` for URLs, usernames, configuration - **Least Privilege**: Use action-level secrets for the most sensitive data ### 3. Action Development - **Never Log Secrets**: Avoid printing secret values in action output - **Mask in Errors**: Don't include secrets in error messages - **Clear After Use**: In long-running processes, clear secrets from memory when done ### 4. Access Control - **RBAC**: Limit who can create/read secrets using Attune's RBAC system - **Audit Logging**: Enable audit logging for secret access (future feature) - **Regular Reviews**: Periodically review and rotate secrets ## Implementation Details ### Encryption Process ```rust // 1. Derive encryption key from password let key = SHA256(password); // 2. Generate random nonce let nonce = random_bytes(12); // 3. Encrypt plaintext let ciphertext = AES256GCM.encrypt(key, nonce, plaintext); // 4. Format as "nonce:ciphertext" (base64-encoded) let encrypted_value = format!("{}:{}", base64(nonce), base64(ciphertext) ); ``` ### Decryption Process ```rust // 1. Parse "nonce:ciphertext" format let (nonce_b64, ciphertext_b64) = encrypted_value.split_once(':'); let nonce = base64_decode(nonce_b64); let ciphertext = base64_decode(ciphertext_b64); // 2. Validate encryption key hash (if present) if key_hash != SHA256(encryption_key) { return Error("Key mismatch"); } // 3. Decrypt ciphertext let plaintext = AES256GCM.decrypt(encryption_key, nonce, ciphertext); ``` ### Secret Injection Flow ``` 1. ActionExecutor prepares execution context 2. SecretManager fetches secrets for action a. Query system-level secrets b. Query pack-level secrets c. Query action-level secrets d. Merge with later overriding earlier 3. Decrypt encrypted secrets 4. Transform to environment variables 5. Inject into execution context 6. Action executes with secrets available ``` ## Troubleshooting ### "No encryption key configured" **Problem**: Worker service cannot decrypt secrets. **Solution**: Set the encryption key in configuration: ```yaml security: encryption_key: "your-encryption-key-here" ``` ### "Encryption key hash mismatch" **Problem**: The encryption key used to decrypt doesn't match the key used to encrypt. **Solution**: - Verify you're using the correct encryption key - Check if encryption key was recently changed - May need to re-encrypt secrets with new key ### "Decryption failed" **Problem**: Secret cannot be decrypted. **Causes**: - Wrong encryption key - Corrupted encrypted value - Invalid format **Solution**: - Verify encryption key is correct - Check secret value format (should be "nonce:ciphertext") - Try re-encrypting the secret ### Secrets Not Available in Action **Problem**: Environment variables like `SECRET_API_KEY` are not set. **Checklist**: - Verify secret exists in database with correct owner type - Check secret name matches expected format - Ensure action's pack has access to the secret - Check worker logs for "Failed to fetch secrets" warnings ## API Reference ### SecretManager Methods #### `fetch_secrets_for_action(action: &Action) -> Result>` Fetches all secrets relevant to an action (system + pack + action level). #### `encrypt_value(plaintext: &str) -> Result` Encrypts a plaintext value using the configured encryption key. #### `prepare_secret_env(secrets: &HashMap) -> HashMap` Transforms secret names to environment variable format. ## Future Enhancements ### Planned Features - [ ] Secret versioning and rollback - [ ] Audit logging for secret access - [ ] Integration with external secret managers (Vault, AWS Secrets Manager) - [ ] Automatic secret rotation - [ ] Secret expiration and TTL - [ ] Multi-key encryption (key per pack/action) - [ ] Secret templates and inheritance ### Under Consideration - [ ] Dynamic secret generation - [ ] Just-in-time secret provisioning - [ ] Secret usage analytics - [ ] Integration with certificate management ## References - [AES-GCM Encryption](https://en.wikipedia.org/wiki/Galois/Counter_Mode) - [NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final) - Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) - [Key Management Best Practices](https://www.owasp.org/index.php/Key_Management_Cheat_Sheet)