Some checks failed
CI / fmt, clippy, docs (push) Failing after 5s
CI / test (ubuntu-latest) (push) Failing after 5s
CI / iroh integration smoke tests (push) Failing after 5s
CodeQL / Analyze Rust (push) Failing after 4s
Security / RustSec cargo-audit (push) Failing after 5s
CI / test (macos-latest) (push) Has been cancelled
CI / test (windows-latest) (push) Has been cancelled
138 lines
5.8 KiB
Markdown
138 lines
5.8 KiB
Markdown
# Geth Keychain And SSHSIGCHAIN
|
|
|
|
## Purpose
|
|
|
|
The geth keychain is the local identity-plane operation log for admin SSH keys,
|
|
users, devices, nodes, agents, and endpoint bindings. The daemon stores these
|
|
operations in SQLite and synchronizes authorized records over Iroh. Reducing
|
|
the operation log produces the current keychain view used by node management,
|
|
authorization, and OpenSSH `allowed_signers` projection.
|
|
|
|
[`sshsigchain.md`](sshsigchain.md) specifies SSHSIGCHAIN, the only portable
|
|
signed-chain format in this repository. It is deliberately separate from the
|
|
current local keychain operation log: SSHSIGCHAIN starts with an explicit
|
|
out-of-band trust tuple, uses exact parent hashes without a sequence counter,
|
|
and never uses a downloaded `allowed_signers` file as a trust root. The previous
|
|
test-only static JSONL publication format was removed.
|
|
|
|
## Local keychain model
|
|
|
|
The durable local log contains `KeychainOp` records and their
|
|
`KeychainOpSignature` records. Every operation has a deterministic canonical
|
|
binary signing payload under the `geth.keychain.v1@geth.local` namespace.
|
|
JSON is an API and storage representation, never the signed payload.
|
|
|
|
Important operation kinds are:
|
|
|
|
- `KeychainInit`
|
|
- `AdminKeyAdd`, `AdminKeyRevoke`
|
|
- `UserAdd`, `UserRename`, `UserRevoke`
|
|
- `DeviceAdd`, `DeviceRevoke`, `DeviceKeyAdd`, `DeviceKeyRevoke`
|
|
- `NodeAdd`, `NodeRename`, `NodeRevoke`
|
|
- `NodeEndpointAdd`, `NodeEndpointRevoke`
|
|
- `AgentBind`
|
|
|
|
`AdminKeyAdd` carries the OpenSSH public key material used to reconstruct the
|
|
active `allowed_signers` view. Local operation and signature identities are
|
|
append-only: an existing identity with different bytes is rejected rather than
|
|
replaced.
|
|
|
|
## Local signatures
|
|
|
|
An operation signature contains its operation ID, signer key fingerprint,
|
|
signer public key, SSHSIG namespace, OpenSSH signature bytes, and creation
|
|
time. geth signs through OpenSSH, for example:
|
|
|
|
```sh
|
|
ssh-keygen -Y sign -n geth.keychain.v1@geth.local -f <signing-key> <payload>
|
|
```
|
|
|
|
`<signing-key>` may be a local private key, an OpenSSH security-key stub, or a
|
|
public key whose private half is available through `ssh-agent`. The daemon does
|
|
not store private keys or passphrases.
|
|
|
|
Local verification accepts an operation only when a signature from an admin
|
|
key in the previously accepted local view verifies over the canonical payload.
|
|
This is useful for the local Iroh-synchronized operation log, but it is not a
|
|
portable SSHSIGCHAIN history and MUST NOT be presented as one.
|
|
|
|
## SSHSIGCHAIN authority and profiles
|
|
|
|
SSHSIGCHAIN uses the `sshsigchain.v1` SSHSIG namespace. Device/key management is
|
|
not a geth-specific profile: the mandatory Authority v1 reducer defines genesis,
|
|
device add/revoke, key add/revoke, proof-of-possession, scoped and delegable
|
|
permissions, device permission ceilings, anchor-policy changes, and no-op links.
|
|
Authority is always public and evaluated from the exact parent state. The root
|
|
key seeds authority only at genesis and can later be causally revoked.
|
|
|
|
Applications attach profile-ID-scoped commitments. A fresh 32-byte nonce salts
|
|
each payload commitment so a withheld small value is not directly vulnerable to
|
|
dictionary guessing. A disclosure can be added to or removed from transport
|
|
without changing the SSH signature or record hash. Hidden application data can
|
|
never alter devices, keys, permissions, or anchor policy. Verification without
|
|
all disclosures remains useful for authority, but affected application profile
|
|
state is reported as incomplete.
|
|
|
|
Keys receive direct permissions such as `DeviceAdd`, `KeyAddSelf`,
|
|
`ManagePermissions`, `AnchorAttest`, and `ProfileWrite(<profile>)`. An attached
|
|
key cannot exceed its device ceiling, and it cannot grant a permission outside
|
|
its delegable set. Newly added keys must sign the exact proposed key/device/
|
|
permission binding under `sshsigchain.key-proof.v1`.
|
|
|
|
Head claims are separately signed by an active `AnchorAttest` key. An authority
|
|
anchor policy names weighted/required attesting keys plus backend IDs, classes,
|
|
locators, weights, required backends/classes, and a separate backend threshold.
|
|
Adapters verify backend-specific receipts, while the core
|
|
rejects rollback behind a cached head and incomparable histories. Backends do
|
|
not decide which history is canonical. The head that changes anchor policy is
|
|
witnessed under the previous policy.
|
|
|
|
To inspect an SSHSIGCHAIN JSONL transport file, pin the chain ID and root public
|
|
key locally:
|
|
|
|
```sh
|
|
geth keychain verify-sigchain \
|
|
--in geth.sshsigchain.v1.jsonl \
|
|
--chain-id <64-hex-character-chain-id> \
|
|
--root-key ~/.ssh/geth-root.pub
|
|
```
|
|
|
|
This verifier is experimental while geth adds record creation, publication,
|
|
import, accepted-head persistence, concrete anchor adapters, and independently
|
|
generated wire vectors. Those later workflows must preserve the same explicit
|
|
trust tuple and Authority v1 semantics; they must not introduce a compatibility
|
|
route for the removed static format.
|
|
|
|
## Local commands
|
|
|
|
Bootstrap an owner node:
|
|
|
|
```sh
|
|
geth init \
|
|
--admin-key ~/.ssh/id_ed25519_sk.pub \
|
|
--signing-key ~/.ssh/id_ed25519_sk \
|
|
--node-name laptop
|
|
```
|
|
|
|
Manage the current local keychain and its OpenSSH projection:
|
|
|
|
```sh
|
|
geth keychain admin-add \
|
|
--admin-key ~/.ssh/new_admin.pub \
|
|
--signing-key ~/.ssh/current_admin_sk \
|
|
--principal admin
|
|
geth keychain admin-revoke <key-fingerprint> \
|
|
--signing-key ~/.ssh/current_admin_sk
|
|
geth keychain allowed-signers --out allowed_signers
|
|
geth keychain sign-file --in authorized_keys --out authorized_keys.sig \
|
|
--signing-key ~/.ssh/id_ed25519_sk
|
|
geth keychain verify-file --in authorized_keys --signature authorized_keys.sig
|
|
geth keychain explain <op-id>
|
|
geth keychain explain-signer <key-id>
|
|
geth keychain verify
|
|
```
|
|
|
|
The keychain does not manage host `authorized_keys` policy. It can sign a
|
|
snapshot supplied by the operator, allowing another system to retain its own
|
|
SSH login policy while usefully checking approval against an active local admin
|
|
key.
|