Sign inSign up

tessaridb/oauth

By tessaridb

•Updated about 2 hours ago

OAuth 2.1 and OpenID Connect server with every piece of its state in TessariDB

Image
0

144

tessaridb/oauth repository overview

⁠TessariDB OAuth

Sign-in for your own applications, with every piece of its state in TessariDB.

An OAuth 2.1 authorization server and OpenID provider written in Rust. One binary carries the protocol endpoints, the sign-in pages and account portal your users see, and an admin console on a listener of its own. Companies (tenants), users, clients, codes, grants, sealed signing keys and the audit trail are records in a TessariDB store; nothing of the database is inside this image.

⁠Tags

Tag
0.2.0, latestclient sectors and service credentials for the admin API
0.1.0the first release; linux/amd64 and linux/arm64

The configuration and the HTTP surface may still change in a minor release before 1.0. Pin a version tag.

⁠What it needs

  • A TessariDB node, 0.38.2 or later, reached over the wire protocol with TLS. The server signs in with the account named by TESSARIDB_OAUTH_STORE_USER.
  • Two key files, 32 random bytes each: the key-encryption key that seals signing keys, and the password pepper. The image runs as the user 65532, which must own both files and be the only one able to read them (mode 400 or 600). Neither file is in the store, so neither is in a backup of it.
  • Two addresses: PUBLIC_URL, where users and applications reach the issuers, and ADMIN_URL, the admin console, at another origin.

⁠Ports

PortListener
8443the issuers, sign-in pages and account portal (TESSARIDB_OAUTH_PUBLIC_ADDRESS)
9443the admin console (TESSARIDB_OAUTH_ADMIN_ADDRESS); company acme's console is <ADMIN_URL>/admin/acme/

Without TLS_CERT/TLS_KEY the public listener speaks plain HTTP for a TLS terminator in front of it, which answers at the https PUBLIC_URL. Keep the plain listener reachable from the terminator alone.

⁠Run it

head -c 32 /dev/urandom > kek && head -c 32 /dev/urandom > pepper
chmod 400 kek pepper && sudo chown 65532:65532 kek pepper
docker run -d --name oauth -p 8443:8443 -p 127.0.0.1:9443:9443 \
  -v "$PWD/kek:/keys/kek:ro" -v "$PWD/pepper:/keys/pepper:ro" \
  -e TESSARIDB_OAUTH_KEK=/keys/kek -e TESSARIDB_OAUTH_PEPPER=/keys/pepper \
  -e TESSARIDB_OAUTH_PUBLIC_URL=https://id.example.com -e TESSARIDB_OAUTH_ADMIN_URL=https://admin.example.com \
  -e TESSARIDB_OAUTH_STORE=db.example.com:9080 -e TESSARIDB_OAUTH_STORE_USER=oauth \
  -e TESSARIDB_OAUTH_STORE_PASSWORD -e TESSARIDB_OAUTH_STORE_AUTHORITY=/keys/store-ca.pem \
  -v "$PWD/store-ca.pem:/keys/store-ca.pem:ro" \
  -e TESSARIDB_OAUTH_REGION=eu -e TESSARIDB_OAUTH_TENANTS=acme tessaridb/oauth:0.2.0

⁠With a TessariDB node, in Compose

The node gets a certificate for the name tessaridb (its key owned by 10001, mode 600) and declares the server's account as its first user; the server trusts the node through ca.pem. Put the certificate, its key, the authority and the two key files in ./keys, set STORE_PASSWORD, and run docker compose up -d.

services:
  tessaridb:
    image: tessaridb/tessaridb:0.38.2
    restart: unless-stopped
    volumes:
      - tessaridb-data:/var/lib/tessaridb
      - ./keys/store.pem:/keys/store.pem:ro
      - ./keys/store.key:/keys/store.key:ro
    environment:
      TESSARIDB_INITIAL_USER: oauth
      TESSARIDB_INITIAL_PASSWORD: ${STORE_PASSWORD:?set it}
      TESSARIDB_TLS_CERT: /keys/store.pem
      TESSARIDB_TLS_KEY: /keys/store.key

  oauth:
    image: tessaridb/oauth:0.2.0
    restart: unless-stopped
    depends_on: [tessaridb]
    ports:
      - "8443:8443"             # issuers, sign-in pages, account portal
      - "127.0.0.1:9443:9443"   # admin console, this host only
    volumes:
      - ./keys/kek:/keys/kek:ro
      - ./keys/pepper:/keys/pepper:ro
      - ./keys/ca.pem:/keys/ca.pem:ro
    environment:
      TESSARIDB_OAUTH_PUBLIC_URL: https://id.example.com
      TESSARIDB_OAUTH_ADMIN_URL: http://127.0.0.1:9443
      TESSARIDB_OAUTH_KEK: /keys/kek
      TESSARIDB_OAUTH_PEPPER: /keys/pepper
      TESSARIDB_OAUTH_STORE: tessaridb:9080
      TESSARIDB_OAUTH_STORE_USER: oauth
      TESSARIDB_OAUTH_STORE_PASSWORD: ${STORE_PASSWORD:?set it}
      TESSARIDB_OAUTH_STORE_AUTHORITY: /keys/ca.pem
      TESSARIDB_OAUTH_REGION: eu
      TESSARIDB_OAUTH_TENANTS: acme

volumes:
  tessaridb-data:

Discovery then answers at http://127.0.0.1:8443/acme/.well-known/openid-configuration with the issuer https://id.example.com/acme; in production a TLS terminator serves that URL.

⁠The first operator

A new installation has no operator yet. The emergency path makes one, reading the same environment:

docker exec oauth tessaridb-oauth bootstrap-operator --company acme --email [email protected]

⁠Configuration

The server reads its configuration from the environment when it starts, checks all of it, and refuses to start, naming the variable, when anything is missing or does not fit.

Variable (TESSARIDB_OAUTH_…)MeaningDefault
PUBLIC_URLthe https base URL; tenant acme's issuer is <PUBLIC_URL>/acme; its host is the passkey relying party, so changing the host retires every enrolled passkeyrequired
PUBLIC_ADDRESSwhere the public listener binds0.0.0.0:8443
ADMIN_URLthe admin console's base URL, at an origin other than PUBLIC_URL's: https, or http on 127.0.0.1 or [::1] only; company acme's console is <ADMIN_URL>/admin/acmerequired
ADMIN_ADDRESSwhere the admin console's listener binds0.0.0.0:9443
ADMIN_TLS_CERT, ADMIN_TLS_KEYthe admin console over TLS: its certificate chain and private key, PEM, both or neither. Without them the console is served in the clear, so keep it on a loopback address or behind a TLS terminatorunset (plain)
TLS_CERT, TLS_KEYthe public listener over TLS: its certificate chain and private key, PEM, both or neither. Without them it is served in the clear for a TLS terminator in front of it, which answers at the https PUBLIC_URLunset (plain)
TRUSTED_PROXIESthe proxies in front of the server, IP addresses or address/prefix networks, comma-separated; a request from one of them is counted by the right-most X-Forwarded-For address that is not itself a listed proxy. From any other peer the header is ignorednone
KEKthe 32-byte key-encryption key file, mode 600 or 400; signing keys are stored sealed under itrequired
PEPPERthe password pepper file, at least 32 bytes, mode 600 or 400; passwords are hashed under it and the sign-in counters named by keys derived from itrequired
PASSWORD_MEMORY_KIB, PASSWORD_ITERATIONS, PASSWORD_PARALLELISMthe Argon2id cost of new password hashes; a value below the floor stops the start65536, 3, 1
PASSWORD_CONCURRENCYpassword hashes or checks run at once, each holding the cost's memory4
BREACHED_PASSWORDS_URLwhere a new password is looked up among passwords seen in breaches: an https range-query base ending in /, sent the first five hex digits of the password's SHA-1 and nothing else; off turns the lookup offhttps://api.pwnedpasswords.com/range/
STORE, STORE_USER, STORE_PASSWORDthe TessariDB node's wire address and the account the server signs in with; the node runs TessariDB 0.38.2 or laterrequired
STORE_AUTHORITYthe PEM the node's certificate must lead to; without it only a loopback store is reachednone
STORE_CONNECTIONSconnections to the store at most16
REGIONthe region whose tenants this server holds (namespace oauth_<REGION>)required
TENANTSthe companies (tenants) created and activated at start, comma-separated; a company an operator suspended stays suspendedrequired
PEERSthe other regions, region=host:port comma-separated, each its own TessariDB store; a company one of them serves is sent therenone
PEER_USER, PEER_PASSWORDthe account each other region's store holds for reading its directory; required with PEERSnone
PEER_AUTHORITYthe PEM the other regions' certificates must lead to; without it only loopback stores are reachednone
READ_LIMIT, READ_WINDOW_SECSmetadata, key-set and UserInfo requests one client address may make per window300, 60
TOKEN_LIMIT, TOKEN_WINDOW_SECStoken requests one client address may make per window60, 60
AUTHORIZE_LIMIT, AUTHORIZE_WINDOW_SECSauthorization requests one client address may make per window120, 60
LOGIN_LIMIT, LOGIN_WINDOW_SECSsign-in steps one client address may post per window30, 60
SMTP_URLthe mail relay for password-reset links and sign-in notices: smtps://host[:port] (TLS from the start, port 465), smtp://host[:port] (STARTTLS required, port 587), or smtp://<loopback IP>:port?plain in the clear on a loopback address only; no credentials in the URL. Without it no mail is sent and a password reset answers 503unset (no mail)
SMTP_FROMthe sender, address or Name <address>; required with SMTP_URLnone
SMTP_USER, SMTP_PASSWORD_FILEthe relay account, both or neither; the password file must be mode 600 or 400none
SMTP_AUTHORITYthe PEM the relay's certificate must lead to; without it the system's roots are usednone

⁠Licence

Business Source License 1.1. Production use is free, including use by and within a commercial company. Providing TessariDB OAuth to third parties as a hosted or managed identity, authentication or authorization service, or selling, reselling, renting or sublicensing it, needs a commercial licence from the Licensor ([email protected]⁠). Each version converts to the Apache License 2.0 four years after its release.

Tag summary

Content type

Image

Digest

sha256:9f44ee467…

Size

14 MB

Last updated

about 2 hours ago

docker pull tessaridb/oauth