Episode 4: Vaultwarden single sign-on via Forgejo
- compose: SSO_ENABLED, SSO_AUTHORITY and the two client credentials, with the undocumented redirect path written down where people will look for it - .env.example: was referenced by the README since episode 1 but never actually committed, so a fresh clone could not follow the quickstart - README: the four steps, plus the debugging notes that cost the most time (no SSO line at startup, 404 on routes without query parameters, HEAD on the discovery endpoint, and the three-step member join) - HARDENING: never copy db.sqlite3 alone while the container runs. WAL mode keeps recent writes in a side file, so that backup silently misses data
This commit is contained in:
parent
0341ae777d
commit
d5398caece
4 changed files with 130 additions and 1 deletions
48
.env.example
Normal file
48
.env.example
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# ~$ selfhostedops — copy me: cp .env.example .env
|
||||
# Generate secrets with: openssl rand -hex 32
|
||||
|
||||
# Your base domain. Services live at git.* wiki.* vault.* status.* n8n.* vpn.*
|
||||
# DNS: one wildcard A (and AAAA) record *.ops.example.com -> your server IP
|
||||
DOMAIN=ops.example.com
|
||||
|
||||
# Let's Encrypt notifications + expiry mails
|
||||
ACME_EMAIL=you@example.com
|
||||
|
||||
TZ=Europe/Berlin
|
||||
|
||||
# --- Outline ---
|
||||
OUTLINE_PG_PASSWORD=CHANGE_ME_openssl_rand_hex_32
|
||||
OUTLINE_SECRET_KEY=CHANGE_ME_openssl_rand_hex_32
|
||||
OUTLINE_UTILS_SECRET=CHANGE_ME_openssl_rand_hex_32
|
||||
# From Forgejo: Site Administration -> Applications -> OAuth2
|
||||
# Redirect URI: https://wiki.<DOMAIN>/auth/oidc.callback
|
||||
OUTLINE_OIDC_CLIENT_ID=
|
||||
OUTLINE_OIDC_CLIENT_SECRET=
|
||||
|
||||
# --- Vaultwarden ---
|
||||
# Admin panel token (argon2 hash recommended, plain string works to start)
|
||||
VAULTWARDEN_ADMIN_TOKEN=CHANGE_ME_openssl_rand_hex_32
|
||||
# Flip to false once your team has signed up:
|
||||
VAULTWARDEN_SIGNUPS_ALLOWED=true
|
||||
# Single sign-on against your own Forgejo (episode 4).
|
||||
# From Forgejo: Site Administration -> Applications -> OAuth2 Application
|
||||
# Redirect URI must be EXACTLY: https://vault.<DOMAIN>/identity/connect/oidc-signin
|
||||
VAULTWARDEN_SSO_CLIENT_ID=
|
||||
VAULTWARDEN_SSO_CLIENT_SECRET=
|
||||
|
||||
# --- wg-easy (optional, --profile vpn) ---
|
||||
# Generate with: docker run --rm ghcr.io/wg-easy/wg-easy wgpw 'yourpassword'
|
||||
WGEASY_PASSWORD_HASH=
|
||||
|
||||
# --- CI (episode 2, optional: docker compose --profile ci up -d) ---
|
||||
# One shared secret registers the runner declaratively. Generate it with:
|
||||
# openssl rand -hex 20
|
||||
# Then derive the UUID Forgejo will know it by:
|
||||
# python3 -c 'import uuid,sys;print(uuid.UUID(bytes=sys.argv[1][:16].encode()))' <secret>
|
||||
# And register it on the Forgejo side:
|
||||
# docker compose exec -u git forgejo forgejo forgejo-cli actions register \
|
||||
# --name box-runner --labels docker --secret <secret>
|
||||
# A registration token copied from the web UI does NOT work here.
|
||||
RUNNER_SECRET=
|
||||
RUNNER_UUID=
|
||||
|
||||
16
HARDENING.md
16
HARDENING.md
|
|
@ -58,6 +58,20 @@ apt install -y fail2ban # default ssh jail is enough to start
|
|||
|
||||
- `VAULTWARDEN_SIGNUPS_ALLOWED=false` the moment your team is on board.
|
||||
- Use an argon2 `ADMIN_TOKEN` (`vaultwarden hash` / see wiki), not a plain string.
|
||||
A plain one makes Vaultwarden print a NOTICE on every start. It is telling
|
||||
the truth.
|
||||
- **Never back up `db.sqlite3` on its own while the container runs.** The
|
||||
database is in WAL mode: recent writes live in `db.sqlite3-wal` and are not
|
||||
in the main file yet. Copy it alone and you get a backup that silently
|
||||
misses your newest users and entries, and you find out on restore day.
|
||||
Either stop the container first (see section 7), or take a consistent copy:
|
||||
|
||||
```bash
|
||||
docker compose exec vaultwarden sh -c "sqlite3 /data/db.sqlite3 \".backup '/data/backup.sqlite3'\""
|
||||
```
|
||||
|
||||
- Restore the backup somewhere else, once, before you trust it. A backup you
|
||||
have never restored is a belief, not a backup.
|
||||
- The admin panel (`/admin`) is for setup — consider blocking it in Caddy afterwards:
|
||||
|
||||
```
|
||||
|
|
@ -89,7 +103,7 @@ Plus: enable your provider's server snapshots. Two clicks, saves your company.
|
|||
|
||||
## 8. What this list is NOT
|
||||
|
||||
Not covered here, on purpose (own episodes): SSO everywhere, 2FA enforcement
|
||||
Not covered here, on purpose (own episodes): 2FA enforcement
|
||||
per service, Netbird zero-trust mesh, central logging, CrowdSec, offsite borg.
|
||||
Baseline first. Perfect later.
|
||||
|
||||
|
|
|
|||
49
README.md
49
README.md
|
|
@ -140,6 +140,55 @@ command (`examples/deploy-hello-ops.sh`). Read the CI section of
|
|||
[HARDENING.md](HARDENING.md) before you wire the deploy step — there are two
|
||||
comfortable ways to hand your whole server to anyone who can push a branch.
|
||||
|
||||
## One login for the vault (Episode 4)
|
||||
|
||||
Your Forgejo already knows your team. Let it log them into the password
|
||||
manager too, so offboarding is one switch instead of a checklist.
|
||||
|
||||
```bash
|
||||
# 1. Create the OAuth2 app in Forgejo
|
||||
# Site Administration -> Applications -> OAuth2 Application
|
||||
# Name: Vaultwarden
|
||||
# Redirect URI: https://vault.<DOMAIN>/identity/connect/oidc-signin
|
||||
# Confidential client: yes
|
||||
#
|
||||
# That redirect path is not documented anywhere in the UI, and a wrong one
|
||||
# only fails AFTER the Forgejo login. Copy it character for character.
|
||||
|
||||
# 2. Put the client id and secret in .env
|
||||
VAULTWARDEN_SSO_CLIENT_ID=...
|
||||
VAULTWARDEN_SSO_CLIENT_SECRET=...
|
||||
|
||||
# 3. Restart just that container
|
||||
docker compose up -d vaultwarden
|
||||
|
||||
# 4. Log in: https://vault.<DOMAIN>
|
||||
# Type your work mail, hit "Use single sign-on", authorize once in Forgejo.
|
||||
```
|
||||
|
||||
Vaultwarden will then ask you to set a master password. That is not a bug.
|
||||
Forgejo proved *who you are*; nothing so far produced the key that *decrypts
|
||||
your vault*. Your master password never leaves your browser, and the server
|
||||
stores blobs it cannot read. Two different jobs, two different mechanisms.
|
||||
|
||||
Debugging notes, all of them learned the hard way:
|
||||
|
||||
- Vaultwarden logs nothing about SSO at startup. It builds the OIDC client on
|
||||
the first login attempt, not before. Watch `docker compose logs -f
|
||||
vaultwarden` *while* you log in instead: you will see a `prevalidate` call
|
||||
and then the `oidc-signin` callback coming back with a code.
|
||||
- `/identity/connect/authorize` and the callback both answer `404` when called
|
||||
without query parameters. That is route matching, not a wrong path.
|
||||
- `curl -I` on Forgejo's `/.well-known/openid-configuration` returns 404 because
|
||||
there is no HEAD route. Use `curl -s` and it is a normal 200.
|
||||
- Turn on `SSO_SIGNUPS_MATCH_EMAIL` **before** anyone logs in, or you get
|
||||
duplicate accounts instead of linked ones.
|
||||
|
||||
Then make it a team vault: create an organization, add collections, invite
|
||||
people. Note that joining is three steps, not two: you invite, they log in,
|
||||
and then an owner has to **confirm** them. Until that confirmation, a member
|
||||
sees no shared items at all.
|
||||
|
||||
## Hardening
|
||||
|
||||
Before you put real company data on this box, read **[HARDENING.md](HARDENING.md)**.
|
||||
|
|
|
|||
18
compose.yml
18
compose.yml
|
|
@ -126,6 +126,24 @@ services:
|
|||
DOMAIN: https://vault.${DOMAIN}
|
||||
ADMIN_TOKEN: ${VAULTWARDEN_ADMIN_TOKEN}
|
||||
SIGNUPS_ALLOWED: "${VAULTWARDEN_SIGNUPS_ALLOWED:-true}" # set to false after your team joined!
|
||||
# --- Single sign-on via your own Forgejo (episode 4) ---
|
||||
# Create the OAuth2 app in Forgejo FIRST: Site Administration ->
|
||||
# Applications -> OAuth2. Its redirect URI must be exactly:
|
||||
# https://vault.${DOMAIN}/identity/connect/oidc-signin
|
||||
# That path is not documented in the UI. Getting it wrong fails only
|
||||
# AFTER the Forgejo login, which is why it costs people twenty minutes.
|
||||
SSO_ENABLED: "true"
|
||||
# Just the base URL. Vaultwarden reads Forgejo's discovery document
|
||||
# (/.well-known/openid-configuration) from here on the first login.
|
||||
SSO_AUTHORITY: https://git.${DOMAIN}
|
||||
SSO_CLIENT_ID: ${VAULTWARDEN_SSO_CLIENT_ID}
|
||||
SSO_CLIENT_SECRET: ${VAULTWARDEN_SSO_CLIENT_SECRET}
|
||||
# Vaultwarden prepends "openid" itself, so listing it here again shows up
|
||||
# duplicated on the Forgejo consent screen. Harmless, but untidy.
|
||||
SSO_SCOPES: "profile email"
|
||||
# Attach an SSO login to the existing account with the same mail address
|
||||
# instead of creating a duplicate. Turn this on BEFORE people log in.
|
||||
SSO_SIGNUPS_MATCH_EMAIL: "true"
|
||||
volumes:
|
||||
- vaultwarden_data:/data
|
||||
networks: [proxy]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue