Add homelab installer and services (allprox, authelia, lldap)
This commit is contained in:
parent
68b23f1cbe
commit
bb754cdd8c
32 changed files with 2356 additions and 1 deletions
131
services/README.md
Normal file
131
services/README.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# services
|
||||
|
||||
One folder per service. Each folder must contain a `metadata.json` describing the service
|
||||
(including the Docker Compose definition used to deploy it). The folder may also contain any extra
|
||||
files the service needs — a `Caddyfile`, a `Dockerfile`, static assets — which the installer copies
|
||||
into the deploy directory.
|
||||
|
||||
## Folder conventions
|
||||
|
||||
- The folder name **must** equal the service `id` in `metadata.json` (lowercase kebab-case).
|
||||
- Only folders that contain a valid `metadata.json` are treated as services by the installer.
|
||||
- After adding, editing, or removing a service, regenerate the index:
|
||||
|
||||
```bash
|
||||
cd installer
|
||||
bun run catalog
|
||||
```
|
||||
|
||||
This rewrites [`catalog.json`](catalog.json), which the installer uses as a one-request listing.
|
||||
|
||||
## Extra files
|
||||
|
||||
A service folder can contain files beyond `metadata.json` — for example the `allprox` service ships
|
||||
a `Caddyfile` and a `portal/index.html`. On install/update the installer copies the whole folder
|
||||
(except `metadata.json`) into `~/.homelab/services/<id>/`, preserving subdirectories.
|
||||
|
||||
Reference those files from `compose.volumes` with **relative** paths:
|
||||
|
||||
```json
|
||||
"volumes": [
|
||||
"./Caddyfile:/etc/caddy/Caddyfile:ro",
|
||||
"./portal:/srv/portal:ro"
|
||||
]
|
||||
```
|
||||
|
||||
Because the generated `docker-compose.yml` lives in the same directory, `docker compose` resolves
|
||||
the `./...` paths against it.
|
||||
|
||||
## Shared networks
|
||||
|
||||
Services that need to talk to each other (e.g. `allprox` → `authelia` → `lldap`) join an external
|
||||
Docker network. Declare it at the top level of `metadata.json` and attach the service to it:
|
||||
|
||||
```json
|
||||
"networks": { "homelab": { "external": true } },
|
||||
"compose": {
|
||||
"networks": ["homelab"]
|
||||
}
|
||||
```
|
||||
|
||||
The installer creates the external network automatically (if it doesn't already exist) before
|
||||
running `docker compose up`.
|
||||
|
||||
## `metadata.json` reference
|
||||
|
||||
| Field | Required | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `id` | ✅ | string | Unique kebab-case id, equal to the folder name. |
|
||||
| `name` | ✅ | string | Human-readable name shown in the TUI. |
|
||||
| `description` | ✅ | string | Short description. |
|
||||
| `version` | ✅ | string | Version string for change detection on update. |
|
||||
| `compose` | ✅ | object | A Docker Compose **service** definition (`image`, `ports`, `volumes`, `environment`, …). The installer wraps it in a generated `docker-compose.yml`. |
|
||||
| `volumes` | — | object | Optional top-level named volumes to declare. |
|
||||
| `networks` | — | object | Optional top-level networks to declare. |
|
||||
| `env` | — | array | Variables the installer resolves for you (see below). |
|
||||
| `category` | — | string | Grouping shown as a hint in the TUI. |
|
||||
| `tags` | — | string[] | Free-form tags. |
|
||||
| `icon` | — | string | URL to an icon. |
|
||||
| `author` | — | string | Upstream author. |
|
||||
| `license` | — | string | License of the deployed software. |
|
||||
| `homepage` | — | string | Project homepage URL. |
|
||||
| `documentation` | — | string | Documentation URL. |
|
||||
| `dependsOn` | — | string[] | Ids of services that should be installed first (informational). |
|
||||
| `notes` | — | string | Free-form notes shown by `info`. |
|
||||
|
||||
A machine-readable JSON Schema is available at [`docs/metadata-schema.json`](../docs/metadata-schema.json).
|
||||
|
||||
### `compose` (the service definition)
|
||||
|
||||
This object is the value you would normally put under a service key in `docker-compose.yml`. For
|
||||
example:
|
||||
|
||||
```json
|
||||
{
|
||||
"image": "ghcr.io/example/myapp:1.0.0",
|
||||
"container_name": "myapp",
|
||||
"restart": "unless-stopped",
|
||||
"ports": ["8080:8080"],
|
||||
"volumes": ["myapp_data:/data"],
|
||||
"environment": ["TZ=${TZ}"]
|
||||
}
|
||||
```
|
||||
|
||||
The installer turns it into:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
myapp:
|
||||
image: ghcr.io/example/myapp:1.0.0
|
||||
# ...
|
||||
```
|
||||
|
||||
Pin images to a tag so updates are predictable; the installer compares `version` to decide whether
|
||||
an update is available.
|
||||
|
||||
### `env` (interactive variables)
|
||||
|
||||
Each entry describes a variable the installer should collect (and write to `.env`):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "WEBPASSWORD",
|
||||
"label": "Web admin password",
|
||||
"description": "Password for the web UI",
|
||||
"default": "change-me",
|
||||
"required": true,
|
||||
"secret": true,
|
||||
"options": []
|
||||
}
|
||||
```
|
||||
|
||||
- `name` (required) — the variable name. Reference it in `compose` as `${NAME}`.
|
||||
- `label` / `description` — shown when prompting.
|
||||
- `default` — used when the user accepts the default or runs non-interactively.
|
||||
- `required` — a missing value is an error in non-interactive mode unless a `default` is set.
|
||||
- `secret` — mask input at the prompt (e.g. passwords).
|
||||
- `options` — if provided, the installer offers a fixed choice list instead of free text.
|
||||
- `generate` — generate a random 64-char hex secret instead of prompting (for JWT/session secrets).
|
||||
|
||||
Docker Compose automatically reads the `.env` written next to the generated compose file, so
|
||||
`${NAME}` references resolve at `docker compose up` time.
|
||||
63
services/allprox/Caddyfile
Normal file
63
services/allprox/Caddyfile
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# allprox — Caddy reverse proxy
|
||||
#
|
||||
# Maps domains to local IPs and hosts an authenticated portal (web UI).
|
||||
# Edit the domain names below to match your DNS, and set the upstream
|
||||
# IP:port values in the installer's .env (APP1_UPSTREAM, APP2_UPSTREAM, ...).
|
||||
# Duplicate a "Proxied services" block to add more services.
|
||||
#
|
||||
# Authentication:
|
||||
# * Today: basic_auth (a single admin account; hash set via PORTAL_AUTH_HASH).
|
||||
# * Later: Authelia SSO — replace the basic_auth blocks with the forward_auth
|
||||
# block shown below (see https://www.authelia.com/integration/proxies/caddy/).
|
||||
|
||||
{
|
||||
# For public domains with automatic HTTPS, set your ACME email here:
|
||||
# email you@example.com
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web UI — authenticated portal (change the domain to your own)
|
||||
# ---------------------------------------------------------------------------
|
||||
portal.example.com {
|
||||
# `tls internal` issues a self-signed cert for LAN use. Remove this line
|
||||
# and set the ACME email above when exposing a real public domain.
|
||||
tls internal
|
||||
|
||||
basic_auth {
|
||||
admin {$PORTAL_AUTH_HASH}
|
||||
}
|
||||
|
||||
# Authelia SSO (later): replace the basic_auth block above with:
|
||||
# forward_auth authelia:9091 {
|
||||
# uri /api/authz/forward-auth
|
||||
# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||
# }
|
||||
|
||||
root * /srv/portal
|
||||
file_server
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Authelia portal — the SSO login page (proxy to the authelia container).
|
||||
# With Authelia running, users are redirected here to sign in.
|
||||
# ---------------------------------------------------------------------------
|
||||
auth.example.com {
|
||||
tls internal
|
||||
reverse_proxy authelia:9091
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Proxied services — copy a block per service. The local IP:port comes from
|
||||
# the {$APPx_UPSTREAM} variable in .env.
|
||||
# ---------------------------------------------------------------------------
|
||||
app1.example.com {
|
||||
tls internal
|
||||
# basic_auth { admin {$PORTAL_AUTH_HASH} } # or the Authelia forward_auth block
|
||||
reverse_proxy {$APP1_UPSTREAM}
|
||||
}
|
||||
|
||||
app2.example.com {
|
||||
tls internal
|
||||
# basic_auth { admin {$PORTAL_AUTH_HASH} }
|
||||
reverse_proxy {$APP2_UPSTREAM}
|
||||
}
|
||||
64
services/allprox/metadata.json
Normal file
64
services/allprox/metadata.json
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"id": "allprox",
|
||||
"name": "allprox",
|
||||
"description": "Caddy reverse proxy that routes domains to local IPs, hosts an authenticated portal (web UI), and is SSO-ready for Authelia (OAuth2/OIDC).",
|
||||
"version": "1.0.0",
|
||||
"category": "network",
|
||||
"tags": ["reverse-proxy", "caddy", "authelia", "sso", "https"],
|
||||
"author": "Caddy / Authelia",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://caddyserver.com",
|
||||
"documentation": "https://www.authelia.com/integration/proxies/caddy/",
|
||||
"compose": {
|
||||
"image": "caddy:2-alpine",
|
||||
"container_name": "allprox",
|
||||
"restart": "unless-stopped",
|
||||
"ports": ["80:80", "443:443", "443:443/udp"],
|
||||
"volumes": [
|
||||
"./Caddyfile:/etc/caddy/Caddyfile:ro",
|
||||
"./portal:/srv/portal:ro",
|
||||
"allprox_data:/data",
|
||||
"allprox_config:/config"
|
||||
],
|
||||
"environment": [
|
||||
"APP1_UPSTREAM=${APP1_UPSTREAM}",
|
||||
"APP2_UPSTREAM=${APP2_UPSTREAM}",
|
||||
"PORTAL_AUTH_HASH=${PORTAL_AUTH_HASH}"
|
||||
],
|
||||
"networks": ["homelab"]
|
||||
},
|
||||
"volumes": {
|
||||
"allprox_data": {},
|
||||
"allprox_config": {}
|
||||
},
|
||||
"networks": {
|
||||
"homelab": { "external": true }
|
||||
},
|
||||
"env": [
|
||||
{
|
||||
"name": "PORTAL_AUTH_HASH",
|
||||
"label": "Portal admin password (bcrypt hash)",
|
||||
"description": "bcrypt hash for basic_auth (default is 'changeme'). Generate your own with: docker compose exec allprox caddy hash-password",
|
||||
"default": "$2b$10$fKkpKSlwLZtOBXpInl8pG.8mS65kiEfjOVsuvBj7ikHgtfqEa7h4y",
|
||||
"required": false,
|
||||
"secret": true
|
||||
},
|
||||
{
|
||||
"name": "APP1_UPSTREAM",
|
||||
"label": "Upstream for app1.example.com",
|
||||
"description": "Local IP:port to proxy app1.example.com to",
|
||||
"default": "127.0.0.1:3000",
|
||||
"required": false,
|
||||
"secret": false
|
||||
},
|
||||
{
|
||||
"name": "APP2_UPSTREAM",
|
||||
"label": "Upstream for app2.example.com",
|
||||
"description": "Local IP:port to proxy app2.example.com to",
|
||||
"default": "127.0.0.1:8080",
|
||||
"required": false,
|
||||
"secret": false
|
||||
}
|
||||
],
|
||||
"notes": "Edit services/allprox/Caddyfile to add domains and change upstreams. The portal (web UI) is at portal.example.com (change the domain). Interim auth is basic_auth (admin / 'changeme' by default) — swap to the Authelia forward_auth block in the Caddyfile for OAuth2/OIDC SSO."
|
||||
}
|
||||
80
services/allprox/portal/index.html
Normal file
80
services/allprox/portal/index.html
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>allprox — portal</title>
|
||||
<style>
|
||||
:root { color-scheme: dark; }
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
main { width: 100%; max-width: 680px; padding: 2rem; }
|
||||
h1 { font-size: 2rem; margin: 0 0 .25rem; letter-spacing: -.02em; }
|
||||
.muted { color: #94a3b8; }
|
||||
.card {
|
||||
background: #1e293b;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 12px;
|
||||
padding: 1.1rem 1.35rem;
|
||||
margin: 1.25rem 0;
|
||||
}
|
||||
.card strong { display: block; margin-bottom: .25rem; }
|
||||
ul { list-style: none; padding: 0; margin: 0; }
|
||||
li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: .55rem 0;
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
li:last-child { border-bottom: none; }
|
||||
a { color: #7dd3fc; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
code {
|
||||
background: #0b1220;
|
||||
padding: .15rem .45rem;
|
||||
border-radius: 5px;
|
||||
font-size: .85em;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
.badge {
|
||||
font-size: .7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .06em;
|
||||
color: #86efac;
|
||||
border: 1px solid #166534;
|
||||
background: #052e16;
|
||||
padding: .15rem .5rem;
|
||||
border-radius: 999px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>allprox <span class="badge">signed in</span></h1>
|
||||
<p class="muted">Authenticated access portal — you are signed in.</p>
|
||||
|
||||
<div class="card">
|
||||
<strong>Services</strong>
|
||||
<ul>
|
||||
<li><a href="https://app1.example.com">app1.example.com</a> <code>127.0.0.1:3000</code></li>
|
||||
<li><a href="https://app2.example.com">app2.example.com</a> <code>127.0.0.1:8080</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p class="muted">
|
||||
Edit this page at <code>services/allprox/portal/index.html</code> and the proxy
|
||||
rules in <code>services/allprox/Caddyfile</code>.
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
71
services/authelia/configuration.yml
Normal file
71
services/authelia/configuration.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Authelia configuration — https://www.authelia.com/configuration/prologue/introduction/
|
||||
#
|
||||
# Secrets (JWT_SECRET, RESET_JWT_SECRET, SESSION_SECRET, LDAP_ADMIN_PASSWORD) are
|
||||
# resolved from the service's .env by the installer and substituted into this file
|
||||
# on install/update, so they are not committed here.
|
||||
|
||||
theme: dark
|
||||
|
||||
jwt_secret: '${JWT_SECRET}'
|
||||
|
||||
server:
|
||||
address: 'tcp://0.0.0.0:9091/'
|
||||
endpoints:
|
||||
authz:
|
||||
forward-auth:
|
||||
implementation: 'ForwardAuth'
|
||||
|
||||
log:
|
||||
level: info
|
||||
|
||||
totp:
|
||||
issuer: 'homelab'
|
||||
|
||||
identity_validation:
|
||||
reset_password:
|
||||
jwt_secret: '${RESET_JWT_SECRET}'
|
||||
|
||||
authentication_backend:
|
||||
password_reset:
|
||||
disable: true
|
||||
refresh_interval: '5m'
|
||||
ldap:
|
||||
implementation: 'lldap'
|
||||
address: 'ldap://lldap:3890'
|
||||
base_dn: 'dc=homelab,dc=local'
|
||||
user: 'uid=admin,ou=people,dc=homelab,dc=local'
|
||||
password: '${LDAP_ADMIN_PASSWORD}'
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
rules:
|
||||
- domain: 'auth.example.com'
|
||||
policy: bypass
|
||||
- domain: 'portal.example.com'
|
||||
policy: one_factor
|
||||
- domain: '*.example.com'
|
||||
policy: one_factor
|
||||
|
||||
session:
|
||||
name: 'authelia_session'
|
||||
secret: '${SESSION_SECRET}'
|
||||
expiration: '1h'
|
||||
inactivity: '5m'
|
||||
remember_me: '1M'
|
||||
cookies:
|
||||
- domain: 'example.com'
|
||||
authelia_url: 'https://auth.example.com'
|
||||
default_redirection_url: 'https://portal.example.com'
|
||||
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: '2m'
|
||||
ban_time: '5m'
|
||||
|
||||
storage:
|
||||
local:
|
||||
path: '/config/db.sqlite3'
|
||||
|
||||
notifier:
|
||||
filesystem:
|
||||
filename: '/config/notification.txt'
|
||||
57
services/authelia/metadata.json
Normal file
57
services/authelia/metadata.json
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"id": "authelia",
|
||||
"name": "Authelia",
|
||||
"description": "Open-source authentication and authorization server providing SSO and 2FA for the homelab.",
|
||||
"version": "1.0.0",
|
||||
"category": "identity",
|
||||
"tags": ["sso", "authentication", "2fa", "oidc", "forward-auth"],
|
||||
"author": "Authelia",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://www.authelia.com",
|
||||
"documentation": "https://www.authelia.com/configuration/prologue/introduction/",
|
||||
"compose": {
|
||||
"image": "authelia/authelia:latest",
|
||||
"container_name": "authelia",
|
||||
"restart": "unless-stopped",
|
||||
"ports": ["9091:9091"],
|
||||
"volumes": [
|
||||
"authelia_config:/config",
|
||||
"./configuration.yml:/config/configuration.yml:ro",
|
||||
"./users_database.yml:/config/users_database.yml:ro"
|
||||
],
|
||||
"networks": ["homelab"]
|
||||
},
|
||||
"volumes": {
|
||||
"authelia_config": {}
|
||||
},
|
||||
"networks": {
|
||||
"homelab": { "external": true }
|
||||
},
|
||||
"env": [
|
||||
{
|
||||
"name": "JWT_SECRET",
|
||||
"label": "JWT secret (auto-generated)",
|
||||
"generate": true
|
||||
},
|
||||
{
|
||||
"name": "RESET_JWT_SECRET",
|
||||
"label": "Reset-password JWT secret (auto-generated)",
|
||||
"generate": true
|
||||
},
|
||||
{
|
||||
"name": "SESSION_SECRET",
|
||||
"label": "Session secret (auto-generated)",
|
||||
"generate": true
|
||||
},
|
||||
{
|
||||
"name": "LDAP_ADMIN_PASSWORD",
|
||||
"label": "LDAP admin password",
|
||||
"description": "Must match lldap's LLDAP_LDAP_USER_PASS",
|
||||
"default": "changeme-admin",
|
||||
"required": false,
|
||||
"secret": true
|
||||
}
|
||||
],
|
||||
"dependsOn": ["lldap"],
|
||||
"notes": "Authelia's portal is normally reached through allprox at auth.example.com (see the allprox Caddyfile). The forward-auth endpoint is http://authelia:9091/api/authz/forward-auth. Default access-control rules protect portal.example.com and *.example.com — edit configuration.yml to match your domains."
|
||||
}
|
||||
3
services/authelia/users_database.yml
Normal file
3
services/authelia/users_database.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Local users (file backend). We authenticate against LLDAP, so this stays empty.
|
||||
# Authelia still requires the file to exist.
|
||||
users: {}
|
||||
20
services/catalog.json
Normal file
20
services/catalog.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"generatedAt": "2026-09-01T16:20:56.550Z",
|
||||
"services": [
|
||||
{
|
||||
"id": "allprox",
|
||||
"version": "1.0.0",
|
||||
"path": "services/allprox/metadata.json"
|
||||
},
|
||||
{
|
||||
"id": "authelia",
|
||||
"version": "1.0.0",
|
||||
"path": "services/authelia/metadata.json"
|
||||
},
|
||||
{
|
||||
"id": "lldap",
|
||||
"version": "1.0.0",
|
||||
"path": "services/lldap/metadata.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
66
services/lldap/metadata.json
Normal file
66
services/lldap/metadata.json
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"id": "lldap",
|
||||
"name": "LLDAP",
|
||||
"description": "Lightweight LDAP server with a simple web UI for managing users and groups.",
|
||||
"version": "1.0.0",
|
||||
"category": "identity",
|
||||
"tags": ["ldap", "authentication", "identity", "web-ui"],
|
||||
"author": "LLDAP",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/lldap/lldap",
|
||||
"documentation": "https://github.com/lldap/lldap#readme",
|
||||
"compose": {
|
||||
"image": "lldap/lldap:stable",
|
||||
"container_name": "lldap",
|
||||
"restart": "unless-stopped",
|
||||
"ports": ["17170:17170", "3890:3890"],
|
||||
"volumes": ["lldap_data:/data"],
|
||||
"environment": [
|
||||
"LLDAP_LDAP_BASE_DN=${LLDAP_LDAP_BASE_DN}",
|
||||
"LLDAP_LDAP_USER_DN=admin",
|
||||
"LLDAP_LDAP_USER_PASS=${LLDAP_LDAP_USER_PASS}",
|
||||
"LLDAP_LDAP_USER_EMAIL=${LLDAP_LDAP_USER_EMAIL}",
|
||||
"LLDAP_JWT_SECRET=${LLDAP_JWT_SECRET}",
|
||||
"LLDAP_LDAP_PORT=3890",
|
||||
"LLDAP_HTTP_PORT=17170"
|
||||
],
|
||||
"networks": ["homelab"]
|
||||
},
|
||||
"volumes": {
|
||||
"lldap_data": {}
|
||||
},
|
||||
"networks": {
|
||||
"homelab": { "external": true }
|
||||
},
|
||||
"env": [
|
||||
{
|
||||
"name": "LLDAP_LDAP_BASE_DN",
|
||||
"label": "LDAP base DN",
|
||||
"description": "Base DN for users and groups (keep in sync with Authelia)",
|
||||
"default": "dc=homelab,dc=local",
|
||||
"required": false,
|
||||
"secret": false
|
||||
},
|
||||
{
|
||||
"name": "LLDAP_LDAP_USER_PASS",
|
||||
"label": "Admin password",
|
||||
"description": "Admin bind password — must match authelia's LDAP_ADMIN_PASSWORD",
|
||||
"default": "changeme-admin",
|
||||
"required": false,
|
||||
"secret": true
|
||||
},
|
||||
{
|
||||
"name": "LLDAP_LDAP_USER_EMAIL",
|
||||
"label": "Admin email",
|
||||
"default": "admin@homelab.local",
|
||||
"required": false,
|
||||
"secret": false
|
||||
},
|
||||
{
|
||||
"name": "LLDAP_JWT_SECRET",
|
||||
"label": "JWT secret (auto-generated)",
|
||||
"generate": true
|
||||
}
|
||||
],
|
||||
"notes": "Web UI at http://<host>:17170 — log in with 'admin' and your admin password. LDAP endpoint is ldap://lldap:3890 (base DN dc=homelab,dc=local). Create users and groups here; Authelia authenticates against them."
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue