744c85f4bf
sso-manager-node/jump-host already had the relay-automation mechanism
(noInbound/meshIp/publicHost -> theta-proxy route via proxy_client.js,
GET /api/mesh/self on jump-host) but nothing in the actual operator
bring-up flow could ever reach it -- setup.sh, bootstrap/site-join.js,
and setup.env.example had zero wiring for it.
Add bootstrap/site-relay-register.js: reads this spoke's own role from
/config/site.json, logs into the local jump-host as its bootstrap
admin to discover its mesh IP, and registers it with the master. Mesh
peering itself stays a manual step (mint/paste a join token, same
pattern as the site join key), so this runs on every setup.sh
invocation via CFG_SPOKE_NO_INBOUND/CFG_SPOKE_PUBLIC_HOST and is a
no-op ("not meshed yet") until an operator has actually meshed the two
jump-hosts.
Also updates MULTI_SITE_SPEC.md's status table/TODO and the published
mesh.md docs page, which still described this as "designed but not
automated" after the API-level work had already shipped.
147 lines
9.0 KiB
Bash
147 lines
9.0 KiB
Bash
# ─────────────────────────────────────────────────────────────────────────────
|
|
# setup.env — first-run setup for the theta-suite stack.
|
|
#
|
|
# This file is used ONLY on the FIRST run of ./setup.sh, to generate
|
|
# ./config/sso-secrets.js + ./config/proxy-secrets.js with your domain filled
|
|
# in everywhere consistently. Once ./config/*.js exist they are operator-owned
|
|
# (edit them directly; setup.env is ignored on later runs).
|
|
#
|
|
# cp setup.env.example setup.env
|
|
# $EDITOR setup.env # set CFG_DOMAIN below to your domain
|
|
# ./setup.sh # generates ./config/ and builds the stack
|
|
#
|
|
# Copying this file to setup.env (gitignored) keeps your domain out of git.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
# Your domain. THIS IS THE ONE PLACE THE DOMAIN IS ENTERED. Everything else
|
|
# derives from it: the SSO/proxy hostnames default to sso.<domain> /
|
|
# proxy.<domain>, and the LDAP base DN is built from it (example.com becomes
|
|
# dc=example,dc=com; a 3-label domain like myhost.duckdns.org becomes
|
|
# dc=myhost,dc=duckdns,dc=org — any number of labels works). Required —
|
|
# setup.sh refuses to run without it.
|
|
CFG_DOMAIN=example.com
|
|
|
|
# Site name for the SSO directory — the root node this stack registers itself
|
|
# under on the Directory page, and the default "Location (Site)" that Linux
|
|
# hosts joined via ldap-client attach to (parent slug: site_<name>).
|
|
# Optional — defaults to "local".
|
|
#CFG_SITE_NAME=local
|
|
|
|
# Public hostnames. Optional — default to sso.<domain> / proxy.<domain> derived
|
|
# from CFG_DOMAIN above. Uncomment and set only if your hostnames differ
|
|
# (e.g. a different subdomain, or the domain isn't the bare apex):
|
|
#CFG_SSO_HOST=sso.example.com
|
|
#CFG_PROXY_HOST=proxy.example.com
|
|
|
|
# ── SSH jump host (always installed) ─────────────────────────────────────────
|
|
# The theta42/jump-host component is installed and started by default — a
|
|
# public SSH jump host that authenticates users against the directory and
|
|
# bridges them to downstream hosts (ssh uid_-_target@jump, or an interactive
|
|
# picker). setup.sh clones/builds the jump-host submodule, the bootstrap mints
|
|
# its directory API token + writes ./config/jump-secrets.js, and it's registered
|
|
# in the proxy + directory. See jump-host's README for the LDAP write-ACL note
|
|
# (the bundled deployment binds as cn=admin).
|
|
#CFG_JUMP_HOST=jump.example.com # defaults to jump.<domain>
|
|
#JUMP_SSH_PORT=2222 # host port mapped to the jump host's SSH (never 22 by default)
|
|
|
|
# Advanced: override the derived LDAP base DN directly (e.g. to namespace
|
|
# under an OU-style prefix). Leave unset to use the DN built from CFG_DOMAIN:
|
|
#CFG_BASE_DN=dc=example,dc=com
|
|
|
|
# ── Multi-Site: join an existing (master) directory ──────────────────────────
|
|
# To run THIS deployment as a read-only SPOKE of an existing Theta Directory
|
|
# instead of seeding a fresh one, set the master's URL and a site join key
|
|
# (mint one on the master: Directory -> the Master Site modal -> Site Join Keys
|
|
# -> Mint key). Honored ONLY on a first-run bring-up (before ./config/ exists),
|
|
# so it can never merge an already-populated directory; re-runs ignore it.
|
|
# The spoke adopts the master's users/groups/resources and persists its spoke
|
|
# role in ./config/site.json (isMaster=false, masterUrl, siteSlug). It also
|
|
# registers itself with the master (using this site's own CFG_SSO_HOST) so
|
|
# future catalog changes on the master get pushed here live instead of this
|
|
# being a one-time snapshot -- the master must be able to reach THIS site's
|
|
# CFG_SSO_HOST for that part to work; if it can't (this site has no inbound
|
|
# path), the join still succeeds, it just never receives live updates.
|
|
#CFG_MASTER_DIRECTORY_URL=https://sso.master.example.com
|
|
#CFG_MASTER_DIRECTORY_JOIN_KEY=stj_9f2e...
|
|
|
|
# No public IP at all (CGNAT, etc.)? The master can still reach this spoke by
|
|
# relaying over the gateway-to-gateway WireGuard mesh instead of the open
|
|
# internet (MULTI_SITE_SPEC.md §5.2) -- but the mesh peering itself is a
|
|
# manual, out-of-band step on BOTH jump-hosts (mint a mesh join token on the
|
|
# master's jump-host, paste it into this site's jump-host "Join a mesh" UI
|
|
# action) that can't run unattended inside this script. Once that's done, set
|
|
# these two and re-run setup.sh: it discovers this jump-host's assigned mesh
|
|
# IP (GET /api/mesh/self) and registers it with the master, which then
|
|
# auto-creates the relay route on its own theta-proxy. Safe to leave set
|
|
# before meshing -- setup.sh just reports "not meshed yet" and skips until a
|
|
# later re-run finds the mesh IP.
|
|
#CFG_SPOKE_NO_INBOUND=true
|
|
#CFG_SPOKE_PUBLIC_HOST=sso-branch2.master-domain.example.com
|
|
|
|
# ── Optional outbound HTTP(S) proxy ──────────────────────────────────────────
|
|
# For an isolated/offline/corporate-network test host that only reaches the
|
|
# internet through an upstream HTTP proxy — NOT the theta42 "proxy" app.
|
|
# Wired into every service's docker build (npm/apt) AND its running container
|
|
# (SMTP, ACME/Let's Encrypt, DNS provider calls, the jump-host directory API
|
|
# client). Leave unset to disable (the default); CFG_HTTPS_PROXY falls back to
|
|
# CFG_HTTP_PROXY if unset, and CFG_NO_PROXY defaults to covering the stack's
|
|
# own internal service names so container-to-container traffic never goes
|
|
# through the proxy.
|
|
#CFG_HTTP_PROXY=http://proxy.example.com:3128
|
|
#CFG_HTTPS_PROXY=http://proxy.example.com:3128
|
|
#CFG_NO_PROXY=localhost,127.0.0.1,sso-manager,proxy,jump-host
|
|
|
|
# Optional — sensible defaults if left blank:
|
|
#CFG_ORG=SSO Manager # app display name + outbound email org
|
|
#CFG_ADMIN_UID=admin # initial SSO admin username
|
|
#CFG_ADMIN_EMAIL=admin@proxy.example.com # defaults to admin@<proxyHost>
|
|
#CFG_LDAP_CERT_CN= # LDAP TLS cert CN; empty -> defaults to the domain
|
|
#
|
|
# Hostname advertised on the SSO /integrations page for direct LDAPS binds.
|
|
# Leave blank to derive it from the public SSO host (same as oauth.issuer).
|
|
# Recommended: set an internal-only name like 'ldap.internal.example.com' or
|
|
# 'sso-manager' so clients don't need a public 636 port forward. See docs.
|
|
#CFG_LDAPS_HOST=
|
|
|
|
# ── DO NOT put secrets here ──────────────────────────────────────────────────
|
|
# The LDAP admin password, JWT secret, admin password, LDAP service-account
|
|
# password, and the proxy's local admin password are all GENERATED (random)
|
|
# into ./config/sso-secrets.js + ./config/proxy-secrets.js on first run.
|
|
# Change them later by editing those files directly (the proxy's local admin
|
|
# password is the exception — see ./config/proxy-secrets.js's auth.localAdminPass
|
|
# comment for how to actually change it after the account exists). Do NOT set
|
|
# CFG_LDAP_ADMIN_PASS / CFG_JWT_SECRET / CFG_ADMIN_PASS / CFG_SVC_PASS /
|
|
# CFG_PROXY_ADMIN_PASS here.
|
|
|
|
# ── theta-agent Host Integration ─────────────────────────────────────────────
|
|
# Configure theta-agent integration with the local host. All options default to
|
|
# enabled (1). Set to 0 to disable.
|
|
#
|
|
# Enable theta-agent installation and configuration on this host.
|
|
#CFG_THETA_AGENT_ENABLE=1
|
|
#
|
|
# Configure LDAP authentication for this host via ldap-client (SSSD/PAM).
|
|
#CFG_THETA_AGENT_LDAP_AUTH=1
|
|
#
|
|
# Allow theta-agent full control of this host (arbitrary_bash, service_control,
|
|
# reboot, configure_ldap capabilities).
|
|
#CFG_THETA_AGENT_FULL_CONTROL=1
|
|
|
|
# ── Geo-Location Scaling (N-Way Multi-Master LDAP) ───────────────────────────
|
|
# If deploying this stack across multiple physical sites to provide local HA
|
|
# for directory services, you can enable N-Way Multi-Master OpenLDAP replication.
|
|
# This requires assigning a unique ID to each site and listing the LDAPS URLs
|
|
# of all OTHER sites in the cluster.
|
|
#
|
|
# Each site MUST have a unique LDAP_SERVER_ID (e.g. 1, 2, 3).
|
|
# LDAP_REPLICATION_HOSTS is a space-separated list of the other sites' LDAP URLs.
|
|
# Example for Site 1:
|
|
#LDAP_SERVER_ID=1
|
|
#LDAP_REPLICATION_HOSTS="ldaps://sso.site2.com:636 ldaps://sso.site3.com:636"
|
|
# ── Proxy HTTP/HTTPS Defaults ────────────────────────────────────────────────
|
|
# If you are running the stack behind an external reverse proxy (like Cloudflare
|
|
# or another ingress) that handles TLS termination, you may want the internal
|
|
# proxy to serve everything over plain HTTP without forcing redirects to HTTPS.
|
|
# Set this to 1 to create all default proxy host entries with forcessl=false.
|
|
#CFG_CREATE_ALL_HTTP=1
|