Skip to content

Add from-source Docker image and compose for the pass-through server - #152

Open
eaxi wants to merge 4 commits into
lance0:masterfrom
eaxi:pr-c-docker
Open

eaxi wants to merge 4 commits into
lance0:masterfrom
eaxi:pr-c-docker

Conversation

@eaxi

@eaxi eaxi commented Oct 6, 2026

Copy link
Copy Markdown

Problem

Dockerfile.release only wraps a prebuilt binary from the release matrix, so there is no way to build and run the server from a checkout — and the multi-user pass-through mode (see #151) is exactly the deployment where a container is the natural shape: one shared process, TLS terminated in front.

Change

A multi-stage Dockerfile (build from source with --locked, runtime on debian:bookworm-slim) and a docker-compose.yml aimed at the pass-through mode.

Security posture of the runtime image:

  • unprivileged (USER 10001, no shell)
  • read-only root filesystem, all capabilities dropped, no-new-privileges
  • CA certificates only — no credential, token, or config baked in
  • publishes on loopback by default; widen via NBOX_BIND/NBOX_PORT only behind a TLS-terminating proxy
  • optional NBOX_CA_BUNDLE mount (/etc/nbox/ca.pem, :ro) for a NetBox behind an internal CA — harmless when unset

examples/nbox-docker.toml is deliberately token-free: in pass-through mode the caller's token is the only credential, and a missing one must fail rather than fall back to a server-side identity.

Notes for review

Stacked on #151 — merge after it; until then the diff also shows the pass-through changes. If you'd rather keep only Dockerfile.release, happy to drop this — the compose file is the part with real value.

Checks

No Rust-code changes: fmt/clippy/build/test green as on the base branch (1404 passed / 0 failed). Docker build verified locally (docker compose build + config smoke).

eaxi and others added 4 commits October 6, 2026 15:58
`nbox serve --http --netbox-token-passthrough` (or
`[serve].netbox_token_passthrough = true`) lets one process serve many
users: each caller presents their own NetBox API token per request and
nbox forwards it verbatim, so NetBox's object permissions and changelog
apply to the real caller — no IdP and no `[serve.vault]` mapping.

The token is read from `X-NetBox-Token`, or from `Authorization` when
that header isn't already used by OIDC or `--http-token` (so a host that
can only set a bearer works, without ever mistaking a JWT for a NetBox
token). Each request builds its own client from the caller's credential.

Isolation is a correctness requirement here, not an optimization, since
NetBox returns different results to different users:

- the read cache is partitioned per token fingerprint;
- `nbox_cache_clear` drops only the caller's own partition;
- `--allow-writes` runs writes under the caller's token.

Fail closed: a request without a caller token is rejected with 401 and
never downgraded to the server's profile token. Tokens are redacted in
Debug, logs, and errors; the audit log carries `auth=netbox-token` plus a
short SHA-256 fingerprint, which also keys the per-caller rate limit.
Pass-through requires the HTTP transport — stdio has no per-request
headers, so asking for it there is a usage error rather than a silent
no-op — and it allows a routable bind, with TLS terminated in front.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ADR-0001 §7 documents MCP writes as running "in one of two explicit modes";
pass-through adds a third credential path, so the decision needs a record of
the same rank. Follows the ADR-0002 precedent: what problem the mode solves
(no per-user RBAC without an IdP and vault), the decision (caller NetBox API
token forwarded verbatim, fail closed, cache partitioned per fingerprint), and
the accepted trade-offs (TLS required in front, IP-keyed pre-auth rate limit,
tokens in process memory).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`Dockerfile.release` only wraps a prebuilt binary from the release matrix, so
there was no way to build and run the server from a checkout. Add a multi-stage
`Dockerfile` plus a `docker-compose.yml` aimed at the multi-user pass-through
mode, where running a shared container is the point.

The runtime image is unprivileged (uid 10001), read-only, drops all
capabilities, and carries nothing but the binary and CA certificates. It
publishes on loopback so a TLS-terminating proxy fronts it — callers send real
NetBox credentials on every request, so plaintext off-host would leak them.

No NetBox token is baked into the image or the shipped config: in pass-through
mode the container has no shared credential, which is the security property
worth preserving in the packaging too. The config is file-backed rather than
inline because a `read_only` service cannot take content-based compose configs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compose previously hard-coded 127.0.0.1:8080 and offered no way to give the
container extra trust anchors. Set NBOX_BIND/NBOX_PORT to publish elsewhere
(loopback stays the default — callers send real NetBox credentials on every
request, so anything wider should be fronted by a TLS-terminating proxy), and
mount ${NBOX_CA_BUNDLE} at /etc/nbox/ca.pem for a NetBox behind an internal
CA, harmless when unset.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant