feat: container-based Bitwarden CLI, no host Node.js

Adds the dockerized bw deployment in production use on the TSGCOO
orchestration host since 2026-08-13: pinned debian-slim image carrying
the pre-compiled bw binary, an in-container auth lifecycle entrypoint
(config, API-key login, unlock, sync), a transparent host wrapper, and
a one-command installer.

ADR-002 records the decision and supersedes ADR-001 for BW CLI
purposes: hosts keep zero language runtimes. Known caveat documented:
the upstream "native" binary is a Node.js SEA, so Node is embedded in
the image though absent from all hosts.

Shellcheck clean (zero warnings incl. info-level).
This commit is contained in:
VP TechOps
2026-08-14 09:51:43 -05:00
committed by vptechops
parent 1936e54b5f
commit 01b0eed9fc
5 changed files with 344 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
# ADR-002: Container-Based Bitwarden CLI (No Host Node.js)
## Status
Accepted (supersedes ADR-001 for BW CLI purposes)
## Context
ADR-001 selected a hybrid Node.js version-management strategy because the
Bitwarden CLI (`bw`) is distributed via npm and therefore requires a Node.js
runtime on every consuming host. For TSYS hosts operating under CMMC L3 /
ITAR / STIG alignment, any host-side language runtime is an attack surface
and a compliance finding. The KNEL/TSYS baseline ("Docker for everything")
also forbids host language toolchains.
Bitwarden additionally publishes the CLI as a pre-compiled single binary.
Distributing that binary inside a minimal pinned container gives every host
transparent `bw` access with zero host-side runtimes.
## Decision
**Run the official pre-compiled bw binary inside a pinned Docker container,
exposed to users as a transparent `bw` wrapper at `~/.local/bin/bw`.**
Components (all in this repo):
| File | Role |
|---|---|
| `docker/bw-native/Dockerfile` | `debian:12-slim` + ca-certificates + bw binary, pinned `2026.7.0` |
| `bin/bw-entrypoint.sh` | In-container auth lifecycle: config server, API-key login, unlock, sync, exec |
| `bin/bw-cli.sh` | Host wrapper: docker run with volume-persisted session, tsys- container prefix |
| `bin/bw-install.sh` | One-command installer for the wrapper + image |
Credentials live only in `~/.config/bw/env` (single-quoted values, mode 600)
per the org-wide "no secrets on disk except BW access info" rule. The
Vaultwarden API key is used for login (no TOTP interaction needed); the
master password unlocks the vault; `bw sync` runs after every login so
multi-client vault state stays coherent.
## Known Caveat
The Bitwarden "native" Linux binary is actually a Node.js SEA (Single
Executable Application): it embeds a Node.js runtime and can print Node
errors on crash. Node.js is **absent from every host**, which satisfies the
host-hygiene goal, but the "zero Node.js anywhere" stretch goal is not met.
Alternatives (Rust vaultwarden clients such as `rbw`) were considered and
rejected for now due to Vaultwarden API compatibility gaps. Revisit if a
mature Rust client emerges; the wrapper isolates users from this swap.
## Consequences
### Positive
- Zero language runtimes on hosts (CMMC/ITAR/STIG-friendly host hygiene)
- Identical bw behavior across all TSYS hosts; version pinned in one Dockerfile
- Session persistence via named Docker volume; no state divergence when
wrapper is used consistently
- Vendoring into shell frameworks is one installer invocation
### Negative
- Requires Docker on every consuming host (accepted: it is an org baseline)
- SEA caveat above (Node embedded inside the container image)
## Verification
Deployed and verified on the TSGCOO orchestration host against
`https://pwvault.turnsys.com` (account `coo@turnsys.com`): status, list,
generate, get password/totp/item, create/edit items. Shellcheck clean at
zero warnings including info-level. In production use since 2026-08-13.
---
**Decision Date:** 2026-08-14
**Decision Makers:** VP TechOps (proposed), TSGCOO (implemented)
**Related:** ADR-001 (superseded for BW CLI purposes; MISE guidance remains
valid for projects that genuinely require host Node.js)