2026-06-24 20:17:46 -04:00
|
|
|
; Gere par Set-OPS (role serveur_forgejo). Ne pas editer a la main.
|
2026-07-04 15:47:39 -04:00
|
|
|
APP_NAME = {{ serveur_forgejo_app_name }}
|
2026-06-24 20:17:46 -04:00
|
|
|
RUN_USER = {{ serveur_forgejo_utilisateur }}
|
|
|
|
|
RUN_MODE = prod
|
|
|
|
|
WORK_PATH = {{ serveur_forgejo_data }}
|
|
|
|
|
|
|
|
|
|
[server]
|
|
|
|
|
DOMAIN = {{ serveur_forgejo_hostname }}
|
site : un SITE a bien un plan — et huit defauts que lui seul pouvait reveler
J'ai repete toute la journee qu'un SITE n'est pas un plan. C'etait faux : j'en
reconstruisais un morceau par morceau dans underlay.yml sans le nommer. Ce qui est vrai,
c'est qu'un site ne DERIVE pas — il declare ses adresses parce qu'il EST le terrain — et
ne passe donc pas par `instancier`. Mais ne pas deriver n'est pas ne pas avoir de plan.
SITE-Chezlepro/plan/{10-intrants,serveurs,applications}.yml ; underlay.yml ne garde que
la fabric. Ce qui est MESURE d'un cote, ce qui est VOULU de l'autre. Les groupes viennent
du registre des applications, les gardes ont suivi le plan (4 controles negatifs).
Huit defauts, tous invisibles chez un tenant parce qu'il a toujours tout :
- client_pki codait `infra-pki-01` EN DUR — vrai par coincidence de nomenclature ;
- aucun moyen pour un service non-root de lire la cle (Forgejo tourne en `git`) ;
- le certificat, PUBLIC par nature, restait en 0600 ;
- l'unite Forgejo n'avait pas d'ExecReload : un cert renouvele aurait ete servi perime ;
- le role ne savait pas servir TLS lui-meme (il y avait toujours un edge) ;
- le cert ne couvrait pas le nom de SERVICE, faute d'`expose:` ;
- les registres se chargeaient en tout-ou-rien : sans domaines.yml, plancher vide ;
- le flux declarait 3000 en dur.
Le message accusait presque toujours autre chose : le DNS quand c'etait un nom faux, une
permission de fichier quand c'etait un port privilegie, rien du tout quand le plancher
s'ecrivait vide.
ROOT_URL est gravee dans les URL de clonage : la forge sert desormais sur 443, avec
CAP_NET_BIND_SERVICE, et son URL n'a plus de port.
Verifie depuis le reseau : Verify return code 0, https://forge.genese.internal/ -> 200.
42 preuves vertes, flux coherents (34 roles, 92 flux).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:15:34 -04:00
|
|
|
{# ROOT_URL EST GRAVEE DANS LES URL DE CLONAGE, les webhooks et les courriels. Un port
|
|
|
|
|
qui n'est pas celui du schema y reste pour toujours : chaque ecosysteme descendant
|
|
|
|
|
porterait `:3000` dans son `origin`. On ne l'ecrit donc que s'il est vraiment atypique. #}
|
|
|
|
|
ROOT_URL = https://{{ serveur_forgejo_hostname }}{{ (':' ~ serveur_forgejo_http_port | string) if (serveur_forgejo_tls and serveur_forgejo_http_port | int != 443) else '' }}/
|
2026-07-03 15:52:38 -04:00
|
|
|
HTTP_ADDR = {{ serveur_forgejo_http_addr }}
|
2026-06-24 20:17:46 -04:00
|
|
|
HTTP_PORT = {{ serveur_forgejo_http_port }}
|
site : un SITE a bien un plan — et huit defauts que lui seul pouvait reveler
J'ai repete toute la journee qu'un SITE n'est pas un plan. C'etait faux : j'en
reconstruisais un morceau par morceau dans underlay.yml sans le nommer. Ce qui est vrai,
c'est qu'un site ne DERIVE pas — il declare ses adresses parce qu'il EST le terrain — et
ne passe donc pas par `instancier`. Mais ne pas deriver n'est pas ne pas avoir de plan.
SITE-Chezlepro/plan/{10-intrants,serveurs,applications}.yml ; underlay.yml ne garde que
la fabric. Ce qui est MESURE d'un cote, ce qui est VOULU de l'autre. Les groupes viennent
du registre des applications, les gardes ont suivi le plan (4 controles negatifs).
Huit defauts, tous invisibles chez un tenant parce qu'il a toujours tout :
- client_pki codait `infra-pki-01` EN DUR — vrai par coincidence de nomenclature ;
- aucun moyen pour un service non-root de lire la cle (Forgejo tourne en `git`) ;
- le certificat, PUBLIC par nature, restait en 0600 ;
- l'unite Forgejo n'avait pas d'ExecReload : un cert renouvele aurait ete servi perime ;
- le role ne savait pas servir TLS lui-meme (il y avait toujours un edge) ;
- le cert ne couvrait pas le nom de SERVICE, faute d'`expose:` ;
- les registres se chargeaient en tout-ou-rien : sans domaines.yml, plancher vide ;
- le flux declarait 3000 en dur.
Le message accusait presque toujours autre chose : le DNS quand c'etait un nom faux, une
permission de fichier quand c'etait un port privilegie, rien du tout quand le plancher
s'ecrivait vide.
ROOT_URL est gravee dans les URL de clonage : la forge sert desormais sur 443, avec
CAP_NET_BIND_SERVICE, et son URL n'a plus de port.
Verifie depuis le reseau : Verify return code 0, https://forge.genese.internal/ -> 200.
42 preuves vertes, flux coherents (34 roles, 92 flux).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:15:34 -04:00
|
|
|
{% if serveur_forgejo_tls %}
|
|
|
|
|
; TLS SERVI PAR LA FORGE ELLE-MEME : il n'y a pas d'edge devant elle. Le certificat est
|
|
|
|
|
; celui de la MACHINE, pose et renouvele par `client_pki` depuis l'AC interne.
|
|
|
|
|
PROTOCOL = https
|
|
|
|
|
CERT_FILE = {{ serveur_forgejo_tls_cert }}
|
|
|
|
|
KEY_FILE = {{ serveur_forgejo_tls_cle }}
|
|
|
|
|
{% endif %}
|
2026-06-24 20:17:46 -04:00
|
|
|
SSH_DOMAIN = {{ serveur_forgejo_hostname }}
|
|
|
|
|
|
|
|
|
|
[database]
|
forgejo : apprendre SQLite, et retirer ce qui ne servait pas
Doute de l'exploitant sur patient 0 : « je doute de la pertinence de pgsql ». Mesure
plutot que discussion.
REDIS NE SERVAIT A RIEN : le role serveur_forgejo ne le mentionne ni dans son app.ini, ni
dans ses defauts, et ne declare aucun lien. Heritage du modele `forge`. Retire du plan.
POSTGRESQL ETAIT EXIGE PAR LE ROLE : DB_TYPE = postgres en dur, resoudre_base sans
condition. Le doute etait fonde, le moteur ne savait pas faire autrement.
INTERRUPTEUR `serveur_forgejo_bd: postgres|sqlite`. En sqlite la base devient un FICHIER
sous serveur_forgejo_data. Ce que ca change ailleurs : rien. Le job de sauvegarde
`serveur_forgejo` emporte deja ce dossier ; PGSSLROOTCERT etait deja conditionne au mode
TLS ; et P35 lit desormais l'interrupteur (convention `<role>_bd`, group_vars de
l'instance puis defaut du role), donc n'attend aucune entree de registre. Une valeur
inconnue est REFUSEE au debut du role plutot que de retomber en silence sur PostgreSQL.
PATIENT 0 PASSE DE SIX A QUATRE MACHINES (Dovecot, Redis, PostgreSQL et sa VM). Sur la
machine dont tout descend, chaque service en moins est une chose de moins a defendre, a
sauvegarder et a rebatir. Et l'effet depasse patient 0 : une offre `forge` pour un petit
organisme cesse d'exiger une VM PostgreSQL.
LA NEUVIEME. En verifiant P35 sur patient 0, elle a rendu un verdict JUSTE SUR LE MAUVAIS
ECOSYSTEME : `plan = RACINE / "instance" / "plan"`, le symlink en dur. Neuvieme resolution
d'instance codee en dur en cinq jours. Ce n'est plus une serie de bogues, c'est une piece
manquante : une resolution unique et partagee, a faire en une fois et de tete reposee.
Enseigne : SQLite au glossaire (P39 l'exige desormais), et le README du role documente
l'interrupteur et ce qu'il ne change pas.
make verifier 40/40 ; make ci 40/40 ; lint et syntaxe du role verts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 13:10:05 -04:00
|
|
|
{% if serveur_forgejo_bd == 'sqlite' %}
|
|
|
|
|
; Un FICHIER, pas un serveur. Il vit dans les donnees de Forgejo, donc la sauvegarde
|
|
|
|
|
; existante l'emporte deja — rien de special a prevoir.
|
|
|
|
|
DB_TYPE = sqlite3
|
|
|
|
|
PATH = {{ serveur_forgejo_db_chemin }}
|
|
|
|
|
{% else %}
|
2026-06-24 20:17:46 -04:00
|
|
|
DB_TYPE = postgres
|
|
|
|
|
HOST = {{ serveur_forgejo_db_host }}:{{ serveur_forgejo_db_port | default(5432) }}
|
|
|
|
|
NAME = {{ serveur_forgejo_entree.base }}
|
|
|
|
|
USER = {{ serveur_forgejo_entree.proprietaire }}
|
|
|
|
|
PASSWD = {{ serveur_forgejo_db_password }}
|
2026-07-04 18:53:31 -04:00
|
|
|
SSL_MODE = {{ serveur_forgejo_db_sslmode | default('disable') }}
|
forgejo : apprendre SQLite, et retirer ce qui ne servait pas
Doute de l'exploitant sur patient 0 : « je doute de la pertinence de pgsql ». Mesure
plutot que discussion.
REDIS NE SERVAIT A RIEN : le role serveur_forgejo ne le mentionne ni dans son app.ini, ni
dans ses defauts, et ne declare aucun lien. Heritage du modele `forge`. Retire du plan.
POSTGRESQL ETAIT EXIGE PAR LE ROLE : DB_TYPE = postgres en dur, resoudre_base sans
condition. Le doute etait fonde, le moteur ne savait pas faire autrement.
INTERRUPTEUR `serveur_forgejo_bd: postgres|sqlite`. En sqlite la base devient un FICHIER
sous serveur_forgejo_data. Ce que ca change ailleurs : rien. Le job de sauvegarde
`serveur_forgejo` emporte deja ce dossier ; PGSSLROOTCERT etait deja conditionne au mode
TLS ; et P35 lit desormais l'interrupteur (convention `<role>_bd`, group_vars de
l'instance puis defaut du role), donc n'attend aucune entree de registre. Une valeur
inconnue est REFUSEE au debut du role plutot que de retomber en silence sur PostgreSQL.
PATIENT 0 PASSE DE SIX A QUATRE MACHINES (Dovecot, Redis, PostgreSQL et sa VM). Sur la
machine dont tout descend, chaque service en moins est une chose de moins a defendre, a
sauvegarder et a rebatir. Et l'effet depasse patient 0 : une offre `forge` pour un petit
organisme cesse d'exiger une VM PostgreSQL.
LA NEUVIEME. En verifiant P35 sur patient 0, elle a rendu un verdict JUSTE SUR LE MAUVAIS
ECOSYSTEME : `plan = RACINE / "instance" / "plan"`, le symlink en dur. Neuvieme resolution
d'instance codee en dur en cinq jours. Ce n'est plus une serie de bogues, c'est une piece
manquante : une resolution unique et partagee, a faire en une fois et de tete reposee.
Enseigne : SQLite au glossaire (P39 l'exige desormais), et le README du role documente
l'interrupteur et ce qu'il ne change pas.
make verifier 40/40 ; make ci 40/40 ; lint et syntaxe du role verts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 13:10:05 -04:00
|
|
|
{% endif %}
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
[security]
|
|
|
|
|
INSTALL_LOCK = true
|
|
|
|
|
SECRET_KEY = {{ serveur_forgejo_secret_key }}
|
|
|
|
|
INTERNAL_TOKEN = {{ serveur_forgejo_internal_token }}
|
|
|
|
|
|
|
|
|
|
[service]
|
2026-07-03 15:52:38 -04:00
|
|
|
{% if serveur_forgejo_oidc_actif %}
|
|
|
|
|
; Identites depuis l'annuaire via le SSO uniquement : pas d'inscription locale,
|
|
|
|
|
; mais auto-enregistrement des utilisateurs OIDC.
|
|
|
|
|
DISABLE_REGISTRATION = false
|
|
|
|
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = true
|
authentification : SSO Keycloak devant, secours par sudo, formulaire local fermé
Directive : toute authentification web passe par Keycloak, LDAP est la source
unique des comptes, chaque service garde un accès de secours par sudo sur
l'hôte. Les trois sont indissociables — la chaîne service → Keycloak → LDAP est
en série, donc sans secours une panne exclut tout le monde, y compris pour
réparer. Portée : le web seulement ; IMAP/SMTP se lient à LDAP directement et
SSH est en clé seule.
Posture <rôle>_connexion_locale, false par défaut. Le compte local existe — il
ne peut pas dépendre de Keycloak — mais son formulaire n'est plus proposé au
repos : ouvert en permanence, il contourne la politique de mot de passe, le MFA
et surtout la révocation centrale.
Vérifié auprès de l'amont, puis par rendu réel des gabarits dans les deux
postures :
- Grafana GF_AUTH_DISABLE_LOGIN_FORM → ferme ;
- Forgejo ENABLE_INTERNAL_SIGNIN + ENABLE_BASIC_AUTHENTICATION → ferme, API
Basic comprise. N'existe que depuis la v10 (ticket amont 7476) ; le rôle
épingle 10.0.0 et un assert refuse la fermeture en deçà, car le réglage serait
ignoré sans erreur ;
- Nextcloud hide_login_form → MASQUE seulement : ?direct=1 reste le chemin de
secours documenté par l'amont. Écrit comme tel, sans prétendre à l'équivalence.
Défaut attrapé par le rendu : la condition Forgejo sans `| bool` n'émettait rien
dans aucune posture — une valeur en chaîne est vraie au sens Jinja, la connexion
locale serait restée ouverte en silence.
docs/authentification.md, décisions D-38 à D-41. ansible-lint (production) sans
échec, 28 preuves OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 16:50:36 -04:00
|
|
|
{# `| bool` obligatoire : sans lui, une valeur transmise en CHAINE (« false » via
|
|
|
|
|
`-e`, ou un group_vars non typé) est vraie au sens Jinja et le bloc ne sort JAMAIS —
|
|
|
|
|
la connexion locale resterait ouverte en silence. Verifie par rendu reel. #}
|
|
|
|
|
{% if not serveur_forgejo_connexion_locale | bool %}
|
|
|
|
|
; Connexion interne fermee : le SSO est le seul chemin. Le compte local
|
|
|
|
|
; survit et se rattrape par `forgejo admin user change-password` (sudo).
|
|
|
|
|
ENABLE_INTERNAL_SIGNIN = false
|
|
|
|
|
ENABLE_BASIC_AUTHENTICATION = false
|
|
|
|
|
{% endif %}
|
2026-07-03 15:52:38 -04:00
|
|
|
{% else %}
|
2026-06-24 20:17:46 -04:00
|
|
|
DISABLE_REGISTRATION = true
|
2026-07-03 15:52:38 -04:00
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
{% if serveur_forgejo_oidc_actif %}
|
|
|
|
|
[oauth2_client]
|
|
|
|
|
ENABLE_AUTO_REGISTRATION = true
|
|
|
|
|
USERNAME = preferred_username
|
|
|
|
|
ACCOUNT_LINKING = auto
|
|
|
|
|
{% endif %}
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
[mailer]
|
|
|
|
|
ENABLED = true
|
|
|
|
|
PROTOCOL = smtp
|
|
|
|
|
SMTP_ADDR = {{ serveur_forgejo_smtp }}
|
|
|
|
|
SMTP_PORT = 25
|
|
|
|
|
FROM = forge@{{ domaine_interne }}
|
2026-07-04 15:47:39 -04:00
|
|
|
|
|
|
|
|
[ui]
|
|
|
|
|
DEFAULT_THEME = {{ serveur_forgejo_theme }}
|
|
|
|
|
|
|
|
|
|
[ui.meta]
|
|
|
|
|
DESCRIPTION = {{ serveur_forgejo_meta_description }}
|
2026-08-09 14:44:03 -04:00
|
|
|
|
|
|
|
|
; --- Secret JWT : appartient au SERVICE, relu par le role -------------------------
|
|
|
|
|
; Forgejo genere ce secret au premier demarrage et l'ajoute lui-meme a la fin du
|
|
|
|
|
; fichier. Le gabarit ne le portait pas : chaque rendu l'EFFACAIT donc, Forgejo en
|
|
|
|
|
; generait un nouveau au redemarrage, et le passage suivant recommencait — autrement dit,
|
|
|
|
|
; chaque deploiement faisait tourner le secret JWT de la forge et invalidait les jetons
|
|
|
|
|
; qu'elle avait emis. Trouve par le passage d'idempotence du 2026-08-09 : seule ligne
|
|
|
|
|
; differente entre deux rendus.
|
|
|
|
|
;
|
|
|
|
|
; Le role le RELIT avant de rendre (tache « Relire le secret JWT ») et le repose ici.
|
|
|
|
|
; Vide au premier deploiement : Forgejo le genere alors, et les rendus suivants le
|
|
|
|
|
; conservent.
|
|
|
|
|
[oauth2]
|
|
|
|
|
{% if serveur_forgejo_jwt_secret | default("") %}
|
|
|
|
|
JWT_SECRET = {{ serveur_forgejo_jwt_secret }}
|
|
|
|
|
{% endif %}
|
|
|
|
|
|