feat(creds): centralize credentials to ~/.creds/, add KNELCredsManager
Rewire all MCP wrappers, docker-compose services, and validate scripts to source credentials from ~/.creds/ instead of scattered per-service .env files. This removes credential duplication and prepares for the HashiCorp Vault migration. Add KNELCredsManager under tooling-cli with: - Containerized Bitwarden CLI (pinned image, host stays clean) - scripts/bw wrapper with session management and data persistence - README documenting credential layout, consumer wiring, and roadmap Also fixes latent bug in MCP wrappers that were silently getting empty creds from ambient shell env — they now explicitly source ~/.creds/. Tracked in Redmine #407. Discourse: https://community.turnsys.com/t/308 💘 Generated with Crush Assisted-by: Crush
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
FROM node:22-slim
|
||||
|
||||
ARG BW_CLI_VERSION=2026.7.0
|
||||
RUN npm install -g @bitwarden/cli@${BW_CLI_VERSION} && npm cache clean --force
|
||||
|
||||
ENTRYPOINT ["bw"]
|
||||
@@ -0,0 +1,87 @@
|
||||
# KNELCredsManager
|
||||
|
||||
Centralized credential management for KNEL infrastructure. Stores service
|
||||
credentials on disk in `~/.creds/` and provides a containerized Bitwarden CLI
|
||||
for future migration to a password manager.
|
||||
|
||||
## Credential store layout
|
||||
|
||||
All credentials live in `~/.creds/` as flat `.env` files, one per service:
|
||||
|
||||
```
|
||||
~/.creds/
|
||||
├── beszel.env # Beszel monitoring (hub URL set, auth TBD — see #406)
|
||||
├── discourse.env # Discourse forum API keys + admin key
|
||||
├── phpipam.env # phpIPAM app_id + app_code
|
||||
├── redmine.env # Redmine REST API key
|
||||
├── technitium.env # Technitium DNS API key + admin password
|
||||
└── uptime-kuma.env # Uptime Kuma push/API key
|
||||
```
|
||||
|
||||
Permissions: directory `700`, files `600` (owner read/write only).
|
||||
|
||||
### Consumers
|
||||
|
||||
All credential consumers source from `~/.creds/`:
|
||||
|
||||
| Service | Wrapper / consumer | Mechanism |
|
||||
|---|---|---|
|
||||
| Redmine | `~/daytoday/redmine/bin/redmine` | `--env-file ~/.creds/redmine.env` |
|
||||
| Redmine MCP | `mcp-redmine-wrapper.sh` | `set -a; . ~/.creds/redmine.env; set +a` |
|
||||
| Discourse | `~/daytoday/discourse/bin/discourse` | `--env-file ~/.creds/discourse.env` |
|
||||
| Discourse MCP | `mcp-discourse-wrapper.sh` | `set -a; . ~/.creds/discourse.env; set +a` |
|
||||
| Beszel MCP | `mcp-beszel-wrapper.sh` | `set -a; . ~/.creds/beszel.env; set +a` |
|
||||
| Uptime Kuma | (no active consumer yet) | Direct env reference |
|
||||
| Technitium | (no active consumer yet) | Direct env reference |
|
||||
| phpIPAM | (no active consumer yet) | Direct env reference |
|
||||
|
||||
## Bitwarden CLI
|
||||
|
||||
The Bitwarden CLI runs in a pinned Docker container — no host installation
|
||||
required. Built from this directory's Dockerfile.
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
docker build -t reachableceo-bw-cli:2026.7.0 .
|
||||
```
|
||||
|
||||
### Wrapper
|
||||
|
||||
```bash
|
||||
# Symlink the wrapper onto PATH
|
||||
ln -sf ~/projects/KNEL-AIMiddleware/tooling-cli/KNELCredsManager/scripts/bw ~/.local/bin/bw
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
bw login # interactive first-time login
|
||||
export BW_SESSION=$(bw unlock --raw) # unlock and capture session
|
||||
bw sync # sync vault
|
||||
bw list items # list all vault items
|
||||
bw get item <name> # get a specific item
|
||||
bw status # check auth/session status
|
||||
```
|
||||
|
||||
Session state persists in `~/.local/share/bw-cli/` across container runs.
|
||||
|
||||
### MCP integration (machine-to-machine)
|
||||
|
||||
For agent automation without interactive login, the KNEL-AIMiddleware fleet
|
||||
includes `mcp-bitwarden-wrapper.sh` which uses `BITWARDEN_CLIENT_ID` /
|
||||
`BITWARDEN_CLIENT_SECRET` / `BITWARDEN_PASSWORD` env vars (machine account
|
||||
auth). That is a separate integration from this CLI wrapper.
|
||||
|
||||
## Build arguments
|
||||
|
||||
| Arg | Default | Description |
|
||||
|---|---|---|
|
||||
| `BW_CLI_VERSION` | `2026.7.0` | Pinned @bitwarden/cli npm version |
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `BW_CLI_IMAGE` | `reachableceo-bw-cli:2026.7.0` | Override image tag |
|
||||
| `BW_SESSION` | (unset) | Session key from `bw unlock` |
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# bw — Bitwarden CLI wrapper (Docker containerized, host stays clean).
|
||||
#
|
||||
# Usage:
|
||||
# bw login (interactive — prompts for email/master password/2FA)
|
||||
# bw unlock (interactive — prints export BW_SESSION=... line)
|
||||
# bw unlock --raw (prints only the session key, for scripting)
|
||||
# bw list items
|
||||
# bw get item <name-or-id>
|
||||
# bw sync
|
||||
# bw status
|
||||
#
|
||||
# Session management:
|
||||
# After `bw unlock`, capture the session key:
|
||||
# export BW_SESSION=$(bw unlock --raw)
|
||||
# The wrapper passes BW_SESSION through automatically if already set.
|
||||
#
|
||||
# Data persistence:
|
||||
# BW data.json lives at ~/.local/share/bw-cli/ mounted into the container,
|
||||
# so login state persists across invocations.
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE="${BW_CLI_IMAGE:-reachableceo-bw-cli:2026.7.0}"
|
||||
DATA_DIR="${HOME}/.local/share/bw-cli"
|
||||
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# Detect TTY for interactive commands (login, unlock)
|
||||
INTERACTIVE=""
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
INTERACTIVE="-it"
|
||||
fi
|
||||
|
||||
# Pass BW_SESSION through if set
|
||||
SESSION_ARGS=()
|
||||
if [ -n "${BW_SESSION:-}" ]; then
|
||||
SESSION_ARGS+=( -e "BW_SESSION=${BW_SESSION}" )
|
||||
fi
|
||||
|
||||
CONTAINER_NAME="reachableceo-bw-cli-$(date +%s)"
|
||||
|
||||
exec docker run --rm $INTERACTIVE \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-e HOME=/home/bw \
|
||||
-v "${DATA_DIR}:/home/bw/.config" \
|
||||
"${SESSION_ARGS[@]}" \
|
||||
"$IMAGE" "$@"
|
||||
Reference in New Issue
Block a user