Document Docker security, session auth and image verification - #136
Draft
kelsos wants to merge 7 commits into
Draft
Document Docker security, session auth and image verification#136kelsos wants to merge 7 commits into
kelsos wants to merge 7 commits into
Conversation
Adds a session authentication section to the docker page covering ROTKI_SESSION_KEY, single-tab behaviour and the reverse proxy caveats, and points the mobile guide at it. The escape hatch is now ROTKI_ACCEPT_UNAUTHENTICATED_API. The older ROTKI_ACCEPT_DOCKER_RISK is no longer read, so both pages say so: it acknowledged a warning that never mentioned authentication, and carrying it over would leave those operators unaware the option exists.
The page told you to set a session key but never said what the published port actually exposes, so there was no way to judge whether that was enough. It now leads with the surface: a table of every path on the port and what guards it with and without a session key, and a second table of the calls that stay reachable before sign-in, which include account enumeration and ungated account creation. Says plainly what session authentication does not do: no TLS, so the password and cookie cross the network in cleartext; no rate limiting on sign-in; a 7 day session. An authenticating reverse proxy that terminates TLS is the recommendation, with the session key alongside it rather than instead of it, since a proxy you control is what makes reaching rotki over the internet safe. Adds the hardened run recipe, and corrects the volume ownership note: the container drops to uid 10001 and takes ownership of the mounts, it does not need them owned by root.
Released images now carry a keyless provenance attestation and an SBOM. Documents verifying either with the GitHub CLI or cosign, inspecting the SBOM, and states the version floor plus the fact that the SBOM is a partial inventory rather than the full dependency list.
The session cookie is not marked Secure, so a hostname that is also reachable over plain HTTP will still receive it. HSTS stops the browser making that request at all, which is the part a TLS proxy alone does not cover.
Three passages said the session cookie is never marked Secure. That stops being true with ROTKI_SESSION_COOKIE_SECURE, so the env table, the "no TLS" caveat and the HSTS advice all needed updating rather than just an appended section. The new section says why it is off by default (the image speaks plain http, so the flag would break sign-in on loopback or a LAN), what the three values mean, and the two things that surprise people about forwarded mode: a public terminator needs naming with --trusted-proxy or the flag is silently never set, and the default trust set covers the whole private range and can only be extended. HSTS advice reframed rather than dropped. Secure and HSTS close different halves of the same gap: one stops the cookie being sent over plaintext, the other stops the plaintext request happening at all. Also corrects the session lifetime while checking the page against the code: it was described as a flat 7 days, but the idle window is 1 day and 7 days is only the absolute ceiling a rolling session can never be extended past.
The image ships its own HEALTHCHECK and handles SIGTERM, and neither was written down. Operators were left to add a probe that duplicates the built-in one, and a short docker stop timeout kills the backends mid-write. Also fills in the developer side of the same image: docker build needs ROTKI_VERSION or it fails at PyInstaller, and the distroless runtime has no shell, so debugging goes through starling ctl status, docker top, and a busybox image you build yourself.
yabirgb
reviewed
Aug 19, 2026
Comment on lines
+246
to
+255
| docker run -d --name rotki \ | ||
| -p 8084:80 \ | ||
| --cap-drop=ALL \ | ||
| --security-opt=no-new-privileges \ | ||
| --read-only --tmpfs /tmp --tmpfs /run \ | ||
| -v $HOME/.rotki/data:/data \ | ||
| -v $HOME/.rotki/logs:/logs \ | ||
| -e ROTKI_SESSION_KEY="$(openssl rand -hex 32)" \ | ||
| rotki/rotki:latest | ||
| ``` |
Member
There was a problem hiding this comment.
--cap-drop=ALL might not be possible:
The startup of colibri
- recursively changes volume ownership in privsep.rs (line 59);
- clears groups and calls setgid/setuid in privsep.rs (line 152);
- binds port 80 before dropping privileges in main.rs (line 833).
Dropping all capabilities removes CHOWN, SETGID, SETUID, and NET_BIND_SERVICE, which those operations require according to Docker’s capability documentation. Starling treats failures in volume adoption or privilege dropping as fatal.
I get this when running it
INFO starling::privsep: adopting volume ownership path=/data uid=10001 gid=10001
ERROR starling: failed to adopt volume ownership err=Operation not permitted (os error 1)
| | `/api/…` | **open** | session cookie required, except the pre-login calls below | | ||
| | `/colibri/…` | **open** | session cookie required | | ||
| | `/ws` | **open** | handshake refused without a live session | | ||
| | `/mcp/…` | `404`, the MCP service is not running | bearer token tied to the signed-in session | |
Member
There was a problem hiding this comment.
for the mcp it has first to be enabled in the settings so the server starts
|
|
||
| Accessing rotki on mobile when you run Docker on your own can be a bit complicated and depends on the kind of setup you have. You have to make sure that [rotki is never directly accessible from a public network](/requirement-and-installation/docker). | ||
|
|
||
| One way to have rotki accessible on mobile over a public network is by making sure that an [authenticated proxy](#docker-rotki-public) intercepts all traffic directed to rotki. This way you can ensure that no one else can access your rotki instance. |
Member
There was a problem hiding this comment.
codex suggest that this link doesn't exist anymore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft: the Docker half is ready, but
verify.mddocuments attestation that no published image carries yet. See Why this is a draft.Documents the Docker security work that landed in
rotki/rotkiover the last weeks. The Docker page had no account of what the published port actually exposes, and the security advice it did carry ("keep it on a trusted network") predated session authentication existing.What changed
requirement-and-installation/docker.mdgains aSecuritysection, and the page's opening warning is rewritten to point at it:/,/api,/colibri,/ws,/mcp,/healthand/_control, with and withoutROTKI_SESSION_KEY, plus the six calls that stay reachable before sign-in because the login screen needs them. Two of those are worth stating plainly: account names are enumerable, and account creation is not gated.ROTKI_SESSION_KEYturns on and how to set it, including the Compose.envshape.Secure—ROTKI_SESSION_COOKIE_SECURE, its three values, and the two things that surprise people inforwardedmode (below).requirement-and-installation/verify.mdgains a Docker Images section: provenance verification with bothgh attestation verifyandcosign verify-attestation, and how to read the SBOM.usage-guides/advanced/mobile.mdis updated for the same reasons.Corresponding rotki/rotki work
/_controlon the public listenerSecurecookie flag (merged 2026-08-12)Checked against the code, not written from memory
Every factual claim on these pages was verified against
developrather than assumed. The path table, the six pre-login calls (they match the pinned_cookie_less_rulesset exactly), the/healthbody shape, theuid 10001volume adoption, the single-active-session rule and the dev-builds-get-an-SBOM-but-no-attestation split all check out.One claim did not, and is fixed here: the page said a session lasts 7 days. The idle window is 1 day; 7 days is only the absolute ceiling a rolling session can never be extended past. As written it told operators an idle session survives a week when it dies after a day.
The two
forwarded-mode caveats were measured, not reasoned about, by running the image behind traefik with a self-signed certificate: a public-address terminator that is not named with--trusted-proxyhas itsX-Forwarded-Protodiscarded and the cookie is silently never markedSecure, and the default trust set covers the whole private range and can only be extended, so a LAN peer can set the header on its own requests.Why this is a draft
verify.mddocuments provenance attestation for Docker images. The workflow change shipped on 2026-08-06, but the newest tagv1.43.2was cut 2026-06-18, so no published image is attested yet and every command in that section fails today with "no attestation found". The section says v1.44.0 throughout and carries a warning explaining that earlier images predate the feature.So this should not merge before v1.44.0 is out. Two ways forward, happy either way:
verify.mdonto its own PR and merge the Docker security half now, since it has no unreleased dependency left.Note on the base branch
Targets
develop, which now matchesmainexactly. It had been 5 behind, and its one unique commit (bc7d260, "Add Spend/Refund type") was already contained inmain—maincarries bothRefundandBridgeon the Spend and Receive rows, so rebasingdevelopontomaindropped it as redundant. This branch sits directly on that tip, so the diff here is only the three files below.