terminal: per-Authentik-user OS-user isolation; deny unmapped users
Restores the kernel-level isolation the pre-cutover ttyd-session.sh had, but keeps the multi-session lobby UX: - ttyd.service gets `-H X-authentik-username` back. `tmux-attach.sh` reads $TTYD_USER, looks up the local part in /etc/ttyd-user-map, denies the connection (no fallback to wizard) if there's no mapping, otherwise `sudo -n -H -u <os_user> tmux …`. Each Authentik identity → its own Unix user → its own `/tmp/tmux-<uid>/default` socket. - tmux-api scopes every request to the same OS user via the same header. Adds /whoami so the lobby HTML can preflight access and render "logged in as <os_user> (<authentik>)" instead of leaving the user to discover the deny via a reconnect loop. - Commits /etc/ttyd-user-map and the matching /etc/sudoers.d/ttyd-users fragment under files/devvm/ so future operators see one canonical source of truth. Current mappings: vbarzin → wizard, emil.barzin → emo. Adding a user is now: append a line to ttyd-user-map + a NOPASSWD sudoers line + `useradd -m`. README walks through it. No Terraform changes — this is all DevVM-side + lobby JS.
This commit is contained in:
parent
aff4f67671
commit
9fce3c7b09
7 changed files with 316 additions and 65 deletions
|
|
@ -1,22 +1,52 @@
|
|||
# DevVM terminal files
|
||||
|
||||
These files configure ttyd + tmux-api on the DevVM (`10.0.10.10`). ttyd
|
||||
serves the multi-session lobby (and per-session attach via `?arg=<name>`)
|
||||
on port 7681; tmux-api is a small Go REST API on 7684 that powers the
|
||||
lobby's list/kill actions.
|
||||
ttyd + tmux-api on the DevVM (`10.0.10.10`). ttyd serves the multi-session
|
||||
lobby on port 7681 and attaches each Authentik identity into its own OS
|
||||
user's tmux server. tmux-api (port 7684) backs the lobby's list/kill
|
||||
actions, scoped to the same OS user.
|
||||
|
||||
`terminal-ro.service` (port 7682, single read-only session) and
|
||||
`clipboard-upload` (port 7683) are unchanged by these files.
|
||||
|
||||
## Per-user isolation
|
||||
|
||||
The Authentik forward-auth middleware injects `X-authentik-username` on
|
||||
every authenticated request:
|
||||
|
||||
1. **ttyd** is started with `-H X-authentik-username`, so the header value
|
||||
lands as `$TTYD_USER` in each launched `tmux-attach.sh` invocation.
|
||||
2. **`tmux-attach.sh`** looks up `$TTYD_USER` in `/etc/ttyd-user-map`,
|
||||
denies the connection if there is no mapping, and otherwise
|
||||
`sudo -n -H -u <os_user> /usr/bin/tmux …`.
|
||||
3. **`tmux-api`** reads `X-authentik-username` on every request and runs
|
||||
tmux as the mapped OS user too — so the lobby's session list is the
|
||||
intersection of "your Authentik identity" and "what tmux on that OS
|
||||
user's socket reports".
|
||||
|
||||
Different Authentik identities map to different Unix users, which means
|
||||
different `/tmp/tmux-<uid>/default` sockets — kernel-level isolation,
|
||||
not "the API filtered the list".
|
||||
|
||||
Adding a new user:
|
||||
|
||||
1. Append a line to `/etc/ttyd-user-map` (canonical at
|
||||
`files/devvm/ttyd-user-map`).
|
||||
2. Append `wizard ALL=(<os_user>) NOPASSWD: /usr/bin/tmux` to
|
||||
`/etc/sudoers.d/ttyd-users` (canonical at
|
||||
`files/devvm/sudoers.d-ttyd-users`).
|
||||
3. Ensure the OS user exists (`useradd -m <os_user>`).
|
||||
|
||||
## Layout
|
||||
|
||||
| Source | Destination on DevVM |
|
||||
|--------|----------------------|
|
||||
| `tmux-attach.sh` | `/usr/local/bin/tmux-attach.sh` (chmod 0755) |
|
||||
| `ttyd.service` | `/etc/systemd/system/ttyd.service` |
|
||||
| `tmux-api.service` | `/etc/systemd/system/tmux-api.service` |
|
||||
| `../index.html` (one level up) | `/usr/local/share/ttyd/index.html` |
|
||||
| `../../tmux-api/` binary, built `GOOS=linux GOARCH=amd64` | `/usr/local/bin/tmux-api` (chmod 0755) |
|
||||
| Source | Destination on DevVM | Mode |
|
||||
|--------|----------------------|------|
|
||||
| `tmux-attach.sh` | `/usr/local/bin/tmux-attach.sh` | 0755 |
|
||||
| `ttyd.service` | `/etc/systemd/system/ttyd.service` | 0644 |
|
||||
| `tmux-api.service` | `/etc/systemd/system/tmux-api.service` | 0644 |
|
||||
| `ttyd-user-map` | `/etc/ttyd-user-map` | 0644 |
|
||||
| `sudoers.d-ttyd-users` | `/etc/sudoers.d/ttyd-users` | 0440, root:root |
|
||||
| `../index.html` (one dir up) | `/usr/local/share/ttyd/index.html` | 0644 |
|
||||
| `../../tmux-api/` Go binary | `/usr/local/bin/tmux-api` | 0755 |
|
||||
|
||||
## Apply
|
||||
|
||||
|
|
@ -28,12 +58,19 @@ DEVVM=10.0.10.10 # SSH config provides the user
|
|||
# 1. Build the tmux-api binary for linux/amd64
|
||||
( cd infra/stacks/terminal/tmux-api && GOOS=linux GOARCH=amd64 go build -o /tmp/tmux-api . )
|
||||
|
||||
# 2. HTML page + wrapper script
|
||||
scp infra/stacks/terminal/files/index.html $DEVVM:/tmp/index.html
|
||||
scp infra/stacks/terminal/files/devvm/tmux-attach.sh $DEVVM:/tmp/tmux-attach.sh
|
||||
ssh $DEVVM "sudo install -m 0644 /tmp/index.html /usr/local/share/ttyd/index.html && \
|
||||
sudo install -m 0755 /tmp/tmux-attach.sh /usr/local/bin/tmux-attach.sh && \
|
||||
rm /tmp/index.html /tmp/tmux-attach.sh"
|
||||
# 2. HTML + config files
|
||||
scp infra/stacks/terminal/files/index.html $DEVVM:/tmp/index.html
|
||||
scp infra/stacks/terminal/files/devvm/tmux-attach.sh $DEVVM:/tmp/tmux-attach.sh
|
||||
scp infra/stacks/terminal/files/devvm/ttyd-user-map $DEVVM:/tmp/ttyd-user-map
|
||||
scp infra/stacks/terminal/files/devvm/sudoers.d-ttyd-users $DEVVM:/tmp/sudoers.d-ttyd-users
|
||||
ssh $DEVVM "
|
||||
sudo install -m 0644 /tmp/index.html /usr/local/share/ttyd/index.html
|
||||
sudo install -m 0755 /tmp/tmux-attach.sh /usr/local/bin/tmux-attach.sh
|
||||
sudo install -m 0644 /tmp/ttyd-user-map /etc/ttyd-user-map
|
||||
sudo install -m 0440 -o root -g root /tmp/sudoers.d-ttyd-users /etc/sudoers.d/ttyd-users
|
||||
sudo visudo -cf /etc/sudoers.d/ttyd-users
|
||||
rm /tmp/index.html /tmp/tmux-attach.sh /tmp/ttyd-user-map /tmp/sudoers.d-ttyd-users
|
||||
"
|
||||
|
||||
# 3. tmux-api binary
|
||||
scp /tmp/tmux-api $DEVVM:/tmp/tmux-api
|
||||
|
|
@ -42,32 +79,30 @@ ssh $DEVVM "sudo install -m 0755 /tmp/tmux-api /usr/local/bin/tmux-api && rm /tm
|
|||
# 4. systemd units
|
||||
scp infra/stacks/terminal/files/devvm/ttyd.service $DEVVM:/tmp/
|
||||
scp infra/stacks/terminal/files/devvm/tmux-api.service $DEVVM:/tmp/
|
||||
ssh $DEVVM "sudo mv /tmp/ttyd.service /etc/systemd/system/ && \
|
||||
sudo mv /tmp/tmux-api.service /etc/systemd/system/ && \
|
||||
sudo systemctl daemon-reload && \
|
||||
sudo systemctl enable --now tmux-api && \
|
||||
sudo systemctl restart ttyd"
|
||||
ssh $DEVVM "
|
||||
sudo mv /tmp/ttyd.service /etc/systemd/system/
|
||||
sudo mv /tmp/tmux-api.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now tmux-api
|
||||
sudo systemctl restart ttyd
|
||||
"
|
||||
|
||||
# 5. Sanity checks
|
||||
ssh $DEVVM "systemctl status ttyd tmux-api --no-pager"
|
||||
ssh $DEVVM "curl -sf localhost:7684/sessions"
|
||||
ssh $DEVVM "curl -sf localhost:7681/ | head -5"
|
||||
ssh $DEVVM "systemctl is-active terminal-ro" # unrelated unit, unaffected
|
||||
ssh $DEVVM "curl -sf -H 'X-Authentik-Username: vbarzin' localhost:7684/whoami"
|
||||
ssh $DEVVM "curl -sf -H 'X-Authentik-Username: emil.barzin' localhost:7684/whoami"
|
||||
ssh $DEVVM "curl -si -H 'X-Authentik-Username: nobody' localhost:7684/whoami | head -3"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **`User=wizard`** — single Unix user owns the tmux server. Sessions are
|
||||
shared across every browser tab that attaches.
|
||||
- **ttyd version** must be ≥ 1.7 for the `-a` flag (allow URL args → argv).
|
||||
The DevVM currently has 1.7.7.
|
||||
- **Argv flow**: `?arg=foo` on the URL → ttyd appends `foo` as `$1` to
|
||||
`tmux-attach.sh` → the wrapper regex-validates and runs
|
||||
`tmux new-session -A -s "$name"`. ttyd uses argv (never a shell string),
|
||||
so there is no injection path.
|
||||
- **No external exposure of 7684/7681** — the DevVM is reachable only from
|
||||
the cluster (`10.0.10.10` is on the internal VLAN). Authentik forward-auth
|
||||
on the ingress is the access gate.
|
||||
- **ttyd ≥ 1.7** required for the `-a` flag (URL args → argv). DevVM has 1.7.7.
|
||||
- **Argv flow**: `?arg=foo` → ttyd appends `foo` as `$1` to `tmux-attach.sh`
|
||||
→ wrapper regex-validates and runs `tmux new-session -A -s "$name"`. ttyd
|
||||
uses argv, never a shell string — no injection path.
|
||||
- **No external exposure of 7681/7684** — DevVM is internal-VLAN-only;
|
||||
Authentik forward-auth is the access gate.
|
||||
- **Cutover history** — `term.viktorbarzin.me` and `ttyd-multi.service`
|
||||
(port 7685) were the staging surface for this design. Both were retired
|
||||
in the same commit that promoted the multi-session config to port 7681.
|
||||
(port 7685) were the staging surface for this design; both retired
|
||||
when the multi-session config was promoted to port 7681. The
|
||||
per-Authentik-user isolation followed in a separate change.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue