Implement SSH identity authentication
This commit is contained in:
@@ -70,16 +70,35 @@ The HTTPS account name is stored inside the protected credential record, not in
|
||||
TOML. OpenPGP passphrases are addressed by the resolved primary fingerprint.
|
||||
|
||||
SSH remotes use either `ssh://[user@]host[:port]/path` or scp-like
|
||||
`[user@]host:path` syntax and omit the HTTPS credential fields:
|
||||
`[user@]host:path` syntax and omit the HTTPS credential fields. They select
|
||||
exactly one private-key file or one already-running SSH-agent identity:
|
||||
|
||||
```toml
|
||||
[[git.remotes]]
|
||||
name = "origin"
|
||||
url = "git@git.example.test:alice/password-store.git"
|
||||
ssh_identity_file = "keys/id_ed25519"
|
||||
ssh_known_hosts_file = "known_hosts"
|
||||
```
|
||||
|
||||
Relative paths are resolved against `config.toml`. If
|
||||
`ssh_known_hosts_file` is omitted, `~/.ssh/known_hosts` is used. Agent
|
||||
authentication replaces `ssh_identity_file` with an exact SHA-256 fingerprint
|
||||
and may name a socket explicitly:
|
||||
|
||||
```toml
|
||||
ssh_agent_fingerprint = "SHA256:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU"
|
||||
ssh_agent_socket = "/run/user/1000/ssh-agent.socket"
|
||||
```
|
||||
|
||||
Private-key passphrases are stored by fingerprint in the operating-system
|
||||
secret store and never appear in TOML. IronStorage does not read OpenSSH
|
||||
configuration, try additional keys, prompt for passwords or
|
||||
keyboard-interactive authentication, launch an agent, or invoke proxy/helper
|
||||
commands.
|
||||
|
||||
The typed endpoint model is always available so an SSH remote remains readable
|
||||
in configuration even when the binary was built without SSH. Such a build
|
||||
through the Rust API even when the binary was built without SSH. Such a build
|
||||
returns a typed unsupported-transport error before connection or repository
|
||||
mutation. The optional storage `ssh` feature contains `russh` 0.63.1 and Tokio;
|
||||
`russh` default features are disabled and the Ring backend plus RSA key support
|
||||
@@ -94,6 +113,12 @@ bytes, credentials, queries, fragments, ambiguous unbracketed IPv6 or colon
|
||||
paths, leading-option paths, local paths, URL rewrites, separate push URLs,
|
||||
helper transports, and unknown schemes fail closed.
|
||||
|
||||
Server identity is checked before authentication against the configured
|
||||
known-hosts file. Exact, hashed, and non-default-port host entries are
|
||||
supported. Unknown keys return their host, algorithm, and SHA-256 fingerprint
|
||||
for explicit confirmation; confirming appends the key atomically. A changed
|
||||
key is a hard failure and is never replaced by that confirmation API.
|
||||
|
||||
`clipboard_timeout_seconds` controls the native clipboard presentation lease.
|
||||
It defaults to 45 seconds for upstream `pass` compatibility and must be between
|
||||
1 and 300 seconds. The CLI remains alive for the lease so Linux can serve its
|
||||
|
||||
@@ -32,10 +32,7 @@ repository mutation instead of treating its configuration as malformed.
|
||||
## HTTPS transport
|
||||
|
||||
HTTPS credentials are requested with the configured server ID and application
|
||||
ID and remain outside Git configuration. The SSH session, host-verification,
|
||||
authentication, and pack-protocol implementations are separate milestone work;
|
||||
until those layers are present, network operations on SSH endpoints return the
|
||||
typed unsupported-transport result.
|
||||
ID and remain outside Git configuration.
|
||||
|
||||
Fetch uses the embedded Rust smart-HTTP client with an explicit credential
|
||||
callback, so Git's credential cascade is never entered. Push implements the
|
||||
@@ -45,6 +42,24 @@ requests `report-status`, and accepts the update only after both unpack and ref
|
||||
status succeed. HTTP redirects are disabled so authorization cannot cross an
|
||||
origin boundary.
|
||||
|
||||
## SSH authentication
|
||||
|
||||
With the optional `ssh` feature, storage opens an in-process Russh client and
|
||||
verifies the server key before any authentication or command. Known-host files
|
||||
are read with size and line bounds; exact, hashed, and bracketed non-default
|
||||
port entries are supported. Unknown keys require an explicit call to persist
|
||||
the confirmed key atomically. Changed keys always fail and are never replaced.
|
||||
|
||||
Authentication uses one configured OpenSSH private-key file (Ed25519, ECDSA,
|
||||
or RSA) or one exact SHA-256 fingerprint from an already-running SSH agent.
|
||||
Encrypted-key passphrases come from `SecretBytes` in the operating-system
|
||||
secret store. Identity attempts are bounded and deterministic; IronStorage
|
||||
does not spray keys, use passwords or keyboard-interactive authentication,
|
||||
read OpenSSH configuration, start/probe an agent process, or run proxy/helper
|
||||
commands. SHA-1 host signatures and `ssh-rsa` authentication are excluded.
|
||||
Cancellation interrupts connection and authentication without changing Git,
|
||||
known hosts, or secure storage.
|
||||
|
||||
Pull refuses a dirty worktree. It fast-forwards when possible and otherwise
|
||||
uses the embedded three-way tree merge. Unresolved paths are returned as typed
|
||||
`MergeConflicts`; no conflict markers or partial checkout are written. Checkout
|
||||
|
||||
@@ -5,12 +5,13 @@ password-store repositories, Git configuration, command arguments, and logs
|
||||
contain only opaque identifiers; passphrases, tokens, and HTTPS passwords are
|
||||
stored by the operating system.
|
||||
|
||||
`SecretReference` has two validated forms. OpenPGP passphrases are keyed by the
|
||||
`SecretReference` has three validated forms. OpenPGP passphrases are keyed by the
|
||||
primary fingerprint. HTTPS Git credentials are keyed by purpose, server ID,
|
||||
application ID, and account. The account is kept inside the protected record,
|
||||
so a configured server/application pair can retrieve it without adding an
|
||||
account or secret value to TOML. References, locators, store state, and errors
|
||||
all use redacted `Debug` output.
|
||||
account or secret value to TOML. SSH private-key passphrases are keyed by the
|
||||
key's SHA-256 fingerprint. References, locators, store state, and errors all use
|
||||
redacted `Debug` output.
|
||||
|
||||
Stored values use a small versioned binary envelope containing their reference
|
||||
and secret bytes. Retrieval validates the envelope and exact reference before
|
||||
@@ -47,8 +48,8 @@ if the platform lock operation reports an error. Caching is disabled unless a
|
||||
caller explicitly selects `SecretCachePolicy::Timed`. Timed policies are capped
|
||||
at 128 entries and 15 minutes, expire lazily, and are always cleared on lock.
|
||||
|
||||
The same unlocked store implements the OpenPGP `SecretProvider` and HTTPS Git
|
||||
`GitCredentialProvider`. The CLI uses it for terminal `show` and embedded `git
|
||||
fetch`, proving that protected keys and remote authentication are resolved only
|
||||
through opaque references. Tests inject a memory backend and never access a
|
||||
developer or CI user keyring.
|
||||
The same unlocked store implements the OpenPGP `SecretProvider`, HTTPS Git
|
||||
`GitCredentialProvider`, and SSH `SshPassphraseProvider`. The CLI uses it for
|
||||
terminal `show` and embedded Git, proving that protected keys and remote
|
||||
authentication are resolved only through opaque references. Tests inject a
|
||||
memory backend and never access a developer or CI user keyring.
|
||||
|
||||
Reference in New Issue
Block a user