7.0 KiB
Phase 5 Runbook (Session Reuse Prototype)
This runbook starts a minimal k_server + k_proxy prototype for session reuse testing.
Last updated: 2026-04-25
What This Prototype Covers
k_proxycreates short-lived sessions.- Session creation uses a card-presence check (
fido2_probe.py --json) as the current auth gate. - Valid sessions can repeatedly access a protected
k_servercounter endpoint without re-running card auth each request. - Session status and logout/invalidation paths are implemented.
Modes
There are two useful ways to run this prototype:
- Same-VM quickstart:
k_proxyandk_serverrun on one VM for app-local testing. - Split-VM chain:
k_proxyruns ink_proxy,k_serverruns ink_server, and the Qubes forwarding layer must permit the chain.
Start Services
Same-VM quickstart
This matches the code defaults and is useful for basic app behavior only.
In the chosen VM:
python3 /home/user/chromecard/k_server_app.py --host 127.0.0.1 --port 8780 --proxy-token dev-proxy-token
In the same VM:
python3 /home/user/chromecard/k_proxy_app.py \
--host 127.0.0.1 \
--port 8770 \
--session-ttl 300 \
--server-base-url http://127.0.0.1:8780 \
--proxy-token dev-proxy-token
Split-VM chain
This is the current Qubes target shape.
In k_server VM:
python3 /home/user/chromecard/k_server_app.py \
--host 127.0.0.1 \
--port 8780 \
--proxy-token dev-proxy-token \
--tls-certfile /home/user/chromecard/tls/phase2/k_server.crt \
--tls-keyfile /home/user/chromecard/tls/phase2/k_server.key
In k_proxy VM:
qvm-connect-tcp 9780:k_server:8780
Notes:
python3 /home/user/chromecard/k_proxy_app.py \
--host 127.0.0.1 \
--port 8771 \
--session-ttl 300 \
--server-base-url https://127.0.0.1:9780 \
--server-ca-file /home/user/chromecard/tls/phase2/ca.crt \
--proxy-token dev-proxy-token \
--tls-certfile /home/user/chromecard/tls/phase2/k_proxy.crt \
--tls-keyfile /home/user/chromecard/tls/phase2/k_proxy.key
In k_client VM:
qvm-connect-tcp 9771:k_proxy:8771
Notes:
- Current validated split-VM path is
k_client localhost:9771 -> k_proxy localhost:8771 -> k_proxy localhost:9780 forward -> k_server localhost:8780. - Use
--cacert /home/user/chromecard/tls/phase2/ca.crtfor TLS verification incurl-based checks. - Raw VM-IP routing is not the validated path for the current prototype.
Ownership And Concurrency
k_proxyis authoritative for session state.k_serveris authoritative for the protected counter state.- Sessions are in-memory only in
k_proxyand are lost on proxy restart. - The protected counter is in-memory only in
k_serverand resets on server restart. - Both services use
ThreadingHTTPServer. k_proxyguards its session store with a single process-local lock.k_serverguards counter increments with a single process-local lock.- Qubes localhost forwarders are transport plumbing only; they are not a source of state authority.
Test Flow
Use the proxy port that matches the mode you started:
- Same-VM quickstart:
8770 - Split-VM chain:
9771fromk_client,8771insidek_proxy
Create a session (runs auth gate once):
curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/session/login \
-H 'Content-Type: application/json' \
-d '{"username":"alice"}'
Copy session_token from response, then:
TOKEN='<paste-token>'
Check session:
curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/session/status \
-H "Authorization: Bearer $TOKEN"
Call protected resource multiple times (should not require new login):
curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/resource/counter \
-H "Authorization: Bearer $TOKEN"
curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/resource/counter \
-H "Authorization: Bearer $TOKEN"
Logout/invalidate:
curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/session/logout \
-H "Authorization: Bearer $TOKEN"
Re-check after logout (should fail with 401):
curl -i --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:<proxy-port>/resource/counter \
-H "Authorization: Bearer $TOKEN"
Regression Script
For the split-VM chain, use the host-side regression helper:
/home/user/chromecard/phase5_chain_regression.sh
Defaults:
- Drives the test from
k_clientover SSH. - Uses
https://127.0.0.1:9771and/home/user/chromecard/tls/phase2/ca.crtinsidek_client. - Logs in as
alice. - Runs
20counter requests at parallelism8. - Verifies that returned counter values are unique and gap-free, then logs out and checks for
401after logout.
Useful overrides:
REQUESTS=50 PARALLELISM=12 /home/user/chromecard/phase5_chain_regression.sh
/home/user/chromecard/phase5_chain_regression.sh --username alice --client-host k_client
Verified result on 2026-04-25:
- Live split-VM chain passed end-to-end.
- Login, session status, counter reuse, and logout all worked from
k_client. - A
20request /8worker concurrency burst returned unique, gap-free counter values23..42.
Current Limitation
- This uses card-presence probing, not a full WebAuthn assertion verification path.
- Intended as a Phase 5 starter for session semantics and proxy/server behavior.
- Session and counter state are currently process-local only; restart loses state.
- Upstream trust still relies on a shared static
X-Proxy-Token. - Experimental direct FIDO2 mode now exists in
k_proxy_app.pybehind--auth-mode fido2-direct, but it is not the default runtime:- direct registration on the current
k_proxycard/library stack still fails withNo compatible PIN/UV protocols supported! - a CTAP1 fallback probe did not complete quickly enough to promote as the working path
- the deployed service was restored to default
probemode so the validated Phase 5 chain remains usable
- direct registration on the current
- Raw CTAP debugging helper now exists at
/home/user/chromecard/raw_ctap_probe.py:- use it on
k_proxyto exercise low-levelmakeCredential/getAssertion - it logs keepalive callbacks and transport exceptions
- use it on
- Current blocker before the next direct-auth attempt:
k_proxycurrently has no visible/dev/hidraw*python3 /home/user/chromecard/fido2_probe.py --listink_proxyreturnsNo CTAP HID devices found.- restore card visibility first, then retry the raw CTAP probe and stop to tell the user when to press
yesorno
- Latest retry after card reattach:
/dev/hidraw0and/dev/hidraw1are visible ink_proxyagain/dev/hidraw0opens successfully as the normal user, but/dev/hidraw1is still permission-denied- raw
makeCredentialstill shows no card prompt, so the hang is before the firmware confirmation UI - next step is to identify which hidraw interface
python-fido2is selecting