Skip to content

Document Docker security, session auth and image verification - #136

Draft
kelsos wants to merge 7 commits into
rotki:developfrom
kelsos:docs/docker-session-authentication
Draft

Document Docker security, session auth and image verification#136
kelsos wants to merge 7 commits into
rotki:developfrom
kelsos:docs/docker-session-authentication

Conversation

@kelsos

@kelsos kelsos commented Aug 12, 2026

Copy link
Copy Markdown
Member

Draft: the Docker half is ready, but verify.md documents attestation that no published image carries yet. See Why this is a draft.

Documents the Docker security work that landed in rotki/rotki over 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.md gains a Security section, and the page's opening warning is rewritten to point at it:

  • What the published port exposes — a path-by-path table of /, /api, /colibri, /ws, /mcp, /health and /_control, with and without ROTKI_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.
  • Session authentication — what ROTKI_SESSION_KEY turns on and how to set it, including the Compose .env shape.
  • What it does not do — no TLS, no brute-force protection, the pre-login calls stay open, and the real session lifetime.
  • Use an authenticating reverse proxy — why this stays the recommendation even with a session key, and what a proxy fixes that the app cannot.
  • Marking the cookie SecureROTKI_SESSION_COOKIE_SECURE, its three values, and the two things that surprise people in forwarded mode (below).

requirement-and-installation/verify.md gains a Docker Images section: provenance verification with both gh attestation verify and cosign verify-attestation, and how to read the SBOM.

usage-guides/advanced/mobile.md is updated for the same reasons.

Corresponding rotki/rotki work

Checked against the code, not written from memory

Every factual claim on these pages was verified against develop rather than assumed. The path table, the six pre-login calls (they match the pinned _cookie_less_rules set exactly), the /health body shape, the uid 10001 volume 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-proxy has its X-Forwarded-Proto discarded and the cookie is silently never marked Secure, 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.md documents provenance attestation for Docker images. The workflow change shipped on 2026-08-06, but the newest tag v1.43.2 was 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:

  1. Hold the whole PR until v1.44.0 ships, then verify the commands against a real image and mark ready.
  2. Split verify.md onto 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 matches main exactly. It had been 5 behind, and its one unique commit (bc7d260, "Add Spend/Refund type") was already contained in mainmain carries both Refund and Bridge on the Spend and Receive rows, so rebasing develop onto main dropped it as redundant. This branch sits directly on that tip, so the diff here is only the three files below.

kelsos added 5 commits August 11, 2026 16:33
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.
@kelsos
kelsos changed the base branch from main to develop August 12, 2026 15:08
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.
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
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--cap-drop=ALL might not be possible:

The startup of colibri

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 |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex suggest that this link doesn't exist anymore

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.

2 participants