OAuth 2.1 and OpenID Connect server with every piece of its state in TessariDB
144
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.
| Tag | |
|---|---|
0.2.0, latest | client sectors and service credentials for the admin API |
0.1.0 | the 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.
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.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.PUBLIC_URL, where users and applications reach the issuers, and ADMIN_URL, the admin
console, at another origin.| Port | Listener |
|---|---|
8443 | the issuers, sign-in pages and account portal (TESSARIDB_OAUTH_PUBLIC_ADDRESS) |
9443 | the 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.
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
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.
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]
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_…) | Meaning | Default |
|---|---|---|
PUBLIC_URL | the 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 passkey | required |
PUBLIC_ADDRESS | where the public listener binds | 0.0.0.0:8443 |
ADMIN_URL | the 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/acme | required |
ADMIN_ADDRESS | where the admin console's listener binds | 0.0.0.0:9443 |
ADMIN_TLS_CERT, ADMIN_TLS_KEY | the 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 terminator | unset (plain) |
TLS_CERT, TLS_KEY | the 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_URL | unset (plain) |
TRUSTED_PROXIES | the 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 ignored | none |
KEK | the 32-byte key-encryption key file, mode 600 or 400; signing keys are stored sealed under it | required |
PEPPER | the 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 it | required |
PASSWORD_MEMORY_KIB, PASSWORD_ITERATIONS, PASSWORD_PARALLELISM | the Argon2id cost of new password hashes; a value below the floor stops the start | 65536, 3, 1 |
PASSWORD_CONCURRENCY | password hashes or checks run at once, each holding the cost's memory | 4 |
BREACHED_PASSWORDS_URL | where 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 off | https://api.pwnedpasswords.com/range/ |
STORE, STORE_USER, STORE_PASSWORD | the TessariDB node's wire address and the account the server signs in with; the node runs TessariDB 0.38.2 or later | required |
STORE_AUTHORITY | the PEM the node's certificate must lead to; without it only a loopback store is reached | none |
STORE_CONNECTIONS | connections to the store at most | 16 |
REGION | the region whose tenants this server holds (namespace oauth_<REGION>) | required |
TENANTS | the companies (tenants) created and activated at start, comma-separated; a company an operator suspended stays suspended | required |
PEERS | the other regions, region=host:port comma-separated, each its own TessariDB store; a company one of them serves is sent there | none |
PEER_USER, PEER_PASSWORD | the account each other region's store holds for reading its directory; required with PEERS | none |
PEER_AUTHORITY | the PEM the other regions' certificates must lead to; without it only loopback stores are reached | none |
READ_LIMIT, READ_WINDOW_SECS | metadata, key-set and UserInfo requests one client address may make per window | 300, 60 |
TOKEN_LIMIT, TOKEN_WINDOW_SECS | token requests one client address may make per window | 60, 60 |
AUTHORIZE_LIMIT, AUTHORIZE_WINDOW_SECS | authorization requests one client address may make per window | 120, 60 |
LOGIN_LIMIT, LOGIN_WINDOW_SECS | sign-in steps one client address may post per window | 30, 60 |
SMTP_URL | the 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 503 | unset (no mail) |
SMTP_FROM | the sender, address or Name <address>; required with SMTP_URL | none |
SMTP_USER, SMTP_PASSWORD_FILE | the relay account, both or neither; the password file must be mode 600 or 400 | none |
SMTP_AUTHORITY | the PEM the relay's certificate must lead to; without it the system's roots are used | none |
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.
Content type
Image
Digest
sha256:9f44ee467…
Size
14 MB
Last updated
about 2 hours ago
docker pull tessaridb/oauth