Files
Privatae 48899b41a9
ci / personal-data (push) Successful in 4s
ci / test (push) Successful in 4m58s
ci / fuzz (push) Successful in 3m33s
bastion 0.63.1
2026-11-03 22:56:18 +00:00

161 lines
7.1 KiB
Go

package main
// The machine-authority gate, in ONE place.
//
// Five call sites used to ask `os.Geteuid() != 0` directly: set-mode networked
// (mode.go), claim-anchor and claim-anchor --repin (anchor.go), claim-owner
// (owner.go), and unpair-on-a-networked-box (config.go). On Windows that
// expression is always true — Go hardcodes Geteuid to -1 there — so all five
// were permanently refused, and a Windows machine could never become a networked
// bastion or be anchored. Nobody chose that; it fell out of a Unix idiom used
// where the platform has no such concept.
//
// So the question is asked through processIsRoot(), which each platform answers
// in its own terms (euid 0 on Unix; elevated + Administrators on Windows,
// privilege_windows.go), and the REFUSAL text is written once, here, so it tells
// the user what to actually do on the machine they are actually on.
import (
"fmt"
"runtime"
)
// adminHowTo is the platform's instruction for "do this with machine authority".
// Kept beside the check so a new gate cannot invent its own wording, and so the
// Unix text never reaches a Windows user (telling someone to run `sudo` on
// Windows is how a real instruction becomes noise).
func adminHowTo() string { return adminHowToFor(runtime.GOOS) }
func adminHowToFor(goos string) string {
if goos == "windows" {
return "run this from an Administrator terminal (right-click → Run as administrator)"
}
return "run it with `sudo -H`"
}
// machineAuthorityHeld is processIsRoot, held in a variable so a test can
// assert both sides of every machine-authority gate whatever euid the suite
// runs under. Nothing outside tests assigns it.
var machineAuthorityHeld = processIsRoot
// requireMachineAuthority returns nil when this process administers the machine,
// and otherwise an error naming the ACT being refused and how to retry it.
//
// `act` reads into "X must run with machine authority" — pass the command, e.g.
// "claim-anchor".
func requireMachineAuthority(act string) error {
if machineAuthorityHeld() {
return nil
}
return fmt.Errorf("%s needs machine authority — whoever administers this box is what stands behind it; %s",
act, adminHowTo())
}
// anchorCommandHint is the claim-anchor line to print after `set-mode
// networked` succeeded — the same command on both platforms, but reached
// differently, so the prefix has to match where the user actually is.
//
// NO `<token>`. It said `claim-anchor <token>` and kept saying it after the
// token became optional, so the one instruction the user is handed at the exact
// moment they need it still sent them looking for a credential that no longer
// exists. A usage string is not documentation about the command, it is the
// command — when they disagree the string wins, because it is what gets typed.
func anchorCommandHint() string { return anchorCommandHintFor(runtime.GOOS) }
func anchorCommandHintFor(goos string) string {
if goos == "windows" {
return "privatae-bastion claim-anchor (from this Administrator terminal)"
}
return "sudo -H privatae-bastion claim-anchor"
}
// setModeCommandHint is the same shape for `set-mode`, and it exists because
// the setup page hardcoded the unix spelling with no platform branch — so a
// Windows user reading the anchor panel was told to run `sudo -H`. `sudo` is
// not a command on Windows; the line was not merely unidiomatic, it was
// untypeable.
//
// -H on unix is not decoration either: set-mode writes through configDir()
// (mode.go), which is $HOME-derived, so a bare `sudo` can land the mode file in
// the invoking user's directory while the root daemon reads its own and sees no
// change. That is the same hazard fixed one command over for claim-owner,
// and main.go printed the un--H form for BOTH.
func setModeCommandHint() string { return setModeCommandHintFor(runtime.GOOS) }
func setModeCommandHintFor(goos string) string {
if goos == "windows" {
return "privatae-bastion set-mode networked (from this Administrator terminal)"
}
return "sudo -H privatae-bastion set-mode networked"
}
// runCommandHint is how to START the bastion on THIS machine, said the way the
// operator would type it. Separate from restartHint, which is about a change
// taking effect on a daemon that is already up.
func runCommandHint() string {
if systemService.installed && (runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
return serviceRunHintFor(runtime.GOOS)
}
return runCommandHintFor(runtime.GOOS)
}
func runCommandHintFor(goos string) string {
if goos == "windows" {
return "privatae-bastion run"
}
return "systemctl start privatae-bastion"
}
// restartHintFor is how to bring the daemon back after a change that only
// takes at startup, on goos (claim-anchor and pair no longer need it: a
// running bastion follows the anchor and the pairing files by itself).
//
// It said `systemctl restart privatae-bastion` unconditionally — the third
// Linux instruction printed at a Windows user inside one command's output,
// after `sudo` in the machine-authority error and PowerShell at every Mac in
// the dashboard. Windows has no systemctl, and an ordinary install has no
// service: deploy/install.ps1 installs the exe and registers a logon task. So
// the honest instruction there is to start it again. A networked install on
// macOS or Windows runs as a system service (sysservice.go), and for that
// machine serviceRestartHintFor names the service manager's own restart instead.
//
// TAKES THE PLATFORM AS AN ARGUMENT, and that is the point. The first version
// branched on runtime.GOOS directly, so the only machine that could test the
// Windows string was a Windows machine — a mutation reverting this exact fix
// passed clean on the Linux builder. That is the same hole that let 'darwin'
// match 'win' in the dashboard: a platform branch nothing off-platform can
// reach is a platform branch nothing checks.
func restartHintFor(goos string) string {
if goos == "windows" {
return "Start the bastion again so the change takes: privatae-bastion run\n" +
" (Windows has no service for it — it runs in a terminal, so close the old one first.)"
}
return "Restart the service so the change takes: systemctl restart privatae-bastion"
}
// stepupEnrollHint is the stepup-enroll line `pair` prints for a pairing with
// no second factor. It is run as whoever ran `pair`, since that is whose state
// the pairing is in: with machine authority (an anchored or networked machine,
// a root install) through sudo -H or this Administrator terminal, and as the
// person's own account on a local install. --host is named when more than one
// host is paired, as stepup-enroll then requires.
func stepupEnrollHint(host string, paired int) string {
return stepupEnrollHintFor(runtime.GOOS, machineAuthorityHeld(), host, paired)
}
func stepupEnrollHintFor(goos string, authority bool, host string, paired int) string {
cmd := "privatae-bastion stepup-enroll"
if paired > 1 {
cmd += " --host " + host
}
switch {
case goos == "windows" && authority:
return cmd + " (from this Administrator terminal)"
case goos == "windows":
return cmd
case authority:
return "sudo -H " + cmd
}
return cmd
}