mildly-basic-auth 0.1.0

Basic auth with nicer UX.
Documentation

mildly-basic-auth

Crates.io License GitHub Tag crates.io GitHub Actions Workflow Status

Basic auth with nicer UX.

A transparent reverse proxy that shows a password page, sets a secure session cookie, and otherwise gets out of the way.

What it is

mildly-basic-auth puts a password page in front of anything that speaks HTTP. Enter the password once; it sets a session cookie and turns into a transparent passthrough — as if it were never there.

if not authenticated:
    show_password_gate()
else:
    passthrough_transparently()  # as if we're not even there...

It fills the awkward middle ground: HTTP Basic auth is too ugly (a native browser dialog that never remembers you), and a full OAuth2 proxy is far too much (Google sign-in, callback URLs, client secrets) just to keep strangers out of a work-in-progress project or some personal docs.

Philosophy: stupid simple

One environment variable for the password, one for the upstream. No config file to learn, no docs to read, no accounts, no database, no Redis. Drop the container in front of your app and it works.

Everything else follows from that:

  • The login page is a single self-contained HTML file (inline CSS and SVG, no external requests — the wall never phones home).
  • Sessions are stateless. The cookie is a digest of the password, so there is nothing to persist, and rotating the password invalidates every session for free.
  • Authenticated traffic passes through untouched, streaming and WebSockets included.

Cookbook

Images are published to Docker Hub as qrichert/mildly-basic-auth.

Drop-in

The whole thing is two environment variables. Point MBA_UPSTREAM at the service you want to protect and publish the gate's port instead of the app's:

services:
  auth-gate:
    image: qrichert/mildly-basic-auth:latest
    ports:
      - "80:8000"
    environment:
      MBA_PASSWORD: "Tr0ub4dor&3"
      MBA_UPSTREAM: http://app:2001
  app:
    image: traefik/whoami
    command:
      - "--port=2001"

Use a long, random MBA_PASSWORD, not something guessable like the Tr0ub4dor&3 above. The session cookie is a fast digest of the password, so a leaked cookie is an offline verifier of it — a strong secret stays safe, a weak one does not.

Behind Caddy (TLS)

The drop-in above serves plain HTTP, so the session cookie is not Secure and both the password and the token are visible on the wire. That is fine on a trusted network, but for anything public, terminate TLS in front. Caddy sets X-Forwarded-Proto: https, which flips the cookie's Secure flag on automatically:

services:
  caddy:
    image: caddy:2
    ports:
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
  auth-gate:
    image: qrichert/mildly-basic-auth:latest
    environment:
      MBA_PASSWORD: ${MBA_PASSWORD:?set a strong password}
      MBA_UPSTREAM: http://app:2001
  app:
    image: traefik/whoami
    command:
      - "--port=2001"
# Caddyfile
docs.example.com {
    reverse_proxy auth-gate:8000
}

Without Docker

It is a single static binary. Install it from crates.io and hand it the same two variables (it binds 0.0.0.0:8000):

$ cargo install mildly-basic-auth
$ MBA_PASSWORD='…' MBA_UPSTREAM='http://127.0.0.1:2001' mildly-basic-auth

Configuration

Variable Required Description
MBA_PASSWORD yes The password. Startup fails if unset or empty.
MBA_UPSTREAM yes Absolute http(s)://host[:port] to forward to.

A missing or empty variable is a hard startup error, not a silent passthrough — the point is protection, so a misconfiguration fails loud instead of leaving the door open.

The container listens on 0.0.0.0:8000 and runs as a non-root user (UID 10001) on a Debian-slim image.[^debian]

[^debian]: Debian slim, not Alpine: musl's allocator degrades under the per-request, multithreaded allocation a proxy does, and the pure-Rust TLS stack (rustls + blake3, no OpenSSL) means Alpine's usual glibc/OpenSSL payoff does not apply here.

Roadmap

v0 is plain-password-in-an-env-var with a fixed template. Planned next:

  • More auth methods: hashed password (<algo>:<hash>), multiple user/password pairs, and possibly a header-only bearer-token check.
  • Config beyond env vars: an env file or a YAML config file (via MBA_CONFIG_FILE or discovery).
  • Custom template: full override via MBA_TEMPLATE_FILE or a bind mount to /etc/template.html, loaded at startup, with a template engine to interpolate variables and conditionally render fields per auth method.
  • Page customization without a custom template: language, title, placeholder — enough to translate the page.
  • More settings: auth method, bind host/port, logging on/off, session lifetime.
  • Authentication hardening: optional failed-login throttling, once trusted client-IP handling is configurable.

Non-goals

General traffic controls such as rate limiting and host allow-listing belong at the edge, not here. Also deliberately out of scope, permanently: LDAP, OAuth, an admin panel, policy rules, Redis, MFA/TOTP, and SSO.