Repository navigation
feat(auth): skip the password for requests a trusted proxy vouches for - #238
Merged
Merged
Conversation
kahme247
force-pushed
the
main
branch
2 times, most recently
from
October 8, 2026 13:30
3f1dbea to
42b3c29
Compare
andrebrait
force-pushed
the
feat/skip-auth-ranges
branch
from
October 8, 2026 17:26
042572f to
3c2252b
Compare
OMP_WEB_TRUSTED_HEADER_SHA256 holds the SHA-256 digest of a secret a reverse proxy sends in X-Omp-Web-Auth for trusted client ranges; a matching request counts as signed in. omp-web never holds the secret, and the digest is stripped from child processes like the other omp-web secrets. Service installers and the Windows launchers pass it on. The Windows tray (native and PowerShell) and background service now read OMP_WEB_PASSWORD_HASH, the legacy OMP_WEB_PASSWORD and the digest fresh from the user, then system, environment on every server start, so rotating or removing them with setx takes effect at the next restart. bin/omp-web-tray.exe is rebuilt from native/tray. README documents the nginx setup (realip for Cloudflare, Cloudflare Tunnel and other proxies, DNS-rebinding guard) and generating the secret without it reaching shell history.
andrebrait
force-pushed
the
feat/skip-auth-ranges
branch
from
October 9, 2026 23:01
69a35c3 to
a5a14ef
Compare
… one The digest is a fast unsalted SHA-256, safe only for a long random secret.
andrebrait
added a commit
to andrebrait/ompweb
that referenced
this pull request
Oct 10, 2026
… proxy vouches for Squashed from origin/feat/skip-auth-ranges (40d324f) onto upstream/main.
andrebrait
added a commit
to andrebrait/ompweb
that referenced
this pull request
Oct 10, 2026
… proxy vouches for Squashed from origin/feat/skip-auth-ranges (40d324f) onto upstream/main.
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.
Adds
OMP_WEB_TRUSTED_HEADER_SHA256. The reverse proxy sends a secret inX-Omp-Web-Auth, and omp-web holds only the secret's SHA-256 digest: a request whose header hashes to that digest counts as signed in. The reverse proxy decides which clients get the header, typically by address range, so trusted networks can skip the password while everyone else still signs in.Why the proxy decides
omp-web has no client address it can trust. The password check runs in
proxy.ts, which cannot see the socket address. Next fillsX-Forwarded-Forfrom the socket only when the client did not send one (??=inbase-server.js), so any client could claim a trusted address. Behind a reverse proxy, every connection also comes from127.0.0.1. nginx does see the real client address, including behind Cloudflare and other proxies through itsrealipmodule.Changes
proxy.ts/lib/web-auth.ts: hash the header with SHA-256 and compare it to the configured digest in constant time. The secret is random, so a fast hash suffices, and omp-web never holds the secret itself. An unset, empty or malformed digest (anything other than 64 hex characters) disables the feature, and an empty header never matches. The header is ignored when noOMP_WEB_PASSWORD_HASHis configured. The password flow and the non-loopback listen guard are unchanged.lib/project-command-env.ts:OMP_WEB_TRUSTED_HEADER_SHA256replaces the placeholderOMP_WEB_TRUSTED_HEADER_SECRETin the list of secrets stripped from every child process.OMP_WEB_PASSWORD_HASH, the legacyOMP_WEB_PASSWORD(passed through only so the server refuses it with instructions) and the digest from the user, then system, environment on every server start, and remove them from the child when neither scope sets them. Previously they preferred the copy captured at logon, so a hash rotated or removed withsetxkept working until the next Windows sign-in, and the native tray did not pass the hash on at all.bin/omp-web-tray.exeis rebuilt fromnative/tray(dotnet publish -c Release -r win-x64 --self-contained false).geoand thatmap. It always sets the header, using an empty value for untrusted clients, so a client cannot pass its own value through. It coversrealipfor Cloudflare (CF-Connecting-IPtrusted only from Cloudflare's ranges, with a$realip_remote_addrallowlist as hardening), Cloudflare Tunnel, and other proxies (X-Forwarded-Forwithreal_ip_recursive). It also warns about DNS rebinding (explicitserver_name, and adefault_serverthat returns 444) and about local processes on the same host. The Japanese and Chinese READMEs get the table row. DESIGN.md's security contract and the CHANGELOG are updated.Rebased onto
mainafter #239 (password hashes), squashed into one commit.Verification
lib/web-auth.test.mjscovers a matching secret on a page and an API route, the/loginredirect, wrong, near-miss (extra or missing character, different case), empty and missing headers, the digest itself sent as the header, an uppercase digest, and an unset, empty or malformed digest, all with a hashed password configured. The systemd installer test asserts the passthrough, and the child-environment test asserts the digest is stripped. The full suite passes: 1343 pass, 0 fail, 5 skipped.tsc --noEmitpasses, and eslint reports no errors on the changed files.Live smoke test through a real nginx using the README's directives, with
realiponCF-Connecting-IP:10.0.0.5200, and/loginredirects to/203.0.113.9401401(nginx overwrites the header)$realip_remote_addrallowlist403401401The README's generation snippet was run as written (with a temporary path in place of
/etc/nginxand withoutsudo): it wrote a0640include file, the printed digest matched the secret in that file, and the shell history file did not contain the secret.Live smoke on the rebased commit with
OMP_WEB_PASSWORD_HASHfromompweb hash-password: trusted header200(and/login→/), wrong header, digest as header and no header401, password sign-in200.Not run: the PowerShell scripts and the rebuilt exe, which need Windows. The launchd passthrough has no test, because there is no launchd test file.