Documentation
¶
Index ¶
- func BaseMethod(method string) string
- func Downscope(ctx context.Context, vc *vault.Client, token string, c PolicyConstraint) (string, error)
- func FetchTokenFromSocket(ctx context.Context, socketPath string) (string, error)
- func IsExpired(err error) bool
- func ReadTokenEnv() string
- func ReadTokenFile(path string) (string, error)
- func ResolveToken(tokenFilePath string) string
- func SealTokenAtRest(method string) bool
- func WarnUnrestrictedPolicy(c PolicyConstraint)
- func WriteTokenFile(path string, token string, seal bool) error
- type LifecycleManager
- func (lm *LifecycleManager) NeedsReauth() bool
- func (lm *LifecycleManager) Reload()
- func (lm *LifecycleManager) SetOnReauth(fn func())
- func (lm *LifecycleManager) SetTokenFilePath(p string)
- func (lm *LifecycleManager) SetTokenSocket(p string)
- func (lm *LifecycleManager) Start(ctx context.Context) <-chan error
- type LoginStatus
- type LoginTracker
- type MTLSParams
- type Manager
- type PolicyConstraint
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BaseMethod ¶ added in v0.23.0
BaseMethod strips the "+tpm" sealing modifier, returning the underlying auth flow: "oidc", "ldap", "token", or "mtls". The login dispatch switches on this so a "+tpm" variant routes to the same flow as its base.
func Downscope ¶ added in v0.25.0
func Downscope(ctx context.Context, vc *vault.Client, token string, c PolicyConstraint) (string, error)
Downscope exchanges a freshly-minted broad login token for a least-privilege child token carrying only the configured policies, when the constraint is active. The child is minted on an isolated sibling of vc (CreateChildTokenFor) so vc itself is never set to the broad token — Vault still enforces that the requested policies are a subset of the parent's, so this can only drop privilege. Returns the token the caller should adopt and persist: the downscoped child when a constraint is active, otherwise the original token unchanged.
Downscope is a pure "maybe exchange the token" helper: it never warns and never mutates vc. The transition notice for an unrestricted login lives in WarnUnrestrictedPolicy, called only at the sites that adopt a final operational token — so the mtls bootstrap login (whose broad token is transient and never persisted) does not emit it.
On both the active-success and the failure paths the caller's shared client is left exactly as it was, so a downscope failure cannot leave the broad token installed on (or retrievable from) the web server's shared client, and there is no window in which a concurrent reader observes the broad token. The caller is therefore responsible for adopting the returned token — it must call vc.SetToken on the result itself; Downscope deliberately does not.
A downscoping failure is returned as an error rather than silently falling back to the broad token: least privilege must fail closed. The caller treats it like any other login failure.
func FetchTokenFromSocket ¶ added in v0.24.0
FetchTokenFromSocket retrieves a Vault token from a peer dotvault daemon's web API exposed over a Unix-domain socket — the programmatic equivalent of
curl --unix-socket <socketPath> http://localhost/api/v1/token
It backs dotvault-to-dotvault token sharing: a remote-forwarded socket (e.g. ~/.ssh/dotvault.sock created by an SSH RemoteForward from a machine running the dotvault web UI) lets a host with no interactive login facility borrow the live token from a peer that has one.
It is best-effort and never fatal. An empty path, an unexpandable ~, a missing socket file, a stale socket (file present but no listener), a non-200 response (the peer holds no token), or a malformed body all resolve to ("", nil) so the caller simply carries on with its normal auth flow. The returned token is deliberately NOT validated here — callers run LookupSelf before adopting it, exactly as they do for the token file and DOTVAULT_TOKEN.
func IsExpired ¶
IsExpired reports whether err is the token-expired sentinel. The Start loop uses this to route expired tokens through the same recovery path as a 403 response.
func ReadTokenEnv ¶
func ReadTokenEnv() string
ReadTokenEnv reads the Vault token from the DOTVAULT_TOKEN environment variable. VAULT_TOKEN is deliberately ignored — it belongs to the `vault` CLI, and honouring it would let an unrelated shell session's token leak into the daemon (the Vault SDK's own VAULT_TOKEN pickup is likewise neutralised in internal/vault.NewClient).
An env-var token is necessarily plaintext: there is no way to TPM-seal an environment value. The "+tpm" sealing applies only to the token file.
func ReadTokenFile ¶
ReadTokenFile reads a Vault token from a file, trimming whitespace. Returns empty string (not error) if the file doesn't exist.
A file written by WriteTokenFile with sealing on carries sealedTokenPrefix; this transparently unseals it via the TPM. A plaintext file is returned verbatim, so existing token files keep working and migration is free.
func ResolveToken ¶
ResolveToken returns a Vault token, checking the DOTVAULT_TOKEN env var first, then the token file. Returns empty string if neither is set. A sealed token file that cannot be unsealed resolves to "" (the warning is logged in ReadTokenFile), which the auth flow treats as "no usable token, re-authenticate" — never a silent plaintext fallback.
func SealTokenAtRest ¶ added in v0.23.0
SealTokenAtRest reports whether the auth method requests TPM-sealing of the token file — the "+tpm" suffix. When true, WriteTokenFile seals the token under the TPM and writes a self-describing sealed envelope, which ReadTokenFile transparently unseals on read (so the daemon, the CLI, and the public client/ facade all consume it without knowing the method).
func WarnUnrestrictedPolicy ¶ added in v0.25.0
func WarnUnrestrictedPolicy(c PolicyConstraint)
WarnUnrestrictedPolicy logs the one-line transition warning when no least-privilege constraint is configured, nudging the operator toward vault.policies before a future release makes restriction the default. It is a no-op once the operator has opted in.
Call it only at the sites that adopt a final operational token from a downscopeable login: the real CLI oidc/ldap logins, the mtls operational login, and the web oidc/ldap handlers. It is deliberately NOT called on the mtls bootstrap sub-login (whose broad token is transient, unpersisted, and about to be replaced by the downscoped cert-auth token), nor for the `token` auth method / web token-login (a user-supplied token dotvault never downscopes — the nudge to set vault.policies would not apply). Keeping the notice tied to the operational token is why it lives here rather than inside Downscope, which the bootstrap path also runs.
func WriteTokenFile ¶
WriteTokenFile writes a Vault token to a file with 0600 permissions. When seal is true the token is sealed under the TPM (machine-bound) and written as a self-describing sealed envelope that ReadTokenFile transparently unseals. Sealing requires a working TPM (Linux/Windows); seal=true on a host with no hardware backend returns an error rather than silently writing plaintext — the same no-silent-fallback contract as mtls+tpm key sealing.
An empty path is a deliberate no-op: it means "do not persist this token". The mTLS bootstrap login uses this so its broad, PKI-capable bootstrap token lives only in memory (long enough to mint the certificate) and is never left in the on-disk cache if a later step fails — only the final cert-auth working token is persisted, by certLogin with the real path.
Types ¶
type LifecycleManager ¶
type LifecycleManager struct {
// contains filtered or unexported fields
}
LifecycleManager manages token TTL checks and renewal.
func NewLifecycleManager ¶
func NewLifecycleManager(client *vault.Client, checkInterval time.Duration, disableRenewal bool) *LifecycleManager
NewLifecycleManager creates a new token lifecycle manager. When disableRenewal is true the manager still monitors TTL and signals re-auth when the token expires, but never calls RenewSelf.
func (*LifecycleManager) NeedsReauth ¶
func (lm *LifecycleManager) NeedsReauth() bool
NeedsReauth returns true if the token is expired or needs re-authentication.
func (*LifecycleManager) Reload ¶
func (lm *LifecycleManager) Reload()
Reload signals the lifecycle goroutine to perform an immediate tryReload on the next scheduling pass — used by the SIGHUP handler and by the in-process token-file watcher (internal/tokenwatch) so a freshly-written token file (the configured tokenFilePath, by default ~/.dotvault-token) is picked up without waiting for the 5-minute lifecycle tick. Coalescing: concurrent or back-to-back calls collapse into a single reload. Safe to call before or after Start; calls made before Start are buffered and consumed by the goroutine on its first select.
func (*LifecycleManager) SetOnReauth ¶
func (lm *LifecycleManager) SetOnReauth(fn func())
SetOnReauth registers a callback fired when the manager transitions into the needs-reauth state. The callback runs synchronously on the lifecycle goroutine — keep it short. In web mode this is used to clear the in-memory Vault token so the SPA's status check reflects "logged out".
func (*LifecycleManager) SetTokenFilePath ¶
func (lm *LifecycleManager) SetTokenFilePath(p string)
SetTokenFilePath wires a token file path so that on detection of an invalid/expired token the manager will attempt to reload (and re-validate) the token from disk or DOTVAULT_TOKEN before declaring re-auth necessary. This lets an external facility (e.g. a tty session running `dotvault login`) recover a running daemon without a restart.
func (*LifecycleManager) SetTokenSocket ¶ added in v0.24.0
func (lm *LifecycleManager) SetTokenSocket(p string)
SetTokenSocket wires the path to a peer dotvault's web-API Unix socket so the recovery path can borrow the peer's live token (dotvault-to-dotvault sharing) before declaring re-auth necessary. Empty disables it. See FetchTokenFromSocket.
type LoginStatus ¶
type LoginStatus struct {
State string `json:"state"`
Token string `json:"-"`
Error string `json:"error,omitempty"`
MFAMethods []vault.MFAMethod `json:"mfa_methods,omitempty"`
}
LoginStatus represents the current state of an async login attempt.
type LoginTracker ¶
type LoginTracker struct {
// contains filtered or unexported fields
}
LoginTracker manages async login attempts keyed by session ID.
func NewLoginTracker ¶
func NewLoginTracker(vc *vault.Client) *LoginTracker
NewLoginTracker creates a new LoginTracker.
func (*LoginTracker) Clear ¶
func (lt *LoginTracker) Clear(sessionID string)
Clear removes a completed login session.
func (*LoginTracker) Close ¶
func (lt *LoginTracker) Close()
Close stops the background GC goroutine.
func (*LoginTracker) GetStatus ¶
func (lt *LoginTracker) GetStatus(sessionID string) *LoginStatus
GetStatus returns the current login status for a session. Returns nil if the session does not exist.
func (*LoginTracker) StartLogin ¶
func (lt *LoginTracker) StartLogin(sessionID, mount, username, password string)
StartLogin begins an async LDAP login attempt. The login runs in a background goroutine with a 5-minute timeout. Poll GetStatus to check progress.
func (*LoginTracker) SubmitTOTP ¶
func (lt *LoginTracker) SubmitTOTP(sessionID, passcode string)
SubmitTOTP submits a TOTP passcode for an in-progress MFA login.
type MTLSParams ¶ added in v0.23.0
type MTLSParams struct {
// Connectivity for building a cert-presenting login client.
VaultAddress string
CACert string
TLSSkipVerify bool
Method string // "mtls" | "mtls+tpm"
BootstrapMethod string
BootstrapMount string
CertMount string
CertRole string
PKIMount string
PKIRole string
KeyType string
CommonName string // template over {{.user}}
TTL string
ReissueBefore time.Duration
SealToPCRs bool
StorageDir string
BYOCert string
BYOKey string
}
MTLSParams carries everything the cert-auth flow needs. It is populated by the daemon (cmd/dotvault) from the validated vault.mtls config and attached to a Manager whose AuthMethod is "mtls" or "mtls+tpm".
type Manager ¶
type Manager struct {
VaultClient *vault.Client
TokenFilePath string
// AuthMethod is the configured method: "oidc", "ldap", "token", "mtls", or
// "mtls+tpm". A "+tpm" suffix on any base method (e.g. "oidc+tpm") also
// requests TPM-sealing of the cached token file at rest; for "mtls+tpm"
// that is in addition to the cert key the cert flow already seals.
AuthMethod string
AuthMount string // auth mount path
AuthRole string // optional role
Username string
// OIDCCallbackPort is the fixed local TCP port authenticateOIDC binds for
// the OAuth redirect_uri (vault.oidc_callback_port). Zero defaults to
// 8250 (the `vault` CLI's own default); if that port is unavailable,
// authenticateOIDC falls back to a random port. See oidc.go.
OIDCCallbackPort int
// TokenSocket is an optional path to a peer dotvault's web-API Unix
// socket. When set, Login first tries to borrow a live token from the
// peer (dotvault-to-dotvault sharing) before running the configured
// interactive flow. A missing or stale socket is ignored. See
// FetchTokenFromSocket.
TokenSocket string
// Policy narrows a freshly-minted login token to a least-privilege child
// token (vault.policies / vault.no_default_policy). The zero value applies
// no narrowing — the token carries every policy the auth role granted,
// today's behaviour. Consulted by the oidc/ldap/mtls flows; the bootstrap
// login that mints an mtls cert is deliberately left un-narrowed because it
// needs the pki/sign capability.
Policy PolicyConstraint
// MTLS is required when the base auth method is "mtls".
MTLS *MTLSParams
}
Manager orchestrates Vault authentication.
func (*Manager) Authenticate ¶
Authenticate attempts to authenticate with Vault. It first tries to reuse an existing token, then falls back to the configured method.
func (*Manager) Login ¶
Login runs the configured fresh-auth flow unconditionally, without attempting to reuse an existing token. Used by `dotvault login` and as the fallback path inside Authenticate.
func (*Manager) ReissueIfDue ¶ added in v0.23.0
ReissueIfDue rotates the certificate when it is inside the re-issue window, using the current operational Vault token (no human). It is a no-op for non-cert methods, when no credential exists, or when the cert is not yet due for rotation. The daemon calls this periodically so a long-running process whose token keeps renewing still rotates its certificate before expiry — without this, a warm daemon never re-enters the cert flow and the cert could expire unrotated. Safe to call repeatedly: after one successful rotation the fresh NotAfter moves out of the window and subsequent calls return nil.
type PolicyConstraint ¶ added in v0.25.0
type PolicyConstraint struct {
// Policies is the explicit set of Vault policies the working token should
// carry. When non-empty the login token is exchanged for a child token
// restricted to exactly these policies (a subset of the login token's own
// policies — Vault enforces the subset rule). Empty means "carry whatever
// the auth role granted".
Policies []string
// NoDefaultPolicy, when true, strips the implicit `default` policy from the
// downscoped token.
NoDefaultPolicy bool
}
PolicyConstraint describes the least-privilege downscoping applied to a freshly-minted login token. Its two fields come straight from the vault config (vault.policies / vault.no_default_policy).
The zero value applies no narrowing — the working token carries every policy the auth role granted, which is dotvault's historical behaviour. Operators opt in to least privilege by populating Policies (and, increasingly, NoDefaultPolicy); see docs/configuration/config-reference.md for the staged rollout that ends with no_default_policy forced on at 1.0.
func (PolicyConstraint) Active ¶ added in v0.25.0
func (c PolicyConstraint) Active() bool
Active reports whether any narrowing is requested. With neither an explicit policy set nor no_default_policy, the login token is adopted verbatim.