3 Commits

Author SHA1 Message Date
4d0448fa76 move oidc issuer_url and client_id to env vars, rename secret to oidc_secret
Only oidc_client_secret is actually sensitive — issuer_url and client_id
are now plain env vars. Renamed oidc_client_secret to oidc_secret to
pass abra lint. Updated README with accurate quickstart and OIDC setup.
Entrypoint guards git commands for min image compatibility.
2026-04-07 05:09:27 +00:00
bf8153f9db README update 2026-04-07 00:44:53 -04:00
a8cee2f92d add configurable auth_providers, min image overlay, bump to 0.1.9
- Add AUTH_PROVIDERS env var (comma-separated, default: file,atproto)
  to control which login methods are shown
- Unified lichen.toml.tmpl with OIDC section gated on OIDC_ENABLED env
- Entrypoint generates lichen.toml from base config only if not already
  present, preserving user customizations
- Add compose.min.yml overlay for lichen-min image (without atproto/git/shell)
- Entrypoint guards git commands for min image compatibility
- Bump lichen-full and lichen-min images to 0.1.9
- Bump recipe version to 0.1.1+0.1.9
2026-04-07 04:40:06 +00:00
7 changed files with 105 additions and 59 deletions

View File

@ -16,16 +16,21 @@ COMPOSE_FILE="compose.yml"
# Extra domains for sites with custom domains (HostSNI backtick format)
#EXTRA_DOMAINS=', `site1.example.com`, `site2.example.org`'
# Minimal image without atproto/git/shell (uncomment to use)
#COMPOSE_FILE="$COMPOSE_FILE:compose.min.yml"
# SSO/OIDC (uncomment to enable)
#COMPOSE_FILE="$COMPOSE_FILE:compose.oidc.yml"
#SECRET_OIDC_ISSUER_URL_VERSION=v1 # generate=false
#SECRET_OIDC_CLIENT_ID_VERSION=v1 # generate=false
#SECRET_OIDC_CLIENT_SECRET_VERSION=v1 # generate=false
#LICHEN_TOML_VERSION=v1
#OIDC_ISSUER_URL=https://keycloak.example.com/realms/myrealm
#OIDC_CLIENT_ID=lichen
#SECRET_OIDC_SECRET_VERSION=v1 # generate=false
# Secrets
SECRET_ADMIN_PASSWORD_VERSION=v1
# Config versions
ENTRYPOINT_VERSION=v4
CADDYFILE_VERSION=v2
LICHEN_TOML_VERSION=v1
# Auth providers (comma-separated: file, atproto, oidc)
#AUTH_PROVIDERS=file,atproto

View File

@ -4,41 +4,74 @@
* **Category**: Apps
* **Status**: 0
* **Image**: ghcr.io/notplants/lichen-full
* **Image**: [`notplants/lichen-full`](https://hub.docker.com/r/notplants/lichen-full)
* **Healthcheck**: Yes
* **Backups**: No
* **Backups**: Yes
* **Email**: No
* **Tests**: No
* **SSO**: No
* **SSO**: Yes (OIDC)
<!-- endmetadata -->
## Quickstart
## Basic usage
1. `abra app new lichen` (do **not** use `--secrets` yet, see below)
2. `abra app deploy YOURAPPDOMAIN`
1. Set up Docker Swarm and [`abra`]
2. Deploy [`coop-cloud/traefik`]
3. `abra app new lichen`
4. `abra app config YOURAPPDOMAIN` — set `DOMAIN` to something that resolves to your Docker Swarm box
5. `abra app secret generate YOURAPPDOMAIN --all`
6. `abra app deploy YOURAPPDOMAIN`
## Auth providers
The `AUTH_PROVIDERS` env var controls which login methods are available (comma-separated). The default is `file,atproto`.
Valid providers: `file` (username/password), `atproto` (Bluesky), `oidc` (OpenID Connect).
## Minimal image
A lighter image without atproto/git/shell support is available. Enable it by uncommenting the min overlay in your app config:
```
COMPOSE_FILE="$COMPOSE_FILE:compose.min.yml"
```
When using the minimal image, set `AUTH_PROVIDERS=file` (or `file,oidc` with OIDC).
## SSO/OIDC
To enable OIDC login, uncomment the OIDC overlay and set your provider details in your app config:
```
COMPOSE_FILE="$COMPOSE_FILE:compose.oidc.yml"
OIDC_ISSUER_URL=https://keycloak.example.com/realms/myrealm
OIDC_CLIENT_ID=lichen
SECRET_OIDC_SECRET_VERSION=v1
```
Then insert the client secret:
```
abra app secret insert YOURAPPDOMAIN oidc_secret v1 YOUR_CLIENT_SECRET
```
Add `oidc` to your `AUTH_PROVIDERS` (e.g. `AUTH_PROVIDERS=file,oidc`) and redeploy.
## TLS architecture (Caddy sidecar)
This recipe uses a **Caddy sidecar** for TLS instead of letting Traefik terminate
TLS directly.
This recipe uses a **Caddy sidecar** for TLS instead of letting Traefik terminate TLS directly.
The architecture:
1. **Traefik** receives TLS connections on port 443 and does **TCP passthrough**
(no TLS termination) for traffic matching `DOMAIN` and `*.DOMAIN`, forwarding
the raw TLS stream to Caddy.
2. **Caddy** terminates TLS using **on-demand certificates** — it automatically
obtains a Let's Encrypt certificate for each subdomain the first time a
connection arrives, using the TLS-ALPN-01 challenge.
1. **Traefik** receives TLS connections on port 443 and does **TCP passthrough** (no TLS termination) for traffic matching `DOMAIN` and `*.DOMAIN`, forwarding the raw TLS stream to Caddy.
2. **Caddy** terminates TLS using **on-demand certificates** — it automatically obtains a Let's Encrypt certificate for each subdomain the first time a connection arrives.
3. **Caddy** reverse proxies the decrypted HTTP traffic to lichen on port 9000.
**Note:** The first request to a new subdomain handle may take 10-30 seconds while
Caddy obtains the TLS certificate from Let's Encrypt. Subsequent requests are instant.
The first request to a new subdomain may take 10-30 seconds while Caddy obtains the TLS certificate. Subsequent requests are instant.
No changes to the Traefik recipe are needed — the TCP passthrough is configured
entirely via deploy labels on the Caddy service in this recipe's `compose.yml`.
No changes to the Traefik recipe are needed — the TCP passthrough is configured entirely via deploy labels in this recipe's `compose.yml`.
*
---
recipe maintained by @notplants
Recipe maintained by @notplants
[`abra`]: https://git.coopcloud.tech/coop-cloud/abra
[`coop-cloud/traefik`]: https://git.coopcloud.tech/coop-cloud/traefik

6
compose.min.yml Normal file
View File

@ -0,0 +1,6 @@
---
version: "3.8"
services:
app:
image: notplants/lichen-min:0.1.9

View File

@ -3,28 +3,14 @@ version: "3.8"
services:
app:
configs:
- source: lichen_toml
target: /data/lichen.toml
mode: 0444
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER_URL=${OIDC_ISSUER_URL}
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
secrets:
- oidc_issuer_url
- oidc_client_id
- oidc_client_secret
- oidc_secret
secrets:
oidc_issuer_url:
oidc_secret:
external: true
name: ${STACK_NAME}_oidc_issuer_url_${SECRET_OIDC_ISSUER_URL_VERSION}
oidc_client_id:
external: true
name: ${STACK_NAME}_oidc_client_id_${SECRET_OIDC_CLIENT_ID_VERSION}
oidc_client_secret:
external: true
name: ${STACK_NAME}_oidc_client_secret_${SECRET_OIDC_CLIENT_SECRET_VERSION}
configs:
lichen_toml:
name: ${STACK_NAME}_lichen_toml_${LICHEN_TOML_VERSION}
file: lichen.toml.tmpl
template_driver: golang
name: ${STACK_NAME}_oidc_secret_${SECRET_OIDC_SECRET_VERSION}

View File

@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: notplants/lichen-full:0.1.6
image: notplants/lichen-full:0.1.9
entrypoint: ["/entrypoint.sh"]
networks:
- internal
@ -13,11 +13,15 @@ services:
- LM_USE_AUTH=true
- LM_ROOT_DIR=/data
- LM_PUBLIC_URL=https://${DOMAIN}
- AUTH_PROVIDERS=${AUTH_PROVIDERS:-file,atproto}
- RUST_LOG=${RUST_LOG:-info}
configs:
- source: entrypoint
target: /entrypoint.sh
mode: 0555
- source: lichen_toml
target: /data/lichen-base.toml
mode: 0444
secrets:
- admin_password
volumes:
@ -28,7 +32,7 @@ services:
max_attempts: 5
labels:
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT:-120}"
- "coop-cloud.${STACK_NAME}.version=0.1.0+0.1.6"
- "coop-cloud.${STACK_NAME}.version=0.1.2+v0.1.9"
- "backupbot.backup=${ENABLE_BACKUPS:-true}"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:9000/tls-check"]
@ -83,3 +87,7 @@ configs:
caddyfile:
name: ${STACK_NAME}_caddyfile_${CADDYFILE_VERSION}
file: Caddyfile
lichen_toml:
name: ${STACK_NAME}_lichen_toml_${LICHEN_TOML_VERSION}
file: lichen.toml.tmpl
template_driver: golang

View File

@ -7,9 +7,18 @@ rm -f /usr/bin/bwrap
# Install bash for lichen shell feature
apk add --no-cache bash > /dev/null 2>&1 || true
# Set git identity for auto-commit
git config --global user.email "lichen@${LM_DASHBOARD_DOMAIN:-localhost}"
git config --global user.name "lichen"
# Set git identity for auto-commit (git may not be present in min image)
if command -v git > /dev/null 2>&1; then
git config --global user.email "lichen@${LM_DASHBOARD_DOMAIN:-localhost}"
git config --global user.name "lichen"
fi
# Copy base config to lichen.toml only if user hasn't customized it
if [ ! -f /data/lichen.toml ]; then
# Convert comma-separated AUTH_PROVIDERS to TOML array
TOML_PROVIDERS=$(echo "${AUTH_PROVIDERS:-file,atproto}" | sed 's/[^,][^,]*/\"&\"/g')
{ echo "auth_providers = [$TOML_PROVIDERS]"; echo; cat /data/lichen-base.toml; } > /data/lichen.toml
fi
ADMIN_PASSWORD=$(cat /run/secrets/admin_password)

View File

@ -1,7 +1,6 @@
use_auth = true
auth_providers = ["file", "oidc"]
{{ if env "OIDC_ENABLED" }}
[oidc]
issuer_url = "{{ secret "oidc_issuer_url" }}"
client_id = "{{ secret "oidc_client_id" }}"
client_secret = "{{ secret "oidc_client_secret" }}"
issuer_url = "{{ env "OIDC_ISSUER_URL" }}"
client_id = "{{ env "OIDC_CLIENT_ID" }}"
client_secret = "{{ secret "oidc_secret" }}"
{{ end }}