$npx -y skills add aws/agent-toolkit-for-aws --skill aws-secrets-managerSecret safety for AWS Secrets Manager, secret management, credentials, API keys, tokens, and passwords. Prevents AI agents from directly fetching secret values and teaches runtime dynamic references with asm-exec so plaintext never enters the LLM context window.
| 1 | # Using Secrets Safely with Agents |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | When AI agents handle secrets, credentials, API keys, tokens, or passwords with |
| 6 | shell or AWS API access, they can call `aws secretsmanager get-secret-value` |
| 7 | and receive plaintext values in their context window. This creates risk: |
| 8 | secrets may leak into logs, conversation history, or downstream tool calls. |
| 9 | |
| 10 | This skill teaches a safer pattern: **dynamic references** resolved at runtime |
| 11 | by a wrapper script (`asm-exec`), so the agent never sees the secret value. |
| 12 | |
| 13 | > **Best-effort defense, not a security boundary.** This prevents the most common |
| 14 | > leakage path but cannot stop all evasion vectors. Combine with IAM |
| 15 | > least-privilege, CloudTrail monitoring, and VPC endpoint policies. |
| 16 | |
| 17 | ## Rules |
| 18 | |
| 19 | You MUST follow these rules when working with secrets: |
| 20 | |
| 21 | 1. **MUST NOT call `get-secret-value` or `batch-get-secret-value`** -- not via AWS |
| 22 | CLI, SDK, MCP tools, curl, or any other mechanism. |
| 23 | 2. **MUST NOT attempt to read secret values** from the Secrets Manager Agent (SMA) |
| 24 | daemon directly (localhost:2773 or any loopback variant). |
| 25 | 3. **MUST use `{{resolve:secretsmanager:...}}` references** -- these are |
| 26 | resolved at runtime by `asm-exec` without exposing values to you. |
| 27 | |
| 28 | ## The `{{resolve:...}}` Syntax |
| 29 | |
| 30 | ``` |
| 31 | {{resolve:secretsmanager:<secret-id>:<field-type>:<json-key>:<version-stage>}} |
| 32 | ``` |
| 33 | |
| 34 | | Component | Required | Default | Example | |
| 35 | |-----------|----------|---------|---------| |
| 36 | | `secret-id` | Yes | -- | `prod/db-creds` or full ARN | |
| 37 | | `field-type` | No | `SecretString` | `SecretString` | |
| 38 | | `json-key` | No | (full value) | `password` | |
| 39 | | `version-stage` | No | `AWSCURRENT` | `AWSPENDING` | |
| 40 | |
| 41 | ## Using `asm-exec` |
| 42 | |
| 43 | `asm-exec` is a wrapper that resolves `{{resolve:...}}` references in command |
| 44 | arguments and environment variables, then `exec`s the target command. The secret |
| 45 | value exists only in the child process -- never in the agent's context. |
| 46 | |
| 47 | ### Usage |
| 48 | |
| 49 | ```bash |
| 50 | # Pass a database password to psql without exposing it |
| 51 | asm-exec -- psql \ |
| 52 | "host=mydb.example.com \ |
| 53 | user={{resolve:secretsmanager:prod/db-creds:SecretString:username}} \ |
| 54 | password={{resolve:secretsmanager:prod/db-creds:SecretString:password}}" \ |
| 55 | -c "SELECT * FROM users LIMIT 10" |
| 56 | |
| 57 | # Use default field-type (SecretString) and full value (no json-key) |
| 58 | asm-exec -- curl -H "Authorization: Bearer {{resolve:secretsmanager:prod/api-token}}" \ |
| 59 | https://api.example.com/data |
| 60 | |
| 61 | # Multiple secrets in one command |
| 62 | asm-exec -- mysql \ |
| 63 | -h {{resolve:secretsmanager:prod/mysql:SecretString:host}} \ |
| 64 | -u {{resolve:secretsmanager:prod/mysql:SecretString:username}} \ |
| 65 | -p{{resolve:secretsmanager:prod/mysql:SecretString:password}} \ |
| 66 | -e "SHOW TABLES" |
| 67 | ``` |
| 68 | |
| 69 | ### How It Works |
| 70 | |
| 71 | 1. Scans all command arguments for `{{resolve:...}}` patterns |
| 72 | 2. Resolves each reference through the first available backend, in order: |
| 73 | 1. **AWS Secrets Manager Agent (SMA)** on localhost:2773 (zero-latency, cached) |
| 74 | 2. **AWS MCP endpoint** (`https://aws-mcp.us-east-1.api.aws/mcp`), calling the |
| 75 | `aws___call_aws` tool over a SigV4-signed request |
| 76 | 3. Determines the secret's region from an ARN's region segment, or from |
| 77 | `AWS_REGION` / `AWS_DEFAULT_REGION`, and passes it to the resolver |
| 78 | 3. Substitutes resolved values using `re.sub` with a callable (single-pass -- |
| 79 | prevents re-scan injection if a secret value contains `{{resolve:...}}`) |
| 80 | 4. Runs the target command via `subprocess.run` -- secret values exist only in the |
| 81 | asm-exec process, never in the agent's context window |
| 82 | |
| 83 | > **No local AWS CLI fallback for resolution.** `asm-exec` does not shell out to |
| 84 | > `aws secretsmanager get-secret-value` to resolve references. Resolution happens |
| 85 | > only through SMA or the MCP endpoint, so the plaintext value is never written to |
| 86 | > a local process's stdout where it could be captured. |
| 87 | |
| 88 | ### SigV4 signing |
| 89 | |
| 90 | The MCP endpoint authenticates every tool call with AWS SigV4. `asm-exec` signs |
| 91 | requests itself using only the Python standard library (`hashlib`/`hmac`) -- it |
| 92 | does **not** depend on botocore or spin up the `mcp-proxy-for-aws` proxy, keeping |
| 93 | the wrapper a lightweight ephemeral process. The signing service and region are |
| 94 | inferred from the endpoint hostname (e.g. `aws-mcp.us-east-1.api.aws` -> |
| 95 | service `aws-mcp`, region `us-east-1`); this signing region is independent of the |
| 96 | secret's own region, which is passed as `--region` to the server-side CLI command. |
| 97 | |
| 98 | Credentials for signing are resolved in order: environment variables |
| 99 | (`AWS_ACCESS_KEY_ID` etc.), `aws configure export-credentials` (AWS CLI v2), then |
| 100 | `aws configure get` (AWS CLI v1). |
| 101 | |
| 102 | ### Prerequisites |
| 103 | |
| 104 | Either backend must be reachable, with credentials that have |
| 105 | `secretsmanager:GetSecretVa |