Initial commit: Hermes Agent Skills collection
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: security-tools
|
||||
description: "Class-level skill consolidating security-related utilities such as 1Password vault management and its CLI setup."
|
||||
version: 1.0.0
|
||||
tags: [security, 1password, vault]
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This umbrella skill merges:
|
||||
- `1password-vault-management`
|
||||
- `op-cli-setup`
|
||||
|
||||
It provides unified instructions, reference docs, and templates for managing secrets via the 1Password CLI.
|
||||
|
||||
## Using 1Password CLI from Hermes
|
||||
|
||||
Hermes is already configured with `OP_SERVICE_ACCOUNT_TOKEN` in the shell environment. No additional `op signin` is required. Verify with:
|
||||
|
||||
```bash
|
||||
op account list # should show the vault
|
||||
op vault list # lists accessible vaults
|
||||
```
|
||||
|
||||
Service-account tokens can only see vaults explicitly granted to the integration.
|
||||
|
||||
## Storing SSH Keys in 1Password
|
||||
|
||||
### CRITICAL: SSH Key Category is Buggy
|
||||
|
||||
`op item template get "SSH Key"` returns a template with a `private_key` field (type `SSHKEY`), but `op item create` silently **discards** the `private_key` value. The item is created without the private key. Always verify with `op item get ID --reveal`.
|
||||
|
||||
### Store SSH Key in 1Password — SECURE_NOTE vs LOGIN
|
||||
|
||||
There are two usable patterns depending on whether the user needs visibility in the 1Password app:
|
||||
|
||||
#### SECURE_NOTE (automation focus)
|
||||
```bash
|
||||
# Build template with CONCEALED field 'private key'
|
||||
cat << 'EOF' > /tmp/op_template.json
|
||||
{
|
||||
"title": "hermes-agent Proxmox SSH Key",
|
||||
"category": "SECURE_NOTE",
|
||||
"fields": [
|
||||
{
|
||||
"id": "notesPlain",
|
||||
"type": "STRING",
|
||||
"purpose": "NOTES",
|
||||
"label": "notesPlain",
|
||||
"value": "Public key:\nssh-ed25519 AAAAC3N... hermes-agent@proxmox\n\nNodes:\n10.0.20.10 proxmox1 ..."
|
||||
},
|
||||
{
|
||||
"id": "private_key",
|
||||
"type": "CONCEALED",
|
||||
"label": "private key",
|
||||
"value": "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
cat /tmp/op_template.json | op item create - --vault Hermes
|
||||
```
|
||||
|
||||
Retrieval:
|
||||
```bash
|
||||
op read 'op://Hermes/hermes-agent Proxmox SSH Key/private key'
|
||||
```
|
||||
|
||||
**Pitfall:** 1Password app may hide or bury SECURE_NOTE items in a "Documents" view. If user says "I can't see it," convert to LOGIN instead.
|
||||
|
||||
#### LOGIN (user-visible, recommended for interactive use)
|
||||
Use `category: LOGIN` with `purpose: PASSWORD` on the private key field. This renders under the Logins tab in all 1Password apps.
|
||||
|
||||
```bash
|
||||
cat << 'EOF' > /tmp/op_template.json
|
||||
{
|
||||
"title": "hermes-agent Proxmox SSH Key",
|
||||
"category": "LOGIN",
|
||||
"fields": [
|
||||
{
|
||||
"id": "username",
|
||||
"type": "STRING",
|
||||
"purpose": "USERNAME",
|
||||
"label": "username",
|
||||
"value": "root"
|
||||
},
|
||||
{
|
||||
"id": "password",
|
||||
"type": "CONCEALED",
|
||||
"purpose": "PASSWORD",
|
||||
"label": "password",
|
||||
"value": "-----BEGIN OPENSSH PRIVATE KEY-----\n..."
|
||||
},
|
||||
{
|
||||
"id": "notesPlain",
|
||||
"type": "STRING",
|
||||
"purpose": "NOTES",
|
||||
"label": "notesPlain",
|
||||
"value": "Public key:\nssh-ed25519 AAAAC3N... hermes-agent@proxmox\n\nNodes:\n10.0.20.10 proxmox1 ..."
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
cat /tmp/op_template.json | op item create - --vault Hermes --tags infrastructure,ssh
|
||||
```
|
||||
|
||||
Retrieval:
|
||||
```bash
|
||||
op read 'op://Hermes/hermes-agent Proxmox SSH Key/password'
|
||||
```
|
||||
|
||||
### Which to Choose
|
||||
- User will access interactively in the 1Password app → **LOGIN**
|
||||
- Pure automation / agent-only use → **SECURE_NOTE**
|
||||
|
||||
### Retrieval
|
||||
|
||||
```bash
|
||||
# Read private key
|
||||
op read 'op://Hermes/hermes-agent Proxmox SSH Key/private key'
|
||||
|
||||
# Full item (with reveal)
|
||||
op item get "hermes-agent Proxmox SSH Key" --vault Hermes --reveal
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- `references/1password-vault.md`
|
||||
- `references/op-cli-setup.md`
|
||||
- `references/ssh-key-vault-storage.md`
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### `op item create` silently discards `SSH_KEY.private_key` field
|
||||
`op item template get "SSH Key"` returns a `private_key` field (type `SSHKEY`), but `op item create` silently **drops** the value. The item is created but the private key is empty. Always verify with `op item get ID --reveal` immediately after creation. If the field is missing, recreate as LOGIN or SECURE_NOTE.
|
||||
|
||||
### Service Account Token limits
|
||||
`OP_SERVICE_ACCOUNT_TOKEN` can see only vaults explicitly granted. If `op vault list` returns nothing, the token lacks vault access — not a login issue.
|
||||
|
||||
### SSH host key caching and `BatchMode`
|
||||
When deploying keys to many hosts with `-o StrictHostKeyChecking=no`, the first connection adds to `~/.ssh/known_hosts`. Subsequent `BatchMode=yes` calls may still fail with `Host key verification failed.` if `known_hosts` has conflicting entries. Use `ssh-keyscan -H IP >> ~/.ssh/known_hosts` proactively, or force `StrictHostKeyChecking=accept-new` on verification SSH calls.
|
||||
|
||||
### Duplicate authorized_keys entries
|
||||
Running key deployment twice appends the same key again. Always deduplicate: `awk '!seen[$0]++' ~/.ssh/authorized_keys > /tmp/ak && mv /tmp/ak ~/.ssh/authorized_keys`.
|
||||
@@ -0,0 +1,57 @@
|
||||
# SSH Key Storage in 1Password — Reference
|
||||
|
||||
## Session: 2026-06-26 (Hermes Agent)
|
||||
|
||||
### Problem
|
||||
1Password's native "SSH Key" item category (`category: SSH_KEY`, field type `SSHKEY`) does **not** correctly persist the `private_key` field when created via `op item create`. The field is silently dropped from the created item.
|
||||
|
||||
### Workaround
|
||||
Use `SECURE_NOTE` with a `CONCEALED` field labeled `private key`. This stores the key securely, hides it from casual view, and makes it retrievable via `op read` or `op item get --reveal`.
|
||||
|
||||
### Pattern
|
||||
|
||||
1. **Generate key locally** (Ed25519 preferred):
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "hermes-agent@proxmox" -f ~/.ssh/id_ed25519_proxmox -N ""
|
||||
```
|
||||
|
||||
2. **Build JSON template from file** (never pass private key on CLI):
|
||||
```bash
|
||||
python3 -c "
|
||||
import json
|
||||
pk = open('~/.ssh/id_ed25519_proxmox').read().strip()
|
||||
puk = open('~/.ssh/id_ed25519_proxmox.pub').read().strip()
|
||||
template = {
|
||||
'title': 'hermes-agent Proxmox SSH Key',
|
||||
'category': 'SECURE_NOTE',
|
||||
'fields': [
|
||||
{'id': 'notesPlain', 'type': 'STRING', 'purpose': 'NOTES',
|
||||
'label': 'notesPlain', 'value': f'Public key:\\n{puk}'},
|
||||
{'id': 'private_key', 'type': 'CONCEALED',
|
||||
'label': 'private key', 'value': pk}
|
||||
]
|
||||
}
|
||||
json.dump(template, open('/tmp/op_template.json', 'w'))
|
||||
"
|
||||
```
|
||||
|
||||
3. **Create and verify**:
|
||||
```bash
|
||||
cat /tmp/op_template.json | op item create - --vault Hermes
|
||||
# Verify
|
||||
op item get "hermes-agent Proxmox SSH Key" --vault Hermes --reveal
|
||||
```
|
||||
|
||||
### Retrieval in Scripts
|
||||
|
||||
```bash
|
||||
pk=$(op read 'op://Hermes/hermes-agent Proxmox SSH Key/private key')
|
||||
tmpkey=$(mktemp)
|
||||
echo "$pk" > "$tmpkey"
|
||||
chmod 600 "$tmpkey"
|
||||
ssh -i "$tmpkey" root@10.0.20.91 "hostname"
|
||||
rm -f "$tmpkey"
|
||||
```
|
||||
|
||||
### Note on Service Accounts
|
||||
Hermes' `OP_SERVICE_ACCOUNT_TOKEN` is scoped to specific vaults. If `op vault list` shows only "Hermes", that's the only vault accessible. To add access to other vaults, update the 1Password integration's vault grants in the 1Password admin console.
|
||||
Reference in New Issue
Block a user