vault

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2025 License: GPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package vault provides age-encrypted credential management.

Package vault provides age-encrypted credential management.

The vault stores credentials (username/password pairs) encrypted with age, organized into contexts that map to SSH config include files. It supports multiple security backends via the agent package.

Storage Structure

Credentials are stored in an age-encrypted JSON file:

$XDG_CONFIG_HOME/nssh/credentials.age

The encryption key is either:

  • Software mode: passphrase-protected age identity (age.key.enc)
  • PIV mode: YubiKey hardware-backed key (piv.json)

Credential Resolution

The Manager.ResolveCredential method finds credentials for a host using a priority-based lookup:

  1. Host-specific credential (exact hostname match)
  2. Context default credential (from the SSH include file)
  3. Domain-based credential (matching domain suffix)

Contexts

Contexts group related hosts and credentials, typically corresponding to SSH config include files (e.g., "work.conf", "home.conf"). Each context can have a default credential and host-specific overrides.

Session Integration

The vault requires an active agent session for decryption. Use Manager.NeedsUnlock to check if unlock is required, and coordinate with the session package for agent communication.

Index

Constants

This section is empty.

Variables

View Source
var ErrAmbiguousMode = errors.New("ambiguous state: both software and hardware keystores found")

ErrAmbiguousMode is returned when both software and hardware keystores exist.

View Source
var ErrNotInitialized = errors.New("not initialized: no keystore found")

ErrNotInitialized is returned when no keystore is found.

View Source
var ErrSessionUnavailable = errors.New("session unavailable")

ErrSessionUnavailable indicates no decrypt session is available.

Functions

func DetectSecurityMode

func DetectSecurityMode(configDir string) (string, error)

DetectSecurityMode returns the current security mode based on filesystem state. It checks for the presence of keystore files rather than reading config.

Returns:

  • agent.ModeSoftware if age.key.enc exists (and piv.json doesn't)
  • agent.ModePIV if piv.json exists (and age.key.enc doesn't)
  • ErrAmbiguousMode if both keystores exist
  • ErrNotInitialized if neither keystore exists

func HasMultipleKeystores

func HasMultipleKeystores(configDir string) bool

HasMultipleKeystores returns true if both software and hardware keystores exist. This indicates an ambiguous or partial state, typically from a failed mode switch.

func IsInitialized

func IsInitialized(configDir string) bool

IsInitialized returns true if at least one keystore exists.

Types

type Context

type Context struct {
	GitIncludeFile string      `json:"git_include_file"`
	Credential     *Credential `json:"credential"`
	Domain         string      `json:"domain"`
}

Context represents a credential context with associated metadata.

type ContextEntry

type ContextEntry struct {
	Name            string      `json:"name"`
	GitIncludeFile  string      `json:"git_include_file"`
	Domain          string      `json:"domain"`
	Credential      *Credential `json:"credential"`
	CredentialCount int         `json:"credential_count"`
}

ContextEntry is returned by ListContexts with computed fields.

type Credential

type Credential struct {
	Username string `json:"username"`
	Password string `json:"password"`
	Default  bool   `json:"default,omitempty"`
}

Credential represents a username/password pair.

type DecryptFunc

type DecryptFunc func(ctx context.Context, ciphertext []byte) ([]byte, error)

DecryptFunc decrypts vault ciphertext via an external session (typically agent). Implementations should respect ctx (deadlines/cancel) where possible.

type HostCredentials

type HostCredentials struct {
	Credentials []Credential `json:"credentials"`
}

HostCredentials represents host-specific credentials.

type LockFunc

type LockFunc func(ctx context.Context) error

LockFunc terminates the active decrypt session.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles age-encrypted credential storage.

func NewManager

func NewManager(mode Mode, opts ...Option) (*Manager, error)

NewManager creates a vault manager with the specified mode. Options are for orthogonal concerns (logging, paths) only.

func (*Manager) AddContextCredential

func (m *Manager) AddContextCredential(name, username string, password *secret.Secret, overwrite bool) error

AddContextCredential adds or updates a context's credential.

func (*Manager) AddHostCredential

func (m *Manager) AddHostCredential(host, username string, password *secret.Secret) error

AddHostCredential adds a credential to a host.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • username: The username for the credential
  • password: The password for the credential

func (*Manager) AuditLogger

func (m *Manager) AuditLogger() *logging.AuditLogger

AuditLogger exposes the underlying audit logger (nil if auditing disabled).

func (*Manager) ConfigDir

func (m *Manager) ConfigDir() string

ConfigDir returns the config directory path.

func (*Manager) CreateBackup

func (m *Manager) CreateBackup() (string, error)

CreateBackup creates a timestamped backup of the current vault. Returns the backup path on success, empty string if no vault exists.

func (*Manager) CreateContext

func (m *Manager) CreateContext(name, gitIncludeFile, domain string, cred *Credential) error

CreateContext creates a new context.

func (*Manager) CredentialPath

func (m *Manager) CredentialPath() string

CredentialPath returns the path to the credential file.

func (*Manager) DeleteContext

func (m *Manager) DeleteContext(name string) (bool, error)

DeleteContext removes a context.

func (*Manager) DeleteHostCredentials

func (m *Manager) DeleteHostCredentials(host string) (bool, error)

DeleteHostCredentials removes all credentials for a host.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)

func (*Manager) GetContext

func (m *Manager) GetContext(name string) (*ContextEntry, error)

GetContext returns a context by name.

func (*Manager) GetContextByIncludeFile

func (m *Manager) GetContextByIncludeFile(includeFile string) (*ContextEntry, error)

GetContextByIncludeFile returns a context matching the SSH include file. Accepts either a filename or full path - extracts basename for matching.

func (*Manager) GetHostCredentials

func (m *Manager) GetHostCredentials(host string) ([]Credential, error)

GetHostCredentials returns credentials for a specific host.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)

func (*Manager) GetHostDefaultCredential

func (m *Manager) GetHostDefaultCredential(host string) (string, error)

GetHostDefaultCredential returns the default credential username for a host.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)

func (*Manager) GetIdentity

func (m *Manager) GetIdentity() (age.Identity, error)

GetIdentity returns the current age identity for decryption. Requires the vault to be unlocked.

func (*Manager) GetRecipient

func (m *Manager) GetRecipient() (age.Recipient, error)

GetRecipient returns the age recipient for encryption. Does NOT require unlock - reads from cached value, store, or age.pub file.

func (*Manager) ListContexts

func (m *Manager) ListContexts() ([]ContextEntry, error)

ListContexts returns all contexts sorted by name.

func (*Manager) ListHostsWithCredentials

func (m *Manager) ListHostsWithCredentials() (map[string]bool, error)

ListHostsWithCredentials returns a set of Host identifiers that have custom credentials.

func (*Manager) Lock

func (m *Manager) Lock() error

Lock terminates the session and clears cached state.

func (*Manager) ModeString

func (m *Manager) ModeString() string

ModeString returns the mode as a string for logging/IPC.

func (*Manager) NeedsUnlock

func (m *Manager) NeedsUnlock() bool

NeedsUnlock returns true if the vault requires unlock before use. Returns false if a session is already available or identities are pre-loaded.

func (*Manager) ReEncryptVault

func (m *Manager) ReEncryptVault(newRecipient age.Recipient) error

ReEncryptVault re-encrypts the vault with a new recipient. For safer migration with verification, use ReEncryptVaultWithVerify.

func (*Manager) ReEncryptVaultWithVerify

func (m *Manager) ReEncryptVaultWithVerify(newRecipient age.Recipient, verifyFn func(tempPath string) error) (backupPath string, err error)

ReEncryptVaultWithVerify re-encrypts the vault with a new recipient. If verifyFn is provided, it's called with the temp file path before committing. The original file is only replaced if verification succeeds. Returns the backup path (for recovery if needed) and any error.

func (*Manager) RemoveContextCredential

func (m *Manager) RemoveContextCredential(name string) error

RemoveContextCredential removes a context's credential.

func (*Manager) RemoveHostCredential

func (m *Manager) RemoveHostCredential(host, username string) (bool, error)

RemoveHostCredential removes a specific credential by username from a host. Returns true if a credential was removed, false if not found.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • username: The username to remove

func (*Manager) ResolveCredential

func (m *Manager) ResolveCredential(host, gitIncludeFile, username string) (*ResolvedCredential, error)

ResolveCredential resolves credentials for a host using the resolution algorithm.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • gitIncludeFile: The SSH config include file basename (for context lookup)
  • username: Optional username filter (empty = use default)

Algorithm: If username specified:

  1. Search hosts[host].credentials for username match
  2. Search context.credential for username match (if context exists)
  3. Return nil if not found

If username not specified:

  1. If a host credential has default=true, use that credential
  2. Otherwise fall back to context.credential
  3. Return nil

func (*Manager) ResolveCredentialWithDomain

func (m *Manager) ResolveCredentialWithDomain(host, username string) (*ResolvedCredential, error)

ResolveCredentialWithDomain resolves credentials using domain-based context matching. This is used when the host identifier matches a context's domain pattern.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • username: Optional username filter (empty = use default)

func (*Manager) SetHostDefaultCredential

func (m *Manager) SetHostDefaultCredential(host, username string) error

SetHostDefaultCredential sets the default credential username for a host. Pass empty username to clear the default.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • username: The username to set as default (empty to clear)

func (*Manager) SoftwareStore

func (m *Manager) SoftwareStore() software.Store

SoftwareStore returns the software store for CLI unlock operations. Returns nil for hardware modes.

func (*Manager) UpdateContextDomain

func (m *Manager) UpdateContextDomain(name, domain string) error

UpdateContextDomain updates a context's domain.

func (*Manager) UpdateContextIncludeFile

func (m *Manager) UpdateContextIncludeFile(name, gitIncludeFile string) error

UpdateContextIncludeFile updates a context's SSH config include file.

func (*Manager) UpdateHostCredential

func (m *Manager) UpdateHostCredential(host, username string, password *secret.Secret) (bool, error)

UpdateHostCredential updates the password for an existing host credential. Returns true if updated, false if not found.

Parameters:

  • host: The SSH Host identifier (unique within a context/include file)
  • username: The username to update
  • password: The new password

func (*Manager) WithLock

func (m *Manager) WithLock(fn func() error) error

WithLock executes fn while holding the lock.

type Mode

type Mode interface {
	// contains filtered or unexported methods
}

Mode is a sealed interface for manager initialization modes. The unexported mode() method ensures only types in this package can implement Mode.

func Auto

func Auto() Mode

Auto returns a mode that detects configuration from existing files. This is the only mode with a valid zero-value (no parameters needed).

func Hardware

func Hardware(kind hardware.Kind) Mode

Hardware returns a mode using a key stored on a hardware device. The agent mediates access to the hardware token. Panics if kind is empty (programming error). Unknown kinds return errors from NewManager, not panics, since they may come from config.

func Provided

func Provided(identity *age.X25519Identity) Mode

Provided returns a mode using an already-decrypted identity. Used for rekey/enrollment operations where the identity is obtained externally. Panics if identity is nil (programming error).

func Software

func Software(store software.Store) Mode

Software returns a mode using a passphrase-protected key stored on disk. Panics if store is nil (programming error).

type Option

type Option func(*managerConfig)

Option configures orthogonal concerns that apply across all modes.

func WithAuditLogger

func WithAuditLogger(logger *logging.AuditLogger) Option

WithAuditLogger sets the audit logger for security events.

func WithMaxBackups

func WithMaxBackups(n int) Option

WithMaxBackups sets the maximum number of credential backups to retain.

func WithPaths

func WithPaths(paths *config.Paths) Option

WithPaths sets explicit paths (default: config.DefaultPaths()).

func WithSessionDeps

func WithSessionDeps(deps SessionDeps) Option

WithSessionDeps injects optional locked-mode session behavior. The zero-value (all fields nil) means "no session available".

type ResolvedCredential

type ResolvedCredential struct {
	Username string
	Password *secret.Secret
	Source   string // "host", "context", or "default"
}

ResolvedCredential represents a resolved username/password pair.

type SessionAvailableFunc

type SessionAvailableFunc func(ctx context.Context) bool

SessionAvailableFunc reports whether a decrypt session is available.

type SessionDeps

type SessionDeps struct {
	BaseContext context.Context
	Available   SessionAvailableFunc
	Decrypt     DecryptFunc
	Lock        LockFunc
}

SessionDeps holds optional agent-backed session behavior. Zero-value (all fields nil) means "no session available".

type VaultData

type VaultData struct {
	Contexts map[string]*Context         `json:"contexts"`
	Hosts    map[string]*HostCredentials `json:"hosts"`
}

VaultData is the top-level structure stored in the encrypted file.

Directories

Path Synopsis
Package hardware provides types for hardware security device integration.
Package hardware provides types for hardware security device integration.
Package piv provides PIV keystore persistence and ECIES crypto helpers.
Package piv provides PIV keystore persistence and ECIES crypto helpers.
Package software provides host-backed age identity storage.
Package software provides host-backed age identity storage.

Jump to

Keyboard shortcuts

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