--- name: 1password-cli description: > 1Password CLI (`op`) usage patterns: reading secrets, extracting SSH keys, handling sensitive field blocking with --reveal workarounds. --- # 1Password CLI (`op`) Use `op` (v3+) to read secrets from 1Password during agent sessions. All outputs should avoid leaking secrets into logs/chat. ## Authentication 1Password CLI integrates via the 1Password app (`op signin`) or service account tokens. Verify status with: ```bash op whoami # Shows account URL + integration ID ``` ### Session Token Pattern (`op signin --raw`) For scripted use, capture the session token directly: ```bash export OP_SESSION="$(op signin --raw 2>&1)" # Now all subsequent op commands use this session op item list --vault Hermes ``` This avoids the interactive eval pattern and works in non-interactive shells. ## Reading Items ### Basic item retrieval ```bash # Human-readable output op item get 'ITEM_TITLE' --vault 'VAULT_NAME' # JSON (for programmatic parsing) op item get 'ITEM_TITLE' --vault 'VAULT_NAME' --format json ``` ### Reading specific fields ```bash # String fields (works directly — no restrictions) op item get 'ITEM' --field 'field_label' # Sensitive fields (passwords, private keys, tokens) # These are BLOCKED when piped or redirected: op item get 'ITEM' --field 'Sensitive-Field' # Returns "[use 'op item get ID --reveal' to reveal]" ``` ## ⚠️ Pitfall: Sensitive Field Extraction Blocked The 1Password CLI **blocks direct output** of sensitive fields (SSH keys, passwords, tokens) when piped, redirected, or captured. This is a security feature, not a bug. **Workarounds:** | Method | Command | Notes | |--------|---------|-------| | `--reveal` | `op item get ITEM --reveal --field 'Field-Name'` | Outputs the actual value (may include wrapping quotes) | | `--reveal --value` | `op item get ITEM --reveal --field 'Field-Name' --value` | Strips quotes, raw value only | | Temp file | `op item get ITEM --reveal --field 'Field' > /tmp/file` | Write to file, then `cat` or use via tool | ### SSH Key Extraction Pattern ```bash # SSH keys are always TYPE:SSHKEY in 1Password tmpkey=$(mktemp /tmp/op_ssh_key_XXXX) op item get 'SSH-KEY-NAME' --vault 'VaultName' --reveal --field 'Private-Key' --value > "$tmpkey" 2>/dev/null chmod 600 "$tmpkey" # Then use: eval "$(ssh-agent)" && ssh-add "$tmpkey" # Or: ssh -i "$tmpkey" user@host ``` After use, clean up: `rm -f "$tmpkey"` ### SSH Key JSON Parse ```bash # Get full SSH key item as JSON, parse the openssh format op item get 'SSH-KEY-NAME' --vault 'Vault' --reveal --format json | python3 -c " import sys, json data = json.load(sys.stdin) for f in data.get('fields', []): if 'private' in f.get('label','').lower() or f.get('id') == 'private_key': print(f.get('value', '')) " ``` Note: If `--reveal` still returns `[REDACTED]` in openssh format, the session may need refresh. Re-signin with `op signin` may be required. ## Useful Commands ```bash # Search items op item list --vault 'VaultName' op item search 'keyword' # List vaults op vault list # Service account (non-interactive) op connect server CONNECT_URL op connect service-account --account ACCOUNT_ID --secret TOKEN ``` ### ⚠️ Service Account `--vault` Requirement When authenticated as a **service account**, `op item get` REQUIRES `--vault`: ```bash # FAILS: "a vault query must be provided when this command is called by a service account" op item get --fields credential # WORKS: explicit --vault op item get --vault Hermes --fields label=credential --reveal ``` ### API_CREDENTIAL Item Type HA API tokens stored as `API_CREDENTIAL` category have a `credential` field: ```bash # Extract API credential (requires --reveal for concealed field) TOKEN=$(op item get --vault Hermes --fields label=credential --reveal) ``` ### `--fields` Syntax ```bash # By field label (preferred — robust against ID changes) op item get ITEM --fields label=credential --reveal op item get ITEM --fields label=username,label=password --reveal # By field type op item get ITEM --fields type=concealed --reveal # Multiple fields as CSV op item get ITEM --fields label=username,label=password --format json ``` ## Common Field Types ``` STRING → Direct output works (hostnames, usernames, URLs without passwords) PASSWORD → Requires --reveal SSHKEY → Requires --reveal + file redirect for private key usage NOTES → --field 'notesPlain' ``` ## Creating Items with Custom Fields `op item create` supports arbitrary custom fields via `key=value` syntax: ```bash # Login item (username + password) op item create --title="app-db" --vault "$VAULT_ID" \ --category=Login \ username=appuser password="$(openssl rand -base64 32)" # Password item with multiple custom fields op item create --title="app-security" --vault "$VAULT_ID" \ --category=Password \ password="$PRIMARY_SECRET" \ "internal_token=$TOKEN1" \ "jwt_secret=$TOKEN2" \ "secret_key=$TOKEN3" \ "lfs_jwt_secret=$TOKEN4" ``` Custom fields appear as labeled fields in the 1P item and are retrievable via `op item get --fields field_name`. ## ⚠️ Pitfall: API Credential Item Creation — Two-Step Required When creating an `API Credential` item, `op item create` does NOT populate the `credential` (token) field even when passing `credential=VALUE` as a parameter. The item is created with `username` and `url` populated, but the `credential` field stays empty. **Two-step required:** ```bash # Step 1: Create the item (username + url are set, credential is NOT) op item create --category="API Credential" \ --title="My API Token" --vault="Hermes" \ username="myuser" url="https://api.example.com" # Step 2: Edit to set the credential field op item edit "" --vault="Hermes" \ credential="ACTUAL_TOKEN_VALUE" ``` **Contrast with Login category:** `op item create --category="Login"` correctly populates both `username` and `password` in a single step — the two-step issue is specific to the `API Credential` category. ```bash # This works in one step (Login category): op item create --category="Login" \ --title="My Service" --vault="Hermes" \ username="admin" password="s3cret" --url="https://service.example.com" ``` **Verification:** ```bash op item get "" --vault "Hermes" --fields credential --reveal # Should output the actual token value ``` ## ⚠️ Accessing Vaults Outside Local Service Account Scope The local `op` service account may only have access to certain vaults (e.g. "Hermes"). Other vaults (e.g. "Kubernetes ESO" used by External Secrets Operator in K8s) are invisible: ```bash op vault list # ID NAME # 5ythsz37hf3xminhq33drg55tm Hermes # "Kubernetes ESO" NOT listed ``` ### Workaround: Use the ESO Service Account Token Extract the ESO token from K8s and use it as `OP_SERVICE_ACCOUNT_TOKEN`: ```bash # Get ESO token from K8s secret ESO_TOKEN=$(kubectl get secret onepassword-token -n external-secrets \ -o jsonpath="{.data.token}" | base64 -d) # Use it for op commands OP_SERVICE_ACCOUNT_TOKEN="$ESO_TOKEN" op vault list # ID NAME # 334ykdtj5kar3jlpcrztjvx2fu Kubernetes ESO # Now create/read items in that vault OP_SERVICE_ACCOUNT_TOKEN="$ESO_TOKEN" \ op item create --title="app-db" --vault "334ykdtj5kar3jlpcrztjvx2fu" ... ``` ⚠️ **Vault NAME may not resolve with service accounts.** Use the vault ID (e.g. `334ykdtj5kar3jlpcrztjvx2fu`) instead of the name ("Kubernetes ESO") — service accounts sometimes can't resolve vault names, only IDs. ## Related Support Files | File | Purpose | |------|---------| | `references/sensitive-field-extraction.md` | Detailed troubleshooting for blocked/restricted fields |