Help us shape the future of Go! Take the 2026 Go Developer Survey

sshkeys

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: MIT Imports: 11 Imported by: 0

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

View Source
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

func FormatComment(operator, provider string, now time.Time) string

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

func IsIplaneComment(comment string) bool

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

func Generate(operator, provider string, now time.Time) (*KeyPair, error)

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

func UnmarshalPrivatePEM(operator, provider, comment string, pemBytes []byte) (*KeyPair, error)

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

func (k *KeyPair) MarshalAuthorizedKey() ([]byte, error)

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

func (k *KeyPair) MarshalPrivatePEM() ([]byte, error)

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

func WithClock(c func() time.Time) Option

WithClock injects a clock for tests so generated keys carry a known timestamp in their comment string.

func WithDir

func WithDir(dir string) Option

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

func New(opts ...Option) (*Store, error)

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

func (s *Store) EnsureKeyPair(operator, provider string) (*KeyPair, error)

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL