Documentation
¶
Overview ¶
Package hostauth verifies the host OS's own credentials. The application stores no passwords — every authentication goes through the OS subsystem (Open Directory on macOS, the unix_chkpwd/shadow pair on Linux, SAM on Windows) so the same password that unlocks the machine is what unlocks remote superadmin. The Linux path is deliberately cgo-free so the cross-compiled (CGO_ENABLED=0) release binary authenticates correctly — see hostauth_linux.go.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidCredentials = errors.New("invalid credentials")
ErrInvalidCredentials is returned when the OS rejects the supplied username/password pair.
var ErrNotImplemented = errors.New("host auth not implemented on this OS")
ErrNotImplemented is reserved for any future OS that lacks a credential authenticator. All currently-supported platforms (macOS, Linux, Windows) have real implementations, so no live path returns this today.
Functions ¶
func BareUsername ¶ added in v0.5.4
BareUsername strips platform qualifiers from an account name so the same human-typed username works against every OS:
- "MACHINE\alice" / "DOMAIN\alice" (Windows down-level) → "alice"
- "alice@example.com" (Windows UPN) → "alice"
- "alice" (Unix / bare) → "alice"
Unix account names cannot contain '\' or '@' (useradd and macOS both forbid them), and Windows SAM account names forbid both characters too, so stripping at the last '\' / first '@' is unambiguous: any qualifier present is exactly one of the two Windows forms.
func CurrentDisplayName ¶
func CurrentDisplayName() string
CurrentDisplayName returns the GECOS / "full name" of the agent's OS user (e.g. "Alice Smith"). May be empty on stripped-down systems; the portal falls back to the username in that case.
func CurrentUser ¶
CurrentUser returns the agent process's own username — used as the canonical "this host's account". The matrix-agent runs as a user-level service, so this is the account the in-process shell will inherit.
func SameUser ¶ added in v0.5.4
SameUser reports whether a submitted username refers to the same account as the canonical one, accepting either the exact local form or the bare (unqualified) form, case-insensitively. This is the single equality rule for the "submitted user must be the agent's own OS user" gates (/auth and the SSH PasswordCallback).
On Windows, Go's user.Current().Username is the SAM-compatible "MACHINE\user" — demanding that exact string from an SSH client is hostile (quoting a backslash plus a space), so "user" alone must match too. The match only widens the *comparison*: callers still authenticate the canonical name, so the password remains the gate.
Types ¶
type Authenticator ¶
Authenticator verifies a username/password pair against the host OS. Implementations live in per-OS files (hostauth_<goos>.go).
func DefaultAuthenticator ¶
func DefaultAuthenticator() Authenticator
DefaultAuthenticator on Linux verifies the OS password WITHOUT cgo or libpam. It shells out to the setuid unix_chkpwd PAM helper and, if that rejects a valid password, falls back to reading /etc/shadow directly and verifying the stored crypt(3) hash in pure Go.
Why not cgo PAM (the previous implementation)? Outpost release binaries are cross-compiled with CGO_ENABLED=0 (scripts/build-all.sh), so the cgo PAM authenticator never linked in practice — the host fell through to the no-cgo stub that returned ErrNotImplemented and rejected *every* password, which is why a freshly-paired Linux host could not be connected to at all. A pure-Go implementation means the standard cross-compiled binary authenticates correctly with no libpam-dev build dependency.
Why the /etc/shadow fallback? Ubuntu 26.04 ships PAM 1.7.0 with a unix_chkpwd helper that rejects otherwise-valid passwords; reading the shadow hash ourselves sidesteps it. The fallback requires the outpost process to be able to read /etc/shadow (shadow-group membership, or running as root) — when it can't, only the unix_chkpwd path is available.
type StubAuth ¶
StubAuth always returns the configured result. Tests inject this so we don't shell out to dscl during unit/integration tests.