Documentation
¶
Overview ¶
Package sshkeys handles iplane-managed SSH key pairs. v0.1's chapter narrative keeps key management invisible to operators: the CLI auto-generates a key on first `iplane instance create runpod ...`, uploads it to the provider, and uses it at deploy time. Operators never type ssh-keygen, never touch ~/.ssh/, never pass --ssh-key.
Two concerns split across two files:
- keys.go : pure key-pair generation + serialization (Ed25519, OpenSSH authorized_keys public format, PEM private format). No I/O.
- store.go : persistence + scoping. Wraps oneauth's FSKeyStore to give an EnsureKeyPair(operator, provider) call that returns an existing key or generates a new one.
Encryption at rest is deferred to filesystem permissions in v0.1 (the JSON envelope lands at 0600 via oneauth's FSKeyStore, same as ssh-keygen's id_rsa default). Encrypting the asymmetric private key is a tracked oneauth followup but is theoretical for v0.1's threat model (operator laptop, FileVault/LUKS handles disk-at-rest).
Index ¶
Constants ¶
const Algorithm = "ssh-ed25519"
Algorithm is the protojson-stored algorithm name we tag SSH keys with in oneauth's KeyRecord. Chosen to look like the OpenSSH key type string so anyone inspecting the on-disk JSON sees a familiar label, not "raw bytes".
Variables ¶
This section is empty.
Functions ¶
func FormatComment ¶
FormatComment is the canonical iplane comment string for a key. Exposed so tests + KeyRegistrar implementations can construct the same string for skip-if-present comparisons.
func IsIplaneComment ¶
IsIplaneComment returns true if a parsed authorized_keys comment belongs to iplane (prefix-match on "iplane-"). The runpod KeyRegistrar uses this to find iplane's own entries in the blob without depending on an exact match to a specific timestamp.
Types ¶
type KeyPair ¶
type KeyPair struct {
Operator string
Provider string
CreatedAt time.Time
Public ed25519.PublicKey
Private ed25519.PrivateKey
Comment string
}
KeyPair carries an Ed25519 pair plus the operator/provider scope it was generated for. The Comment is the trailing string we stamp on the public-key authorized_keys line so the iplane key can be identified later (for skip-if-present checks on the provider side and for cleanup).
func Generate ¶
Generate returns a fresh Ed25519 key pair scoped to (operator, provider). The comment string is deterministic:
iplane-<operator>-<provider>-<created_at_rfc3339>
so the runpod adapter (and any other KeyRegistrar) can recognize iplane's own entries in the provider's authorized_keys blob and skip re-uploading when one is already present.
func UnmarshalPrivatePEM ¶
UnmarshalPrivatePEM parses a PEM-encoded OPENSSH PRIVATE KEY block back into a KeyPair. The comment is passed in explicitly because `ssh.MarshalPrivateKey` bakes it into the OpenSSH binary blob rather than the PEM headers, and parsing that binary form here would couple the package to internal OpenSSH format details. The Store caches the comment in oneauth's KeyRecord.Kid alongside the PEM bytes.
CreatedAt is parsed from the comment's trailing RFC3339 suffix (the "<ts>" in "iplane-<op>-<prov>-<ts>"); if the comment is empty or malformed, CreatedAt is the zero value -- callers care about the comment for identification, not the timestamp.
func (*KeyPair) MarshalAuthorizedKey ¶
MarshalAuthorizedKey returns the public half in OpenSSH authorized_keys format: "ssh-ed25519 BASE64 <comment>\n". Suitable to concatenate into a server-side authorized_keys file or into RunPod's user-level pubKey blob.
func (*KeyPair) MarshalPrivatePEM ¶
MarshalPrivatePEM returns the private key as a PEM-encoded OPENSSH PRIVATE KEY block. The block can be written to a file with 0600 perms and used directly as ssh -i.
type Option ¶
type Option func(*Store)
Option configures a Store at construction time.
func WithClock ¶
WithClock injects a clock for tests so generated keys carry a known timestamp in their comment string.
func WithDir ¶
WithDir is sugar that constructs an FSKeyStore rooted at dir. Convenient for the common "single-binary CLI with files under ~/.iplane/keys" case so callers do not need to import oneauth's stores/fs package directly.
func WithKeyStorage ¶
func WithKeyStorage(backend keys.KeyStorage) Option
WithKeyStorage injects any oneauth keys.KeyStorage. Use this when the caller already has a GORM-backed store, an InMemoryKeyStore (tests), or a custom backend.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store persists iplane-managed SSH key pairs through any oneauth keys.KeyStorage backend (FS, GORM, GAE, InMemory). The scoping convention (locked by docs/design/0002-deploy.md):
ClientID = "iplane:ssh:<operator>:<provider>"
per-operator-per-provider, so rotation/revocation is scoped to one provider and a leaked key has narrow blast radius. EnsureKeyPair looks up by this ClientID; if missing it generates a new pair, stores the PEM-encoded private under the same ClientID, and returns the parsed KeyPair.
func New ¶
New constructs a Store. A backend is required -- pass either WithKeyStorage(...) or WithDir(...). The order of options is fine either way (later wins); callers who pass both are violating the API and get the last one.
func (*Store) EnsureKeyPair ¶
EnsureKeyPair returns the key for (operator, provider) -- either loading the existing one from disk or generating + storing a new one. Idempotent: subsequent calls with the same scope return the same key (same private bytes, same comment string).