bastion 0.61.0
ci / personal-data (push) Successful in 8s
ci / test (push) Successful in 7m17s
ci / fuzz (push) Successful in 6m33s

This commit is contained in:
Privatae
2026-11-02 06:20:42 +00:00
parent 4f8285ef18
commit 1176f7fd0b
47 changed files with 3574 additions and 294 deletions
+51 -6
View File
@@ -98,17 +98,33 @@ func serviceStateEnv(goos string, installed, authority bool, systemRoot string)
}
// serviceGuardedCommands would each act on a bastion in the caller's own
// profile. Beside a system service that is a second bastion (pair, run) or a
// view of an empty one (status, `switch --list`, activity and report, which
// would print the caller's own records as though they were the machine's).
// profile. Beside a system service that is a second bastion (pair, run), a
// switch of its own, or a view of an empty one (status, `switch --list`,
// activity, report and doctor, which would print the caller's own records,
// or check its own empty bastion, as though they were the machine's).
var serviceGuardedCommands = map[string]bool{"pair": true, "run": true, "status": true, "switch": true,
"activity": true, "report": true}
"switch --list": true, "activity": true, "report": true, "doctor": true, "ops": true,
"device-approvals": true, "backup read": true,
// The narrowing acts (owneracts.go), and device-approve, which stays the
// administrator's (the console keeps it adminOnly): from an ordinary
// account each read that account's own, empty approvals, grants or asks.
"approvals drop": true, "grants": true, "device-deny": true, "device-approve": true,
// The machine's approvals: read, approved and denied through the owner's
// socket on Linux and macOS (reads.go, owneracts.go); on Windows the tray
// answers them over its own pipe, and these would read the account's own.
"approvals": true, "approve": true, "deny": true}
// serviceLaneReads are the guarded commands that, on Linux and macOS, read
// the machine's bastion itself over its socket (reads.go) rather than a
// bastion of the caller's own: not refused here, and refused there for any
// account but the owner's.
var serviceLaneReads = map[string]bool{"status": true, "activity": true, "report": true}
var serviceLaneReads = map[string]bool{"status": true, "switch --list": true, "activity": true, "report": true,
"doctor": true, "ops": true, "device-approvals": true, "backup read": true,
// Not reads: the owner's account switches, and takes reach back (`ops
// drop` is guarded as "ops"), through the same socket, with the console's
// rules (owneracts.go), and any other account is refused there.
"switch": true, "approvals drop": true, "grants": true, "device-deny": true,
"approvals": true, "approve": true, "deny": true}
// serviceGuard refuses a guarded command from an account that does not
// administer a machine whose bastion is a system service.
@@ -372,8 +388,11 @@ func adoptSystemServiceState() {
}
}
// guardServiceFork is serviceGuard for this process.
// guardServiceFork is serviceGuard for this process. `switch --list` is a
// read and `switch` a change, and the backup reads are not the rest of
// backup, so each is guarded apart.
func guardServiceFork(cmd string) error {
cmd = serviceGuardKey(cmd, os.Args)
installed := systemService.installed
if runtime.GOOS == "linux" {
installed = linuxSystemUnitPresent()
@@ -381,6 +400,32 @@ func guardServiceFork(cmd string) error {
return serviceGuard(runtime.GOOS, cmd, installed, systemService.authority)
}
// serviceGuardKey is the name the guard knows a command line by.
func serviceGuardKey(cmd string, args []string) string {
arg := func(i int) string {
if len(args) > i {
return args[i]
}
return ""
}
switch {
case cmd == "switch" && arg(2) == "--list":
return "switch --list"
case cmd == "backup" && (arg(2) == "requests" || arg(2) == "shares" || (arg(2) == "key" && arg(3) == "show")):
// The backup reads; the rest of backup works offline (reseal,
// restore-plan) or is not yet asked of the machine's bastion.
return "backup read"
case cmd == "approvals" && len(args) > 2:
// `approvals drop` is an act, the rest of approvals a read; the
// flags may come first, as main takes them (takeHostFlag).
_, rest := takeHostFlag(args[2:])
if _, rest = takeMachineFlag(rest); len(rest) > 0 && rest[0] == "drop" {
return "approvals drop"
}
}
return cmd
}
// cmdService is `privatae-bastion service install|uninstall|status`.
func cmdService(args []string) error {
if len(args) != 1 {