Files
keeporg/config/nginx/keeporg.conf.tmpl
T
ReactorcoremeltdownandClaude Opus 5.5 4ffc434b1b Keep server data in an encrypted database, and harden every part
Storage. The server keeps its data in SQLite (a file in the data
directory, the default) or PostgreSQL (database, KEEPORG_DATABASE_URL or
-database). Every row, the KDBX versions included, is sealed with
AES-256-GCM under a key derived from the master key and named by an HMAC.
The master key is required to start and a different one is refused. A data
directory from 0.10 or earlier is imported on first start. Several servers
may share one PostgreSQL database. Go goes to 1.26 for the pure-Go
drivers (modernc.org/sqlite, jackc/pgx).

Hardening, after an audit of the server, both clients and the deployment:
- Single sign-on approves a device only after an explicit Approve in the
  browser that started it, bound by a cookie and PKCE; identities are never
  linked by username, by verified email only when allowed, never to admins.
- Sign-in attempts are limited per address and per account, passwords are
  checked outside the store lock with uniform timing, one-time codes work
  once, and starting two-factor needs the password.
- Merge vaults bound KDF parameters, payload size and XML shape, check
  pushes before storing them, and run at most two merges at once. A version
  is written in the same transaction as its metadata.
- Tokens in URLs only on the event stream, which ends on revocation; CSP,
  frame-ancestors, no-store and HSTS; sign-in CSRF; 404 for databases
  without access; new trusted_proxies.
- Clients open only the server's approval page, keep case-variant and
  retired file names apart, never overwrite a save made during a pull,
  require https except locally, send the stream token in a header, bound
  their reads and escape notifications and terminal output.
- The .deb creates the master key, systemd is sandboxed further, nginx sets
  modern TLS and HSTS, and CI deploys from main only.

Server 0.12.0, desktop 0.6.0, Android 0.4.0 (versionCode 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 15:38:32 +01:00

121 lines
4.3 KiB
Cheetah

### Deployed by https://git.rcmd.space/rcmd/keeporg
upstream keeporg {
server 10.200.1.3:32970;
}
server {
listen 80;
listen [::]:80;
server_name keeporg.tiredsysadmin.cc;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
access_log /var/log/nginx/keeporg.tiredsysadmin.cc_access.log json;
error_log /var/log/nginx/keeporg.tiredsysadmin.cc_error.log;
### SSL cert files ###
ssl_certificate {{ .nginx.variables.blog_ssl_certificate }};
ssl_certificate_key {{ .nginx.variables.blog_ssl_certificate_key }};
# Modern TLS only, whatever the host's defaults are.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# The server refuses databases over max_db_size_mb (32 by default); a
# little headroom over that, and no more, so oversized uploads stop here.
client_max_body_size 64M;
server_name keeporg.tiredsysadmin.cc;
# Shared by every proxied location below. A location that sets any
# proxy_set_header of its own replaces this whole set instead of adding to
# it, which is why the Vouch-protected location repeats them.
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_redirect off;
# disable buffering uploads to prevent OOM on reverse proxy server and make uploads twice as fast (no pause)
proxy_request_buffering off;
# set timeout
proxy_read_timeout 600s;
proxy_send_timeout 600s;
send_timeout 600s;
location = /validate {
proxy_pass http://127.0.0.1:30021/validate;
proxy_set_header Host $http_host;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user;
auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt;
auth_request_set $auth_resp_err $upstream_http_x_vouch_err;
auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount;
}
error_page 401 = @error401;
location @error401 {
return 302 https://auth.tiredsysadmin.cc/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
}
# The web interface, behind Vouch.
location / {
auth_request /validate;
proxy_set_header X-Vouch-User $auth_resp_x_vouch_user;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://keeporg;
}
# Desktop and Android clients, which authenticate to KeepOrg itself with a
# bearer token. Sending them to a Vouch login page would break sync.
location /api {
proxy_pass http://keeporg;
}
# Paths that have to work for somebody who has no Vouch account, or no
# account at all. KeepOrg authenticates each of them itself.
#
# /healthz uptime and container checks, unauthenticated by design
# /invite/ the invited person is signing up; an account is what they
# have come here to create
# /oidc/ the identity provider redirects back with a single-use
# authorization code, which a second login round trip spends
# /device a client device being approved from a browser
location = /healthz {
proxy_pass http://keeporg;
}
location /invite/ {
proxy_pass http://keeporg;
}
location /oidc/ {
proxy_pass http://keeporg;
}
location = /device {
proxy_pass http://keeporg;
}
}