Security
kimbap runs with real privilege on your host — it can install system packages, manage a reverse proxy on ports 80/443, and start/stop arbitrary Docker containers. This page covers the security-relevant decisions worth knowing about.
Secrets
- Passwords are hashed with argon2id (the current OWASP-recommended default), stored as a self-describing PHC string so cost parameters can change later without a migration.
- Session tokens and API keys are 256-bit random values; only their SHA-256 hash is ever persisted. The raw value is shown exactly once, at creation. SHA-256 (not a slow hash) is correct here — these are high-entropy random tokens, not low-entropy user input.
- API keys are prefixed
kbp_so they're recognizable at a glance (and by secret-scanning tools) if one ever leaks somewhere it shouldn't. - Registry credentials are the one secret kimbap must be able to recover
in plaintext (to re-run
docker login), so they're encrypted at rest with AES-256-GCM instead of hashed — see Registries for the key management story.
Sessions
HttpOnly,SameSite=Laxcookie.Secureis set once kimbap detects it's being reached over HTTPS (directly, or viaX-Forwarded-Proto: httpsfrom Traefik) — it's not set on the plain-HTTP bootstrap port, since there's no TLS there to protect the cookie with in the first place.- Sliding expiry (~30 days idle) with a server-side row per session, so logging out (or an admin disabling a user) actually revokes access — unlike a bare JWT, which would need its own revocation list to do the same thing.
Rate limiting
Both login and API-key validation are rate-limited per source IP (10 failures/minute → 5-minute lockout for logins; 20/minute for API keys) to blunt brute-force attempts, independent of password/key strength.
OAuth self-registration
kimbap is its own OAuth 2.1 Authorization Server for the MCP endpoint (see MCP), so clients like claude.ai's remote connector can register and authorize themselves with no pre-shared secret. The security-relevant decisions there:
- Every registered client is a public client — no
client_secretis ever issued or trusted. PKCE (S256) is mandatory on every authorization request; "plain" or missing PKCE is rejected outright. - Consent is shown every time, for every authorization request, to a
logged-in kimbap user — there's no silent/automatic re-approval for a
previously-seen
client_id. Approving one grants a new API key with the approving user's role, exactly as if they'd created it by hand. redirect_uriis validated against the client's registered list before any error is ever redirected there — an unvalidatedredirect_uriis never used as a redirect target, closing the standard open-redirect risk in this kind of flow.- Authorization codes are single-use and short-lived (10 minutes), and are
invalidated on first use at
/oauth/tokenregardless of whether that exchange succeeds or fails. - The resulting access token is, underneath, an ordinary kimbap API key — see Secrets above for how those are stored and revoked.
Roles
Two roles: admin (users, Docker/Traefik system actions, every project) and
member (project CRUD/lifecycle only). API keys and MCP tool calls inherit
the role of the user that issued them — there's no standalone "root key"
disconnected from a real account.
Confirmation gating
Host-level or destructive actions — installing/updating Docker, provisioning
Traefik, deleting a project, adding/removing a registry credential — require
an explicit confirm: true in the
request body (REST) or tool arguments (MCP), and (for the system-level ones)
the admin role. This matters most for MCP: an LLM acting on a
loosely-worded instruction shouldn't be able to trigger a host-level change
without a clear, deliberate confirmation in the actual tool call.
Audit log
Every mutating action — through the UI, REST API, or MCP — is recorded in
audit_log with the actor, action, target, and a detail blob (e.g. command
output for lifecycle actions). GET /api/audit (admin only) or the
list_audit_log MCP tool surface it.
Systemd hardening
The installed unit runs kimbap under a dedicated system user with:
AmbientCapabilities=CAP_NET_BIND_SERVICEonly (no full root, even though it may occasionally bind a low port directly).ProtectSystem=strict,ProtectHome=true, a narrowReadWritePathsscoped to the state directory.NoNewPrivileges=true, kernel/namespace/SUID restrictions, and aSystemCallFilter=@system-serviceallowlist.
See kimbap install's rendered unit (systemctl cat kimbap on an installed
host) for the complete set.
Docker socket access
Once Docker is installed, kimbap's system user is added to the docker
group so it can talk to the Docker socket. This is a well-known
privilege-equivalent-to-root tradeoff inherent to any tool that manages
Docker containers on a host (not specific to kimbap) — anyone who can reach
the Docker socket can, in the general case, escape to root on the host.
Treat kimbap's admin account, and any admin-scoped API/MCP key, with the same
care you'd give root/sudo access.
Reporting a vulnerability
Contact the maintainer directly, especially for anything sensitive enough to want a private channel first.