bastion 0.54.1
ci / personal-data (push) Successful in 6s
ci / fuzz (push) Successful in 2m49s
ci / test (push) Successful in 3m7s

This commit is contained in:
Privatae
2026-10-21 23:39:13 +00:00
parent d62da8adb4
commit 77b7853ae1
28 changed files with 1505 additions and 1373 deletions
+87
View File
@@ -0,0 +1,87 @@
# A Host on this machine
A Host with no platform (a customer's own server with the Host on it) pairs
with a bastion on the same machine, so its threads can build and test code in
sealed coder runs. Nothing about the walls changes: mutual TLS and the bearer
on every lane, a CA this bastion pins, grants the Host signs and this bastion
verifies on every operation, an anchored machine. What changes is who issues
the certificates (the Host's own CA) and how the two talk: unix sockets in a
directory both reach, `/run/privatae-host/` (root, group `privatae-host`,
1770: the Host runs without privileges and makes its two sockets there, and the
sticky bit keeps it from moving what root made; `runs/` inside it is 0750 root,
so only this bastion makes run sockets), instead of WireGuard through the relay.
| socket | served by | what |
|---|---|---|
| `/run/privatae-host/enroll.sock` | the Host | `pair` redeems its token here (`POST /bastion/enroll/redeem`, the platform redeem's body) |
| `/run/privatae-host/lane.sock` | the Host | every lane, as over the tunnel to `:8443` |
| `/run/privatae-host/runs/<run_id>.sock` | this bastion | one coder run's control plane, for the owning pairing only |
The commands an install script runs on such a machine, as root:
```sh
# 1. The bastion as the machine's service, in networked mode.
curl -fsSL https://privatae.ai/bastion/install.sh | sudo sh -s -- --networked
# 2. Pair with the Host over its enroll socket (the token comes from the Host).
sudo -H privatae-bastion pair --redeem-url unix:///run/privatae-host/enroll.sock <token>
# 3. Make the Host the anchor: from now on every coder run-up needs the
# standing coder grant the Host signs.
sudo -H privatae-bastion claim-anchor --owner <name>
# 4. A way to approve at this machine: it is its own approval device.
privatae-bastion device-init # prints the pin-device line
sudo -H privatae-bastion pin-device <key it printed>
# 5. Coder runs of the workspace engine, from images built on this machine.
sudo -H privatae-bastion coders set --engines workspace --image-prefix localhost/privatae-harness/
sudo -H privatae-bastion coders on
sudo systemctl restart privatae-bastion
```
**The one thing the owner does by hand:** approve the Host's coder grant, once,
on this machine's console (`privatae-bastion ui`, then Approvals). Until then
every run-up is refused at the gate.
A `unix://` redeem URL names an absolute socket path and nothing else: a
host, a port or a relative path is refused before the token is read. The
Host's answer names its CA, the lane socket and the runs directory, and both
must be inside the directory of the enroll socket you named. The CA is pinned
as the platform's is: it is the only root the lanes trust, the Host's lane
certificate must carry the pairing's name, and a `pair --replace` that brings a
different CA is refused.
The image is built on the machine and tagged
`<prefix>harness-workspace:<tag>`; the Host names it
`<prefix>harness-workspace@<image ID>`, the ID from
`docker image inspect --format '{{.Id}}'`. Run-up takes an image that is
present that way without a pull or any registry credential. Docker's classic
image store keeps no repo digest for an image built or loaded locally, so
there the run starts from the image ID, and only when the image with that ID
carries a tag of the named repository.
## A Host in a VM on this machine
A Host with no platform that runs in a VM on this machine shares no kernel and
no directory with it. Its installer forwards two ports of the VM to this
machine's loopback and hands over a pairing token with the Host's CA:
```sh
privatae-bastion pair --redeem-url https://127.0.0.1:8810/v1/local/redeem --host-ca host-ca.pem <token>
privatae-bastion run
```
| port | what |
|---|---|
| `8810` | `pair` redeems its token here (`POST /v1/local/redeem`, the platform redeem's body) |
| `8811` | every lane, as over the tunnel to `:8443` |
The redeem and every lane go over TLS that trusts the `--host-ca` alone (no
system roots) and checks the name `host.cseries.internal` on the Host's
certificate; the lanes present this bastion's operator certificate, which the
Host knows it by. The Host's answer must carry that same CA and name a lane on
the host the redeem URL names. The CA is pinned for the pairing's life, as for
a unix pairing. `--host-ca` goes with an `https://` redeem URL only, and both
are checked before the token is read.
Such a pairing has no WireGuard forwards (inference, PII, service slots) and
no coder runs: a run's control plane is a tunnel port or a socket in a unix
pairing's runs directory, and a TCP pairing has neither, so its run-up is
refused before any container starts.