feat(deploy): update .env.example and Authentik blueprints for improved configuration
- Enhanced .env.example with new variables for PUBLIC_HOST, SECURE, and SUITE_ORIGIN to streamline environment setup. - Updated Authentik blueprints to utilize the new configuration variables for redirect URIs and launch URLs. - Introduced a new script to render Authentik blueprint templates dynamically based on environment variables. - Modified docker-compose files to reference the updated environment variables for better maintainability. - Improved expose.sh script to derive public URLs from the new configuration, ensuring consistency across deployments.
This commit is contained in:
parent
f97988b51f
commit
125169edee
43
.env.example
43
.env.example
@ -28,17 +28,29 @@ JITSI_INTERNAL_AUTH_PASSWORD=changeme
|
|||||||
KEYDB_PASSWORD=
|
KEYDB_PASSWORD=
|
||||||
MEILISEARCH_API_KEY=changeme
|
MEILISEARCH_API_KEY=changeme
|
||||||
TYPESENSE_API_KEY=changeme
|
TYPESENSE_API_KEY=changeme
|
||||||
# Cloudflare Tunnel — dev local exposé publiquement (npm run expose)
|
# Cloudflare Tunnel — dev local exposé publiquement (./deploy/expose.sh)
|
||||||
# CLOUDFLARE_TUNNEL_TOKEN=
|
# CLOUDFLARE_TUNNEL_TOKEN=
|
||||||
# CLOUDFLARE_TUNNEL_PUBLIC_URL=https://dev.ultispace.fr
|
# CLOUDFLARE_TUNNEL_PUBLIC_URL dérivé de SUITE_ORIGIN (voir section Public suite URL)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Public suite URL — modifier PUBLIC_HOST + SECURE pour changer de domaine
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Nom d'hôte public uniquement (sans schéma) : dev.example.com ou localhost
|
||||||
|
PUBLIC_HOST=localhost
|
||||||
|
# "s" → https/wss (tunnel Cloudflare, prod) ; vide → http/ws (nginx local :80)
|
||||||
|
SECURE=
|
||||||
|
# Alias nginx / compose (expansion {{PUBLIC_HOST}})
|
||||||
|
DOMAIN={{PUBLIC_HOST}}
|
||||||
|
# Origine publique dérivée (http:// ou https:// + hôte)
|
||||||
|
SUITE_ORIGIN=http{{SECURE}}://{{PUBLIC_HOST}}
|
||||||
|
CLOUDFLARE_TUNNEL_PUBLIC_URL={{SUITE_ORIGIN}}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# General
|
# General
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
DOMAIN=localhost
|
|
||||||
ULTID_PORT=8080
|
ULTID_PORT=8080
|
||||||
# Origines navigateur autorisees (web app sur autre port/origine que l'API).
|
# Origines navigateur autorisees (web app sur autre port/origine que l'API).
|
||||||
# Vide = auto : localhost/127.0.0.1/LAN prive en dev ; http(s)://${DOMAIN} en prod.
|
# Vide = auto : localhost/127.0.0.1/LAN prive en dev ; SUITE_ORIGIN en prod.
|
||||||
# Exemple dev explicite : ULTID_CORS_ALLOWED_ORIGINS=http://localhost:3004,http://127.0.0.1:3004
|
# Exemple dev explicite : ULTID_CORS_ALLOWED_ORIGINS=http://localhost:3004,http://127.0.0.1:3004
|
||||||
# ULTID_CORS_ALLOWED_ORIGINS=
|
# ULTID_CORS_ALLOWED_ORIGINS=
|
||||||
|
|
||||||
@ -108,7 +120,9 @@ AUTHENTIK_POSTGRESQL__NAME=authentik
|
|||||||
AUTHENTIK_REDIS__HOST=keydb
|
AUTHENTIK_REDIS__HOST=keydb
|
||||||
AUTHENTIK_WEB__PATH=/auth/
|
AUTHENTIK_WEB__PATH=/auth/
|
||||||
# URL publique affichee dans les redirects OIDC (navigateur)
|
# URL publique affichee dans les redirects OIDC (navigateur)
|
||||||
AUTHENTIK_HOST=http://{{DOMAIN}}
|
AUTHENTIK_HOST=http{{SECURE}}://{{PUBLIC_HOST}}
|
||||||
|
# Authentik provisioning : true quand SECURE=s (sinon déduit de AUTHENTIK_HOST)
|
||||||
|
# AUTHENTIK_PUBLIC_HTTPS=true
|
||||||
# API interne (ultid → authentik-server) pour provisionner les apps OIDC au demarrage
|
# API interne (ultid → authentik-server) pour provisionner les apps OIDC au demarrage
|
||||||
AUTHENTIK_API_URL=http://authentik-server:9000
|
AUTHENTIK_API_URL=http://authentik-server:9000
|
||||||
# Token admin Authentik (Flows & Stages → Tokens) — active le provisioning API ultid
|
# Token admin Authentik (Flows & Stages → Tokens) — active le provisioning API ultid
|
||||||
@ -122,8 +136,8 @@ AUTHENTIK_API_URL=http://authentik-server:9000
|
|||||||
# URL interne (ultid → nginx Nextcloud, racine WebDAV)
|
# URL interne (ultid → nginx Nextcloud, racine WebDAV)
|
||||||
NEXTCLOUD_URL=http://nextcloud:80
|
NEXTCLOUD_URL=http://nextcloud:80
|
||||||
# URL publique UI (edge nginx /cloud/) — aussi base Destination WebDAV MOVE/COPY
|
# URL publique UI (edge nginx /cloud/) — aussi base Destination WebDAV MOVE/COPY
|
||||||
NC_PUBLIC_URL=http://{{DOMAIN}}/cloud
|
NC_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}/cloud
|
||||||
NC_OVERWRITE_PROTOCOL=http
|
NC_OVERWRITE_PROTOCOL=http{{SECURE}}
|
||||||
NC_ADMIN_USER=admin
|
NC_ADMIN_USER=admin
|
||||||
# NC_ADMIN_PASSWORD — defini dans la section Secrets
|
# NC_ADMIN_PASSWORD — defini dans la section Secrets
|
||||||
# Exemple externe :
|
# Exemple externe :
|
||||||
@ -150,7 +164,7 @@ NC_S3_SSL=false
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
ONLYOFFICE_ENABLED=false
|
ONLYOFFICE_ENABLED=false
|
||||||
ONLYOFFICE_URL=http://onlyoffice
|
ONLYOFFICE_URL=http://onlyoffice
|
||||||
ONLYOFFICE_PUBLIC_URL=http://{{DOMAIN}}/office
|
ONLYOFFICE_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}/office
|
||||||
# URL ultid joignable depuis le conteneur OnlyOffice (fetch doc + callback)
|
# URL ultid joignable depuis le conteneur OnlyOffice (fetch doc + callback)
|
||||||
ONLYOFFICE_API_INTERNAL_URL=http://ultid:8080
|
ONLYOFFICE_API_INTERNAL_URL=http://ultid:8080
|
||||||
# URL Nextcloud joignable depuis OnlyOffice (host nginx Docker). Nginx route /index.php/* → Nextcloud.
|
# URL Nextcloud joignable depuis OnlyOffice (host nginx Docker). Nginx route /index.php/* → Nextcloud.
|
||||||
@ -159,15 +173,15 @@ ONLYOFFICE_API_INTERNAL_URL=http://ultid:8080
|
|||||||
ONLYOFFICE_JWT_SECRET=changeme-onlyoffice-jwt
|
ONLYOFFICE_JWT_SECRET=changeme-onlyoffice-jwt
|
||||||
ONLYOFFICE_OIDC_CLIENT_ID=ulti-onlyoffice
|
ONLYOFFICE_OIDC_CLIENT_ID=ulti-onlyoffice
|
||||||
# ONLYOFFICE_OIDC_CLIENT_SECRET — defini dans la section Secrets
|
# ONLYOFFICE_OIDC_CLIENT_SECRET — defini dans la section Secrets
|
||||||
ULTID_PUBLIC_URL=http://{{DOMAIN}}
|
ULTID_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}
|
||||||
# Base URL for public share links (default: {ULTID_PUBLIC_URL}/drive → /drive/s/{token})
|
# Base URL for public share links (default: {ULTID_PUBLIC_URL}/drive → /drive/s/{token})
|
||||||
# DRIVE_PUBLIC_URL=http://{{DOMAIN}}/drive
|
# DRIVE_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}/drive
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Rich text editor (TipTap + Hocuspocus)
|
# Rich text editor (TipTap + Hocuspocus)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
RICHTEXT_ENABLED=true
|
RICHTEXT_ENABLED=true
|
||||||
HOCUSPOCUS_PUBLIC_URL=ws://{{DOMAIN}}/collab
|
HOCUSPOCUS_PUBLIC_URL=ws{{SECURE}}://{{PUBLIC_HOST}}/collab
|
||||||
HOCUSPOCUS_SECRET=changeme-hocuspocus-secret
|
HOCUSPOCUS_SECRET=changeme-hocuspocus-secret
|
||||||
RICHTEXT_STORAGE_MODE=sidecar
|
RICHTEXT_STORAGE_MODE=sidecar
|
||||||
# RICHTEXT_EXPORT_MIRROR=docx
|
# RICHTEXT_EXPORT_MIRROR=docx
|
||||||
@ -183,6 +197,7 @@ AI_GATEWAY_API_KEY=ulti-gateway
|
|||||||
BYPASS_MODEL_ACCESS_CONTROL=true
|
BYPASS_MODEL_ACCESS_CONTROL=true
|
||||||
WEBUI_NAME=UltiAI
|
WEBUI_NAME=UltiAI
|
||||||
AI_ASSISTANT_PUBLIC_PATH=/ai
|
AI_ASSISTANT_PUBLIC_PATH=/ai
|
||||||
|
OPENWEBUI_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}/ai
|
||||||
ULTIMAIL_MCP_URL=http://ultimail-mcp:3100
|
ULTIMAIL_MCP_URL=http://ultimail-mcp:3100
|
||||||
# Public MCP endpoint (nginx → ultid → ultimail-mcp): /api/v1/ai/mcp
|
# Public MCP endpoint (nginx → ultid → ultimail-mcp): /api/v1/ai/mcp
|
||||||
# OpenWebUI uses AI_GATEWAY_API_KEY + forwarded X-OpenWebUI-User-* headers for per-user tokens.
|
# OpenWebUI uses AI_GATEWAY_API_KEY + forwarded X-OpenWebUI-User-* headers for per-user tokens.
|
||||||
@ -202,7 +217,7 @@ JITSI_ENABLED=true
|
|||||||
JITSI_DOMAIN=meet.jitsi
|
JITSI_DOMAIN=meet.jitsi
|
||||||
JITSI_APP_ID=ulti
|
JITSI_APP_ID=ulti
|
||||||
# JITSI_APP_SECRET — defini dans la section Secrets
|
# JITSI_APP_SECRET — defini dans la section Secrets
|
||||||
JITSI_PUBLIC_URL=https://{{DOMAIN}}/meet
|
JITSI_PUBLIC_URL=http{{SECURE}}://{{PUBLIC_HOST}}/meet
|
||||||
# Secret partagé avec Jigasi pour POST /api/v1/meet/transcripts
|
# Secret partagé avec Jigasi pour POST /api/v1/meet/transcripts
|
||||||
MEET_TRANSCRIPT_WEBHOOK_SECRET=changeme-meet-transcript-secret
|
MEET_TRANSCRIPT_WEBHOOK_SECRET=changeme-meet-transcript-secret
|
||||||
# Modèle Faster Whisper (Skynet) : tiny, base, small…
|
# Modèle Faster Whisper (Skynet) : tiny, base, small…
|
||||||
@ -238,7 +253,7 @@ IMMICH_ML_URL=http://immich-ml:3003
|
|||||||
HEALTH_NEXTCLOUD_URL={{NEXTCLOUD_URL}}/status.php
|
HEALTH_NEXTCLOUD_URL={{NEXTCLOUD_URL}}/status.php
|
||||||
HEALTH_IMMICH_URL={{IMMICH_API_URL}}/server-info/ping
|
HEALTH_IMMICH_URL={{IMMICH_API_URL}}/server-info/ping
|
||||||
# Stack Docker locale : probe interne jitsi-web
|
# Stack Docker locale : probe interne jitsi-web
|
||||||
# Déploiement externe : https://{{DOMAIN}}/about/health (ou JITSI_PUBLIC_URL sans /meet + /about/health)
|
# Déploiement externe : {{SUITE_ORIGIN}}/about/health (ou JITSI_PUBLIC_URL sans /meet + /about/health)
|
||||||
HEALTH_JITSI_URL=http://jitsi-web:80/about/health
|
HEALTH_JITSI_URL=http://jitsi-web:80/about/health
|
||||||
HEALTH_HTTP_TIMEOUT=3s
|
HEALTH_HTTP_TIMEOUT=3s
|
||||||
# Grafana local (monitoring)
|
# Grafana local (monitoring)
|
||||||
@ -274,7 +289,7 @@ MAIL_MICROSOFT_OAUTH_CLIENT_ID=
|
|||||||
MAIL_MICROSOFT_OAUTH_CLIENT_SECRET=
|
MAIL_MICROSOFT_OAUTH_CLIENT_SECRET=
|
||||||
MAIL_MICROSOFT_OAUTH_TENANT=common
|
MAIL_MICROSOFT_OAUTH_TENANT=common
|
||||||
MAIL_OAUTH_REDIRECT_URL=
|
MAIL_OAUTH_REDIRECT_URL=
|
||||||
MAIL_APP_URL=http://localhost/mail
|
MAIL_APP_URL=http{{SECURE}}://{{PUBLIC_HOST}}/mail
|
||||||
# Cible nginx → suite frontend unifié mail+drive (dev: Next sur l'hôte :3004 ; prod: suite-frontend:3000)
|
# Cible nginx → suite frontend unifié mail+drive (dev: Next sur l'hôte :3004 ; prod: suite-frontend:3000)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@ -81,12 +81,17 @@ Si `AUTHENTIK_API_TOKEN` est défini, **ultid** provisionne au démarrage les ap
|
|||||||
|
|
||||||
Sans token : les blueprints ci-dessus restent la source (worker Authentik au boot).
|
Sans token : les blueprints ci-dessus restent la source (worker Authentik au boot).
|
||||||
|
|
||||||
|
Les blueprints OIDC (`*-oidc.yaml`) sont **générés** depuis `*.yaml.template` au lancement de `./deploy/compose-up.sh`, avec les redirect URIs dérivés de `SUITE_ORIGIN` (`PUBLIC_HOST` + `SECURE`). Éditer les `.template`, pas les `.yaml` générés.
|
||||||
|
|
||||||
## Appliquer / vérifier
|
## Appliquer / vérifier
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Re-appliquer après modification
|
# Re-render + re-appliquer après changement de domaine
|
||||||
docker exec deploy-authentik-server-1 ak apply_blueprint /blueprints/custom/01-ulti-enrollment.yaml
|
./deploy/compose-up.sh restart authentik-worker
|
||||||
docker exec deploy-authentik-server-1 ak apply_blueprint /blueprints/custom/02-ulti-brand.yaml
|
|
||||||
|
# Ou manuellement :
|
||||||
|
./deploy/authentik/render-blueprints.sh # source .env.resolved d'abord
|
||||||
|
docker exec deploy-authentik-server-1 ak apply_blueprint /blueprints/custom/ulti-oidc.yaml
|
||||||
./deploy/compose-up.sh restart authentik-worker
|
./deploy/compose-up.sh restart authentik-worker
|
||||||
|
|
||||||
# Vérifier OIDC Ultimail
|
# Vérifier OIDC Ultimail
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
# Authentik blueprint — Nextcloud OIDC (when NEXTCLOUD_ENABLED=true)
|
# Authentik blueprint — Nextcloud OIDC (when NEXTCLOUD_ENABLED=true)
|
||||||
|
# Generated from nextcloud-oidc.yaml.template
|
||||||
# Client secret must match NC_OIDC_CLIENT_SECRET in .env
|
# Client secret must match NC_OIDC_CLIENT_SECRET in .env
|
||||||
version: 1
|
version: 1
|
||||||
metadata:
|
metadata:
|
||||||
@ -22,6 +23,8 @@ entries:
|
|||||||
client_id: ulti-nextcloud
|
client_id: ulti-nextcloud
|
||||||
client_secret: changeme
|
client_secret: changeme
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: https://dev.ultispace.fr/cloud/apps/user_oidc/code
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
url: http://localhost/cloud/apps/user_oidc/code
|
url: http://localhost/cloud/apps/user_oidc/code
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
@ -36,5 +39,5 @@ entries:
|
|||||||
slug: nextcloud
|
slug: nextcloud
|
||||||
group: Ulti Suite
|
group: Ulti Suite
|
||||||
provider: !KeyOf nc-oauth-provider
|
provider: !KeyOf nc-oauth-provider
|
||||||
meta_launch_url: http://localhost/cloud/
|
meta_launch_url: https://dev.ultispace.fr/cloud/
|
||||||
policy_engine_mode: any
|
policy_engine_mode: any
|
||||||
|
|||||||
43
deploy/authentik/blueprints/nextcloud-oidc.yaml.template
Normal file
43
deploy/authentik/blueprints/nextcloud-oidc.yaml.template
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Authentik blueprint — Nextcloud OIDC (when NEXTCLOUD_ENABLED=true)
|
||||||
|
# Generated from nextcloud-oidc.yaml.template
|
||||||
|
# Client secret must match NC_OIDC_CLIENT_SECRET in .env
|
||||||
|
version: 1
|
||||||
|
metadata:
|
||||||
|
name: Nextcloud OIDC
|
||||||
|
labels:
|
||||||
|
blueprints.goauthentik.io/instantiate: "true"
|
||||||
|
entries:
|
||||||
|
- model: authentik_providers_oauth2.oauth2provider
|
||||||
|
id: nc-oauth-provider
|
||||||
|
identifiers:
|
||||||
|
name: ulti-nextcloud-provider
|
||||||
|
attrs:
|
||||||
|
name: ulti-nextcloud-provider
|
||||||
|
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
|
||||||
|
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||||
|
property_mappings:
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]]
|
||||||
|
client_type: confidential
|
||||||
|
client_id: ulti-nextcloud
|
||||||
|
client_secret: changeme
|
||||||
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: {{SUITE_ORIGIN}}/cloud/apps/user_oidc/code
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://localhost/cloud/apps/user_oidc/code
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://127.0.0.1/cloud/apps/user_oidc/code
|
||||||
|
signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]
|
||||||
|
|
||||||
|
- model: authentik_core.application
|
||||||
|
identifiers:
|
||||||
|
slug: nextcloud
|
||||||
|
attrs:
|
||||||
|
name: Nextcloud
|
||||||
|
slug: nextcloud
|
||||||
|
group: Ulti Suite
|
||||||
|
provider: !KeyOf nc-oauth-provider
|
||||||
|
meta_launch_url: {{SUITE_ORIGIN}}/cloud/
|
||||||
|
policy_engine_mode: any
|
||||||
@ -1,4 +1,5 @@
|
|||||||
# Authentik blueprint — OnlyOffice OIDC (when ONLYOFFICE_ENABLED=true)
|
# Authentik blueprint — OnlyOffice OIDC (when ONLYOFFICE_ENABLED=true)
|
||||||
|
# Generated from onlyoffice-oidc.yaml.template
|
||||||
# Client secret must match ONLYOFFICE_OIDC_CLIENT_SECRET in .env
|
# Client secret must match ONLYOFFICE_OIDC_CLIENT_SECRET in .env
|
||||||
version: 1
|
version: 1
|
||||||
metadata:
|
metadata:
|
||||||
@ -22,6 +23,10 @@ entries:
|
|||||||
client_id: ulti-onlyoffice
|
client_id: ulti-onlyoffice
|
||||||
client_secret: changeme
|
client_secret: changeme
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: https://dev.ultispace.fr/office/
|
||||||
|
- matching_mode: strict
|
||||||
|
url: https://dev.ultispace.fr/office/oauth2/callback
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
url: http://localhost/office/
|
url: http://localhost/office/
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
@ -36,5 +41,5 @@ entries:
|
|||||||
slug: onlyoffice
|
slug: onlyoffice
|
||||||
group: Ulti Suite
|
group: Ulti Suite
|
||||||
provider: !KeyOf oo-oauth-provider
|
provider: !KeyOf oo-oauth-provider
|
||||||
meta_launch_url: http://localhost/office
|
meta_launch_url: https://dev.ultispace.fr/office
|
||||||
policy_engine_mode: any
|
policy_engine_mode: any
|
||||||
|
|||||||
45
deploy/authentik/blueprints/onlyoffice-oidc.yaml.template
Normal file
45
deploy/authentik/blueprints/onlyoffice-oidc.yaml.template
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Authentik blueprint — OnlyOffice OIDC (when ONLYOFFICE_ENABLED=true)
|
||||||
|
# Generated from onlyoffice-oidc.yaml.template
|
||||||
|
# Client secret must match ONLYOFFICE_OIDC_CLIENT_SECRET in .env
|
||||||
|
version: 1
|
||||||
|
metadata:
|
||||||
|
name: OnlyOffice OIDC
|
||||||
|
labels:
|
||||||
|
blueprints.goauthentik.io/instantiate: "true"
|
||||||
|
entries:
|
||||||
|
- model: authentik_providers_oauth2.oauth2provider
|
||||||
|
id: oo-oauth-provider
|
||||||
|
identifiers:
|
||||||
|
name: ulti-onlyoffice-provider
|
||||||
|
attrs:
|
||||||
|
name: ulti-onlyoffice-provider
|
||||||
|
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
|
||||||
|
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||||
|
property_mappings:
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]]
|
||||||
|
client_type: confidential
|
||||||
|
client_id: ulti-onlyoffice
|
||||||
|
client_secret: changeme
|
||||||
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: {{SUITE_ORIGIN}}/office/
|
||||||
|
- matching_mode: strict
|
||||||
|
url: {{SUITE_ORIGIN}}/office/oauth2/callback
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://localhost/office/
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://localhost/office/oauth2/callback
|
||||||
|
signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]
|
||||||
|
|
||||||
|
- model: authentik_core.application
|
||||||
|
identifiers:
|
||||||
|
slug: onlyoffice
|
||||||
|
attrs:
|
||||||
|
name: OnlyOffice
|
||||||
|
slug: onlyoffice
|
||||||
|
group: Ulti Suite
|
||||||
|
provider: !KeyOf oo-oauth-provider
|
||||||
|
meta_launch_url: {{SUITE_ORIGIN}}/office
|
||||||
|
policy_engine_mode: any
|
||||||
@ -1,4 +1,5 @@
|
|||||||
# Authentik blueprint — Ultimail OIDC (auto-applied on worker startup)
|
# Authentik blueprint — Ultimail OIDC (auto-applied on worker startup)
|
||||||
|
# Generated from ulti-oidc.yaml.template — edit template, not this file directly.
|
||||||
# Client secret must match ULTID_OIDC_CLIENT_SECRET in .env
|
# Client secret must match ULTID_OIDC_CLIENT_SECRET in .env
|
||||||
version: 1
|
version: 1
|
||||||
metadata:
|
metadata:
|
||||||
@ -25,6 +26,8 @@ entries:
|
|||||||
client_id: ulti-backend
|
client_id: ulti-backend
|
||||||
client_secret: changeme
|
client_secret: changeme
|
||||||
redirect_uris:
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: https://dev.ultispace.fr/api/auth/callback
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
url: http://localhost/api/auth/callback
|
url: http://localhost/api/auth/callback
|
||||||
- matching_mode: strict
|
- matching_mode: strict
|
||||||
@ -43,5 +46,5 @@ entries:
|
|||||||
slug: ulti
|
slug: ulti
|
||||||
group: Ulti Suite
|
group: Ulti Suite
|
||||||
provider: !KeyOf ulti-oauth-provider
|
provider: !KeyOf ulti-oauth-provider
|
||||||
meta_launch_url: http://localhost/mail/inbox
|
meta_launch_url: https://dev.ultispace.fr/mail/inbox
|
||||||
policy_engine_mode: any
|
policy_engine_mode: any
|
||||||
|
|||||||
50
deploy/authentik/blueprints/ulti-oidc.yaml.template
Normal file
50
deploy/authentik/blueprints/ulti-oidc.yaml.template
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Authentik blueprint — Ultimail OIDC (auto-applied on worker startup)
|
||||||
|
# Generated from ulti-oidc.yaml.template — edit template, not this file directly.
|
||||||
|
# Client secret must match ULTID_OIDC_CLIENT_SECRET in .env
|
||||||
|
version: 1
|
||||||
|
metadata:
|
||||||
|
name: Ultimail OIDC
|
||||||
|
labels:
|
||||||
|
blueprints.goauthentik.io/instantiate: "true"
|
||||||
|
entries:
|
||||||
|
- model: authentik_providers_oauth2.oauth2provider
|
||||||
|
id: ulti-oauth-provider
|
||||||
|
identifiers:
|
||||||
|
name: ulti-backend-provider
|
||||||
|
attrs:
|
||||||
|
name: ulti-backend-provider
|
||||||
|
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
|
||||||
|
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||||
|
access_token_validity: "hours=1"
|
||||||
|
refresh_token_validity: "days=365"
|
||||||
|
property_mappings:
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]]
|
||||||
|
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, offline_access]]
|
||||||
|
client_type: confidential
|
||||||
|
client_id: ulti-backend
|
||||||
|
client_secret: changeme
|
||||||
|
redirect_uris:
|
||||||
|
- matching_mode: strict
|
||||||
|
url: {{SUITE_ORIGIN}}/api/auth/callback
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://localhost/api/auth/callback
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://127.0.0.1/api/auth/callback
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://localhost:3004/api/auth/callback
|
||||||
|
- matching_mode: strict
|
||||||
|
url: http://127.0.0.1:3004/api/auth/callback
|
||||||
|
signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]
|
||||||
|
|
||||||
|
- model: authentik_core.application
|
||||||
|
identifiers:
|
||||||
|
slug: ulti
|
||||||
|
attrs:
|
||||||
|
name: Ultimail
|
||||||
|
slug: ulti
|
||||||
|
group: Ulti Suite
|
||||||
|
provider: !KeyOf ulti-oauth-provider
|
||||||
|
meta_launch_url: {{SUITE_ORIGIN}}/mail/inbox
|
||||||
|
policy_engine_mode: any
|
||||||
27
deploy/authentik/render-blueprints.sh
Executable file
27
deploy/authentik/render-blueprints.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Render Authentik blueprint templates using PUBLIC_HOST / SECURE / SUITE_ORIGIN from .env.resolved.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
BP_DIR="$ROOT/deploy/authentik/blueprints"
|
||||||
|
|
||||||
|
if [[ -z "${SUITE_ORIGIN:-}" || -z "${PUBLIC_HOST:-}" ]]; then
|
||||||
|
echo "render-blueprints: SUITE_ORIGIN and PUBLIC_HOST must be set (source .env.resolved first)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
render_one() {
|
||||||
|
local tpl="$1"
|
||||||
|
local out="${tpl%.template}"
|
||||||
|
sed \
|
||||||
|
-e "s|{{SUITE_ORIGIN}}|${SUITE_ORIGIN}|g" \
|
||||||
|
-e "s|{{PUBLIC_HOST}}|${PUBLIC_HOST}|g" \
|
||||||
|
-e "s|{{SECURE}}|${SECURE:-}|g" \
|
||||||
|
"$tpl" > "$out"
|
||||||
|
echo "render-blueprints: ${out##*/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
for tpl in "$BP_DIR"/*.yaml.template; do
|
||||||
|
render_one "$tpl"
|
||||||
|
done
|
||||||
@ -34,6 +34,8 @@ set -a
|
|||||||
source .env.resolved
|
source .env.resolved
|
||||||
set +a
|
set +a
|
||||||
|
|
||||||
|
"$ROOT/deploy/authentik/render-blueprints.sh"
|
||||||
|
|
||||||
compose_files=(
|
compose_files=(
|
||||||
"-f" "deploy/docker-compose.yml"
|
"-f" "deploy/docker-compose.yml"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -105,7 +105,7 @@ services:
|
|||||||
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
|
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
|
||||||
AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
|
AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
|
||||||
AUTHENTIK_WEB__PATH: /auth/
|
AUTHENTIK_WEB__PATH: /auth/
|
||||||
AUTHENTIK_HOST: http://${DOMAIN:-localhost}
|
AUTHENTIK_HOST: ${AUTHENTIK_HOST:-http://localhost}
|
||||||
env_file: ../.env.resolved
|
env_file: ../.env.resolved
|
||||||
volumes:
|
volumes:
|
||||||
- ./authentik/blueprints:/blueprints/custom:ro
|
- ./authentik/blueprints:/blueprints/custom:ro
|
||||||
@ -140,7 +140,7 @@ services:
|
|||||||
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
|
AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME:-authentik}
|
||||||
AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
|
AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST:-keydb}
|
||||||
AUTHENTIK_WEB__PATH: /auth/
|
AUTHENTIK_WEB__PATH: /auth/
|
||||||
AUTHENTIK_HOST: http://${DOMAIN:-localhost}
|
AUTHENTIK_HOST: ${AUTHENTIK_HOST:-http://localhost}
|
||||||
env_file: ../.env.resolved
|
env_file: ../.env.resolved
|
||||||
volumes:
|
volumes:
|
||||||
- ./authentik/blueprints:/blueprints/custom:ro
|
- ./authentik/blueprints:/blueprints/custom:ro
|
||||||
@ -207,15 +207,18 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ../../gmail-interface-clone
|
context: ../../gmail-interface-clone
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
PUBLIC_HOST: ${PUBLIC_HOST:-localhost}
|
||||||
|
SECURE: ${SECURE:-}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- ULTI_PROXY_ORIGIN=http://nginx
|
- ULTI_PROXY_ORIGIN=http://nginx
|
||||||
- NEXT_PUBLIC_API_URL=/api/v1
|
- NEXT_PUBLIC_API_URL=/api/v1
|
||||||
- NEXT_PUBLIC_APP_URL=http://${DOMAIN:-localhost}
|
- PUBLIC_HOST=${PUBLIC_HOST:-localhost}
|
||||||
|
- SECURE=${SECURE:-}
|
||||||
- OIDC_CLIENT_SECRET=${ULTID_OIDC_CLIENT_SECRET:-changeme}
|
- OIDC_CLIENT_SECRET=${ULTID_OIDC_CLIENT_SECRET:-changeme}
|
||||||
- NEXT_PUBLIC_OIDC_ISSUER=http://${DOMAIN:-localhost}/auth/application/o/ulti/
|
|
||||||
- NEXT_PUBLIC_OIDC_CLIENT_ID=${ULTID_OIDC_CLIENT_ID:-ulti-backend}
|
- NEXT_PUBLIC_OIDC_CLIENT_ID=${ULTID_OIDC_CLIENT_ID:-ulti-backend}
|
||||||
- NEXT_PUBLIC_ONLYOFFICE_URL=http://${DOMAIN:-localhost}/office
|
- NEXT_PUBLIC_AI_PUBLIC_PATH=/ai
|
||||||
networks:
|
networks:
|
||||||
- ulti-net
|
- ulti-net
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Expose local nginx (:80) via Cloudflare Tunnel (e.g. https://dev.ultispace.fr).
|
# Expose local nginx (:80) via Cloudflare Tunnel (public URL from SUITE_ORIGIN in .env).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
@ -10,9 +10,23 @@ if [[ ! -f .env ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
RESOLVED="$(mktemp)"
|
||||||
|
trap 'rm -f "$RESOLVED"' EXIT
|
||||||
|
|
||||||
|
if command -v go >/dev/null 2>&1; then
|
||||||
|
go run ./cmd/envexpand -in .env -out "$RESOLVED"
|
||||||
|
else
|
||||||
|
if [[ -x ./bin/envexpand ]]; then
|
||||||
|
./bin/envexpand -in .env -out "$RESOLVED"
|
||||||
|
else
|
||||||
|
echo "Go not found — install Go or build: go build -o bin/envexpand ./cmd/envexpand" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
set -a
|
set -a
|
||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
source .env
|
source "$RESOLVED"
|
||||||
set +a
|
set +a
|
||||||
|
|
||||||
if [[ -z "${CLOUDFLARE_TUNNEL_TOKEN:-}" ]]; then
|
if [[ -z "${CLOUDFLARE_TUNNEL_TOKEN:-}" ]]; then
|
||||||
@ -20,7 +34,7 @@ if [[ -z "${CLOUDFLARE_TUNNEL_TOKEN:-}" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PUBLIC_URL="${CLOUDFLARE_TUNNEL_PUBLIC_URL:-https://dev.ultispace.fr}"
|
PUBLIC_URL="${CLOUDFLARE_TUNNEL_PUBLIC_URL:-${SUITE_ORIGIN:-http://localhost}}"
|
||||||
echo "Cloudflare tunnel → local stack (nginx :80)"
|
echo "Cloudflare tunnel → local stack (nginx :80)"
|
||||||
echo "Public URL: ${PUBLIC_URL}"
|
echo "Public URL: ${PUBLIC_URL}"
|
||||||
echo "Start stack first: ./deploy/compose-up.sh up -d"
|
echo "Start stack first: ./deploy/compose-up.sh up -d"
|
||||||
|
|||||||
@ -18,7 +18,7 @@ services:
|
|||||||
AUTH_TYPE: jwt
|
AUTH_TYPE: jwt
|
||||||
JWT_ACCEPTED_ISSUERS: ulti
|
JWT_ACCEPTED_ISSUERS: ulti
|
||||||
JWT_ACCEPTED_AUDIENCES: ulti
|
JWT_ACCEPTED_AUDIENCES: ulti
|
||||||
PUBLIC_URL: https://${DOMAIN:-localhost}/meet
|
PUBLIC_URL: ${JITSI_PUBLIC_URL:-http://localhost/meet}
|
||||||
XMPP_DOMAIN: meet.jitsi
|
XMPP_DOMAIN: meet.jitsi
|
||||||
XMPP_MUC_DOMAIN: muc.meet.jitsi
|
XMPP_MUC_DOMAIN: muc.meet.jitsi
|
||||||
XMPP_BOSH_URL_BASE: http://jitsi-prosody:5280
|
XMPP_BOSH_URL_BASE: http://jitsi-prosody:5280
|
||||||
@ -70,7 +70,7 @@ services:
|
|||||||
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
|
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
|
||||||
JVB_PORT: "10000"
|
JVB_PORT: "10000"
|
||||||
JVB_STUN_SERVERS: stun.l.google.com:19302
|
JVB_STUN_SERVERS: stun.l.google.com:19302
|
||||||
PUBLIC_URL: https://${DOMAIN:-localhost}/meet
|
PUBLIC_URL: ${JITSI_PUBLIC_URL:-http://localhost/meet}
|
||||||
networks:
|
networks:
|
||||||
- ulti-net
|
- ulti-net
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@ -17,7 +17,7 @@ services:
|
|||||||
WEBUI_NAME: UltiAI
|
WEBUI_NAME: UltiAI
|
||||||
OPENAI_API_BASE_URL: http://ultid:8080/api/v1/ai
|
OPENAI_API_BASE_URL: http://ultid:8080/api/v1/ai
|
||||||
OPENAI_API_KEY: ${AI_GATEWAY_API_KEY:-ulti-gateway}
|
OPENAI_API_KEY: ${AI_GATEWAY_API_KEY:-ulti-gateway}
|
||||||
WEBUI_URL: http://${DOMAIN:-localhost}/ai
|
WEBUI_URL: ${OPENWEBUI_PUBLIC_URL:-http://localhost/ai}
|
||||||
WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:-changeme-openwebui-dev-secret}
|
WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:-changeme-openwebui-dev-secret}
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/openwebui
|
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/openwebui
|
||||||
USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED: "false"
|
USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED: "false"
|
||||||
|
|||||||
@ -91,12 +91,20 @@ func EnsureSuiteApplications(ctx context.Context, pool *pgxpool.Pool, cfg *confi
|
|||||||
launchURL = spec.LaunchURL(cfg)
|
launchURL = spec.LaunchURL(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync redirect URIs for blueprint- or API-provisioned providers (domain/https changes).
|
||||||
|
if prov, found, err := client.FindOAuth2ProviderByName(ctx, spec.ProviderName); err != nil {
|
||||||
|
slog.Warn("authentik provider lookup failed", "app", spec.Key, "error", err)
|
||||||
|
} else if found && prov.PK != 0 {
|
||||||
|
if err := client.UpdateOAuth2ProviderRedirects(ctx, prov.PK, redirects); err != nil {
|
||||||
|
slog.Warn("authentik redirect sync failed", "app", spec.Key, "error", err)
|
||||||
|
} else {
|
||||||
|
slog.Info("authentik redirect URIs synced", "app", spec.Key, "count", len(redirects))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if provisioned, err := IsProvisioned(ctx, pool, spec.Key); err != nil {
|
if provisioned, err := IsProvisioned(ctx, pool, spec.Key); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if provisioned {
|
} else if provisioned {
|
||||||
if err := syncRedirects(ctx, pool, client, spec.Key, redirects); err != nil {
|
|
||||||
slog.Warn("authentik redirect sync failed", "app", spec.Key, "error", err)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,17 +130,6 @@ func EnsureSuiteApplications(ctx context.Context, pool *pgxpool.Pool, cfg *confi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncRedirects(ctx context.Context, pool *pgxpool.Pool, client *Client, appKey string, redirects []string) error {
|
|
||||||
rec, err := GetProvisioned(ctx, pool, appKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rec.ProviderID == nil || *rec.ProviderID == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return client.UpdateOAuth2ProviderRedirects(ctx, *rec.ProviderID, redirects)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureApp(
|
func ensureApp(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *Client,
|
client *Client,
|
||||||
|
|||||||
@ -487,6 +487,9 @@ func authentikPublicHTTPS() bool {
|
|||||||
if envBool("AUTHENTIK_PUBLIC_HTTPS", false) {
|
if envBool("AUTHENTIK_PUBLIC_HTTPS", false) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(os.Getenv("SECURE")) == "s" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
host := strings.TrimSpace(os.Getenv("AUTHENTIK_HOST"))
|
host := strings.TrimSpace(os.Getenv("AUTHENTIK_HOST"))
|
||||||
return strings.HasPrefix(strings.ToLower(host), "https://")
|
return strings.HasPrefix(strings.ToLower(host), "https://")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user