Applikations-Konfiguration
Was webhost.md für die Webserver-Schicht beschreibt, deckt dieses Dokument für die einzelne App-Instanz ab — also alles, was eine Applikations-Klasse pro Host ausrollt.
Ressourcen pro App-Instanz
Eine Applikations-Klasse (z.B. iteasapps::iscan) erzeugt pro Instanz:
| Ressource | Beschreibung |
|---|---|
| Linux-System-User | Dedizierter Benutzer für die Isolation gegenüber anderen Apps auf demselben Host |
| Verzeichnis-Struktur | /var/www/<system_user>/{current,shared,releases} |
| PHP-FPM-Pool | Eigener Pool, läuft unter dem System-User, eigener Unix-Socket |
| Nginx-VHost | Server-Block-Konfiguration pro Applikation |
| MariaDB-Datenbank | Lokale Datenbank für die Applikation |
| MariaDB-Login (App) | Lokaler App-DB-Login auf localhost |
Lokale secrets.php | Materialisierte Secrets im config/-Subdirectory (siehe Secrets-Handling) |
| Fail2Ban-Jail | Pro VHost ein Jail-Set für Auth- und 4xx-Floods |
| Smoke-Test | Healthcheck-Aufruf gegen die Domain nach erfolgreichem Rollout |
Naming-Konvention
Da pro Host nur ein Kunde läuft, taucht die Kundenkennung in keinem Resource-Identifier auf — sie steckt im Hostnamen bzw. Puppet-Certname. Die Identifier setzen sich nur aus dem App-Namen zusammen.
Für eine iScan-Instanz:
| Ressource | Wert |
|---|---|
| App-Name | iscan |
| System-User (Linux) | f01_iscan |
| App-Verzeichnis | /var/www/f01_iscan/current/ |
| App-DB-Name | iscan |
| App-DB-Login (MariaDB) | iscan |
| Vault-Basis-Pfad | secret/iteasapps/iscan/ |
Alle Werte sind als Smart-Class-Parameter überschreibbar; die Defaults werden aus dem App-Namen abgeleitet.
Datenbank-Anbindung
Lokaler App-Login
Pro App-Instanz wird eine MariaDB-Datenbank angelegt, dazu ein App-Login mit vollen Rechten auf diese Datenbank. Verbindungs-Host ist localhost.
Das Passwort liegt in Vault unter secret/iteasapps/<app>/db_password und wird vom Puppet-Agent an zwei Stellen geschrieben: beim Anlegen des MariaDB-Logins und in die lokale secrets.php, aus der die App es zur Laufzeit liest.
Fehlt das Secret in Vault, scheitert ausschließlich die Login-Resource. Der Rest des Katalogs läuft durch; beim nächsten Agent-Run wird der Schritt wiederholt.
Eingehender Remote-Login: EDBS-Server
Der MariaDB-User m01_edbs wird über die Klasse iteas_services::edbs im Schwester-Modul verwaltet, nicht über die App-Klasse (siehe Foreman → EDBS-Anbindung). Sie wird einmal pro Host klassifiziert, weil der EDBS-Server pro Host nur eine MariaDB-Identität braucht — unabhängig davon, wie viele iScan- oder iTransfer-Instanzen am gleichen Host laufen.
Die Verbindung läuft von außen in den Host hinein. Die iScan-App stellt sie nicht her und liest die Zugangsdaten nicht — m01_edbs ist nur die Identität, mit der sich der externe EDBS-Server gegen die lokale MariaDB authentifiziert. Importierte Datensätze liest die App anschließend über den lokalen App-Login.
Konsequenzen:
- Der EDBS-Login steht nicht in den Smart-Class-Parametern der App-Klassen.
- Das Passwort liegt unter
secret/iteas_services/edbs/password, nicht untersecret/iteasapps/<app>/. iteas_services::edbswird einmal pro Host klassifiziert, unabhängig von den App-Klassen.
Namensräume
m01_edbs ist ausschließlich ein MariaDB-Login — kein gleichnamiger Linux-User. Konvention: f01_* für Linux, m01_* für MariaDB.
Vault-Pfade
Vault-Struktur pro Applikation:
secret/iteasapps/<app>/
├── db_password ← App-DB-Passwort
├── cookie_validation_key ← Yii-Cookie-Validation-Key
├── edbs_db_password ← Passwort für outbound iScan → Remote-EDBS
├── mailer_dsn ← SMTP-DSN für Standard-Mails (optional)
├── sp_mailer_dsn ← SMTP-DSN für Serviceportal-Mails (optional)
└── app_update_key ← Update-Key für die mobile App (optional)<app> ist der App-Name (z.B. iscan). Die Kundenzuordnung kommt aus der Hiera-Hierarchie über den Puppet-Certname — der Pfad enthält keine Kundenkennung.
| Pfad | Pflicht | Verwendung |
|---|---|---|
db_password | ja | Lokaler App-DB-Login (DB_PASSWORD) |
cookie_validation_key | ja | Yii-Cookie-Validation-Key (COOKIE_VALIDATION_KEY) |
edbs_db_password | ja | Outbound-Verbindung zur Remote-EDBS-DB (EDBS_DB_PASSWORD) |
mailer_dsn | nein | SMTP-DSN; ohne Eintrag greift der Code-Default |
sp_mailer_dsn | nein | dito für das Serviceportal |
app_update_key | nein | Update-Key der mobilen App; Code-Default als Fallback |
Das EDBS-Passwort für die eingehende Verbindung liegt unter secret/iteas_services/edbs/password (siehe Foreman → EDBS-Anbindung).
TLS-Material liegt nicht in Vault — der Stack nutzt Certbot/ACME, siehe Webhost → TLS-Material.
Secrets-Handling
Statt die App pro Request mit Vault sprechen zu lassen, schreibt der Puppet-Agent die Werte einmal pro Run lokal nach:
/var/www/<system_user>/current/config/secrets.phpIm native-Modus ersetzt diese Datei die Container-Environment-Variablen und enthält daher die gesamte App-Konfiguration — Secrets und nicht-sensitive Smart-Class-Parameter zusammen.
<?php
return [
// DB (lokal)
'DB_HOSTNAME' => 'localhost',
'DB_NAME' => 'iscan',
'DB_USER' => 'iscan',
'DB_PASSWORD' => '...', // aus Vault
// EDBS (outbound)
'EDBS_DB_HOST' => '...',
'EDBS_DB_NAME' => '...',
'EDBS_DB_USER' => '...',
'EDBS_DB_PASSWORD' => '...', // aus Vault
// Yii
'YII_ENVIRONMENT' => 'dev',
'YII_DEBUG' => 'true',
'COOKIE_VALIDATION_KEY' => '...', // aus Vault
// App
'CUSTOMER' => 'iteas',
'SENDER_MAIL_ADDRESS' => 'noreply@iscan.at',
'SP_SENDER_MAIL_ADDRESS' => 'noreply@opst.at',
'SP_USER_INVITE_VALID_HOUR' => '12',
'ENABLE_SCHEMA_CACHE' => 'true',
'MAILER_DSN' => '...', // aus Vault (oder Code-Default)
'SP_MAILER_DSN' => '...', // aus Vault (oder Code-Default)
'APP_UPDATE_KEY' => '...', // aus Vault (oder Code-Default)
// Logging (Graylog & Loki)
'ENABLE_LOGGING' => 'false',
'GRAYLOG_FACILITY' => 'iscan-dev',
'GRAYLOG_URL' => 'gelf.log.iteas.at',
'GRAYLOG_PORT' => '443',
'LOKI_ENABLE_LOGGING' => 'false',
'LOKI_API_URL' => 'https://api.log.iteas.cloud/loki/api/v1/push',
'LOKI_SERVICE_NAME' => 'iscan-web',
];Die Schlüssel entsprechen den Environment-Variablen aus dem docker-compose; im App-Code wird getenv('FOO') zu $config['FOO'].
Datei-Eigenschaften:
- Owner = System-User der App (z.B.
f01_iscan), Group dito, Mode0640. - Wird bei jedem Puppet-Run aus den aktuellen Vault-Werten regeneriert. Eine Rotation in Vault wirkt sich beim nächsten Agent-Run aus.
- Konsum durch die App via
require/includein der Yii-Bootstrap-Phase. Kein Vault-Aufruf zur Laufzeit. - Vom App-Repository per
.gitignoreausgeschlossen; einesecrets.php.exampledient als Strukturreferenz für die lokale Entwicklung.
Folge der lokalen Materialisierung: Vault liegt nicht auf dem Request-Pfad. Bei Vault-Ausfall läuft die App weiter, bis der nächste Puppet-Run scheitert.
Sensitive-Typisierung in Puppet
Aus Vault gelesene Passwörter werden als Sensitive[String] typisiert und an Defined Types weitergereicht:
class iteasapps::iscan (
# ...
) {
$db_password = Sensitive(lookup('iteasapps::iscan::db_password'))
iteasapps::sql::login { "${app_name}-app":
login_name => $db_user,
password => $db_password, # bleibt Sensitive bis zur mysql_user-Resource
# ...
}
}Puppet ersetzt solche Werte beim Catalog-Render durch die Sensitive-Maske, die im Foreman-Report, in der PuppetDB und in den Agent-Logs anstelle des Klartexts erscheint.
Im EPP-Template muss $password.unwrap aufgerufen werden, sonst landet die Maske als String im Output.
App-Code-Deployment
Bisher kopierte die GitLab-CI-Pipeline das Build-Artefakt per RSync direkt auf den Zielserver. Mit der Foreman-Automation übernimmt Puppet das Verteilen.
| Schritt | Verantwortlich | Beschreibung |
|---|---|---|
| Test | GitLab CI | Codeception-Tests, statische Analyse |
| Compile / Build | GitLab CI | Composer-Install, Asset-Build, ggf. Container-Build |
| Artefakt-Publikation | GitLab CI | Build-Artefakt landet in einem von Foreman erreichbaren Storage (z.B. GitLab Package Registry) |
| Deployment auf Host | Foreman + Puppet | App-Klasse zieht das Artefakt, entpackt es in /var/www/<system_user>/releases/<version>/, schaltet current/ um |
| Smoke-Test | Foreman + Puppet | iteasapps::healthcheck prüft die Erreichbarkeit |
Puppet kann einen Deployment-Lauf jederzeit erneut anstoßen, ohne dass die CI ein neues Build produzieren muss — z.B. nach einer Smart-Class-Parameter-Änderung.
Smoke-Test
Nach dem Rollout läuft iteasapps::healthcheck: HTTPS-Aufruf gegen die Domain, erwartet Status 200. Bei Fehlschlag ist der Puppet-Run rot; Foreman zeigt das im Report.
Mehrere Applikationen pro Host
Sind mehrere App-Klassen auf demselben Host klassifiziert (z.B. iteasapps::iscan und iteasapps::itransfer):
iteasapps::baseläuft einmal —includeist idempotent.- Jede App bekommt eigene System-User, FPM-Pools, VHosts und Datenbanken.
- Resource-Titel sind über den App-Namen disjunkt.
- Bei Domain-Kollisionen schlägt der Nginx-Reload fehl und damit der Puppet-Run.
Eine weitere App-Klasse auf einem bereits initialisierten Host erfordert kein Re-Provisioning — die Basis bleibt unangetastet (siehe Foreman → Ablauf einer Klassifizierung).
