bastion 0.54.1
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# Tunnel and lanes
|
||||
|
||||
## Tunnel
|
||||
A userspace WireGuard tunnel (gVisor netstack) scoped so this machine can reach
|
||||
**only its own host**. No TUN device is created and `run` does not need root.
|
||||
The tunnel is dialled from this machine. Inside it, the bastion accepts on
|
||||
exactly three addresses, all under the `inference` capability family: a
|
||||
forward to a local ollama (port 11434), a forward to an OpenAI-compatible
|
||||
server when one is configured (port 18080), and a forward to a local PII
|
||||
sterilizer when one is configured. Nothing else listens.
|
||||
|
||||
## Git lane
|
||||
A loopback HTTP proxy plus a `git-credential` helper, so your local `git` reaches
|
||||
`git.privatae.ai` through your host. The forge answers platform addresses only,
|
||||
so this lane is the way in. `privatae-bastion git-setup` prints the three
|
||||
config lines: the proxy, a reset of the credential-helper chain for that URL
|
||||
(so no other helper caches your token on this machine), and the helper itself.
|
||||
|
||||
## Worker lanes
|
||||
Outbound-only reverse relays to the host: egress, management, harness, and
|
||||
session. The management lane runs a **fixed-argv allowlist**. Ops that touch
|
||||
your files are dedicated Go handlers with no shell. Of those file handlers,
|
||||
the one that runs a program is text extraction: it invokes a local tool named
|
||||
in a fixed table
|
||||
(`pdftotext`, `ffprobe`, and the like) from a fixed argv template, and the
|
||||
caller supplies only the tool's name and the document.
|
||||
|
||||
## Session lane
|
||||
An interactive terminal that runs on this machine and streams to the host over
|
||||
the same reverse WebSocket the other lanes use. No inbound port, no SSH server.
|
||||
It is your own act in your Terminal app, and needs no standing grant from the
|
||||
host (0.39.0): on a machine anchored to a host, `session:open` takes your
|
||||
approval from your device or this console, bound to that one session; on a
|
||||
local machine the pairing's caps are the authorization. A grant the host still
|
||||
presents is verified as before, and a deny in it refuses. `session:open` runs
|
||||
as an unprivileged user when the daemon is root. `session:root` runs as root
|
||||
only when the daemon is root **and** you approved that specific session here
|
||||
with the enrolled second factor. Every session is logged locally (output only) and recorded in the
|
||||
pairing's audit file. Not available on Windows.
|
||||
|
||||
**Root commands (0.49.0).** The anchor host can also run one command at a
|
||||
time as root, with no terminal and no approval per command, once you have
|
||||
granted it here:
|
||||
|
||||
```sh
|
||||
sudo -H privatae-bastion root-exec grant --host <anchor host> --for 30d # asks for the code enrolled here
|
||||
privatae-bastion root-exec [--host <host>] # what is granted, until when
|
||||
privatae-bastion root-exec revoke [--host <host>] # take it back
|
||||
```
|
||||
|
||||
The grant names one host (the anchor; a member pairing never gets it) and
|
||||
this machine, ends at the time you chose, and is written only by that command
|
||||
with the code; nothing the host sends can create, widen or extend it. Each
|
||||
command runs as `/bin/sh -c` in its own process group, with a deadline (10
|
||||
minutes unless the host asks for less or more, never more than 8 hours) and
|
||||
with stdout and stderr returned up to a size cap (256 KiB each unless the host
|
||||
asks, at most 4 MiB); a cut output keeps its start and its end and says how
|
||||
many bytes were dropped. At the deadline the whole group gets SIGTERM, then
|
||||
SIGKILL 2 seconds later, so a child that ignores SIGTERM goes too. The
|
||||
bastion must run as root, the `session` family switched off here stops it,
|
||||
and a quarantined pairing runs nothing. These are checked again every 2
|
||||
seconds while a command runs: a revoke, the grant's expiry, the switch or a
|
||||
quarantine stops a command already running, and its answer says why. Every
|
||||
command is written whole, with its output (up to 64 MiB), to the context
|
||||
store's `sessions/<id>.log`, and recorded at start and stop in the audit file
|
||||
with its sha256, exit and byte counts; a refused, malformed or failed one gets
|
||||
an audit row too. Not available on Windows.
|
||||
|
||||
The bastion also records where each root command connects. On Linux the
|
||||
command starts in a cgroup of its own, and an nftables table the bastion
|
||||
creates for that command records every connection the command and its
|
||||
children open; the table only records (it never allows or drops anything)
|
||||
and is removed when the command ends, or at the next start if the bastion
|
||||
died mid-command. Destinations outside the platform's own hosts are returned
|
||||
to the host as `untrusted_connections`, and the host then treats that output
|
||||
as untrusted and ends root for the turn. List the platform's own hosts in
|
||||
`platform_hosts.json` in the bastion's config folder:
|
||||
|
||||
```json
|
||||
{"hosts": ["2001:db8::/32", "203.0.113.0/24", "git.example.org"]}
|
||||
```
|
||||
|
||||
With no list, every connection that leaves this machine counts as outside;
|
||||
loopback never does. A command whose connections could not be watched is
|
||||
reported as if it had connected out. What it cannot see: connections another
|
||||
program makes on the command's behalf, such as dockerd pulling an image for
|
||||
`docker build`, or a service `systemctl` starts. A root command can also leave
|
||||
its cgroup or delete the table on purpose; a missing table reads as "not
|
||||
watched", but a command that leaves its cgroup is not detected.
|
||||
Reference in New Issue
Block a user