Documentation
¶
Overview ¶
Package sshprivilege implements liveness.PrivilegeProbeFunc: dial SSH with the host's resolved credential, run `sudo -n true`, and report whether passwordless privilege escalation is configured.
Per system-ssh-connectivity v1.2.0 the probe ALSO consults systemconfig.SecurityConfig.AllowCredentialSudoPassword; when the initial `sudo -n true` returns non-zero AND the credential carries a non-empty Password AND the policy is on, the probe retries via `sudo -S -k -p ” true` with the password fed through stdin. The retry shape is identical across the THREE inline-retry call sites — this probe, the collector's ssh.RunSudo, and discovery.probeFirewall — and drift between them is forbidden by the spec's C-09. (The compliance scan also supports password sudo, but via a different shape — a per-connection sudo-mode probe in internal/kensa, see system-connection-profile — so it is intentionally not part of this trio; it consults the SAME kill-switch + auth-method gate.)
This package is kept OUT of internal/liveness because the liveness package's AC-14 invariant forbids credential + crypto/ssh imports. The PrivilegeProbeFunc is wired in cmd/openwatch/main.go where both the credential resolver and the liveness service are already in scope.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Probe ¶
func Probe(resolver Resolver, opts ...ProbeOption) liveness.PrivilegeProbeFunc
Probe builds a liveness.PrivilegeProbeFunc backed by the given resolver. The returned function:
- Looks up the host's credential. On ErrNoCredential it returns attempted=false (the multi-layer state machine leaves the privilege axis untouched).
- Dials TCP-22 with cfg.Timeout = timeout.
- Runs `sudo -n true`. Exit 0 → ok=true.
- Non-zero exit AND AllowCredentialSudoPassword AND cred.Password non-empty AND cred.AuthMethod ∈ {password, both} → retries as `sudo -S -k -p ” true` with the password fed via stdin. Exit 0 from the retry → ok=true. Any other outcome → ok=false.
Spec system-ssh-connectivity v1.2.0 C-09, AC-18, AC-19, AC-21.
The probe dials through internal/ssh.Dial — the SAME path the compliance scan and discovery use — so its auth-method handling (key, password, AND PAM keyboard-interactive) and host-key verification stay identical and cannot drift. In particular it offers keyboard-interactive, so a hardened host (PasswordAuthentication no + UsePAM keyboard-interactive) authenticates here exactly as it does for a scan. Host keys are verified via TOFU against the shared registry when WithKnownHosts is wired.
Types ¶
type ConnProfileStore ¶
type ConnProfileStore interface {
Get(ctx context.Context, hostID uuid.UUID) (connprofile.Profile, error)
RecordSSHAuth(ctx context.Context, hostID uuid.UUID, m connprofile.SSHAuthMethod) error
RecordSudoMode(ctx context.Context, hostID uuid.UUID, m connprofile.SudoMode) error
}
ConnProfileStore is the subset of connprofile the probe uses to lead the dial with the host's known-good SSH auth method AND its sudo mode, and to record what actually worked. nil (the default) disables learning.
The liveness probe is the authoritative sudo-mode learner: it runs an innocuous `true` sentinel every cycle (~5 min), so unlike the opportunistic discovery/collector paths it reliably confirms the mode.
type Dialer ¶
type Dialer interface {
Dial(ctx context.Context, cred *credential.Credential, addr string, timeout time.Duration, prefer connprofile.SSHAuthMethod) (SessionExecutor, connprofile.SSHAuthMethod, error)
}
Dialer opens an SSH session against a host. Production uses realDialer (crypto/ssh.Dial); tests inject a stub.
prefer is the host's learned SSH auth method (connprofile.AuthUnknown when none): the dialer leads with it but still offers the other method. The returned method is the one that authenticated, for the caller to record. Both are best-effort learning, never a hard requirement.
type PolicyLoader ¶
type PolicyLoader interface {
LoadSecurity(ctx context.Context) (systemconfig.SecurityConfig, error)
}
PolicyLoader returns the current SecurityConfig — specifically the AllowCredentialSudoPassword flag that gates the sudo -S fallback. Implementations typically wrap systemconfig.Store.LoadSecurity. A nil loader OR an error from the loader defaults to "policy off" so the fallback path stays opt-in.
type ProbeOption ¶
type ProbeOption func(*probeConfig)
ProbeOption configures the probe at construction time. Use WithDialer / WithPolicyLoader.
func WithDialer ¶
func WithDialer(d Dialer) ProbeOption
WithDialer overrides the production SSH dialer. Tests pass a stub that doesn't open real connections.
func WithKnownHosts ¶
func WithKnownHosts(store owssh.KnownHostsStore) ProbeOption
WithKnownHosts pins the probe's SSH dial to the shared host-key registry (TOFU), so the liveness probe verifies host keys exactly like the scan and discovery paths instead of ignoring them. Pass the same pool-backed store the other paths use (knownhosts.NewStore(pool)). nil (the default) still uses TOFU but against an in-process memory store (no cross-restart persistence) — fine for tests, not for production.
func WithPolicyLoader ¶
func WithPolicyLoader(p PolicyLoader) ProbeOption
WithPolicyLoader wires the systemconfig reader the probe consults to decide whether to engage the sudo -S fallback. A nil loader (the default) is equivalent to "policy off".
Spec system-ssh-connectivity v1.2.0 C-09 / AC-18.
func WithProfiles ¶
func WithProfiles(p ConnProfileStore) ProbeOption
WithProfiles enables per-host SSH auth-method learning: the probe leads the dial with the host's recorded method and records which method authenticated. nil (the default) keeps the historical key-first, no-learning order. See system-connection-profile.
type Resolver ¶
type Resolver interface {
Resolve(ctx context.Context, hostID uuid.UUID) (*credential.Credential, error)
}
Resolver returns the credential to use for a host. Implementations typically wrap credential.Service.Resolve. ErrNoCredential is the "no auth configured" signal — the probe records attempted=false and returns without touching SSH.
type SessionExecutor ¶
type SessionExecutor interface {
Run(ctx context.Context, cmd string) ([]byte, int, error)
RunWithStdin(ctx context.Context, cmd string, stdin io.Reader) ([]byte, int, error)
Close() error
}
SessionExecutor is the seam between the probe and the SSH session. Production wraps a real *ssh.Client (see realSession below); tests stub it directly to verify the call ordering without standing up an SSH server.
RunWithStdin feeds the reader's content into the remote process's stdin — used to deliver the credential password to `sudo -S`. The reader is consumed once.