Compose-Vorlage, EspoCRM-Grundeinstellungen und Render-Skript
Die Vorlage ist fuer alle Kunden identisch und traegt genau einen Platzhalter (__FQDN__); bin/espo-compose-rendern setzt ihn ein und schreibt /srv/espo/docker-compose.yml. Keine .env mehr: alles ausser den Geheimnissen steht in der Compose selbst. Inhalt der Vorlage: Bind-Mounts unter /srv/espo, Datei-Secrets fuer die beiden MariaDB-Passwoerter, Record-IDs als uuid4/36 ueber configs (in allen drei EspoCRM-Diensten, weil configs nicht ueber volumes_from vererbt werden), adminUpgradeDisabled, WebSocket ueber wss://<fqdn>/wss, Healthchecks mit Startfenstern fuer den Migrationslauf. ESPO-02 setzt keine abweichende Bindeadresse mehr - die Ports muessen fuer den Reverse Proxy erreichbar sein. Stattdessen beschraenkt eine DOCKER-USER-Regel den Zugang auf das eigene Subnetz, das die VM selbst ermittelt; eine systemd-Unit setzt sie nach jedem Docker-Start neu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
81d86ba363
commit
996490075a
12
README.md
12
README.md
@ -8,6 +8,7 @@ es steht deshalb **kein Passwort, kein Token und keine Kundenangabe** in diesem
|
|||||||
| `autoinstall/` | NoCloud-Seed für die Basis-VM (`user-data`, `meta-data`) |
|
| `autoinstall/` | NoCloud-Seed für die Basis-VM (`user-data`, `meta-data`) |
|
||||||
| `rmm-tasks/` | Skripte, die als Task im ConnectWise RMM hinterlegt sind (Quelle für Änderungen) |
|
| `rmm-tasks/` | Skripte, die als Task im ConnectWise RMM hinterlegt sind (Quelle für Änderungen) |
|
||||||
| `bin/` | Skripte, die auf der VM unter `/srv/espo/bin/` liegen und von Tasks aufgerufen werden |
|
| `bin/` | Skripte, die auf der VM unter `/srv/espo/bin/` liegen und von Tasks aufgerufen werden |
|
||||||
|
| `compose/` | Compose-Vorlage der Kundeninstanz und die EspoCRM-Grundeinstellungen (`config-override.php`) |
|
||||||
|
|
||||||
Konzept, Entscheidungen und Runbooks liegen im internen Repository `espoCRM/espo-fleet-ops`.
|
Konzept, Entscheidungen und Runbooks liegen im internen Repository `espoCRM/espo-fleet-ops`.
|
||||||
|
|
||||||
@ -19,7 +20,14 @@ autoinstall ds=nocloud-net\;s=https://gitea.subcura.net/EspoCRM_Public/espo-depl
|
|||||||
|
|
||||||
Das Semikolon muss maskiert werden – in GRUB trennt ein unmaskiertes `;` zwei Befehle.
|
Das Semikolon muss maskiert werden – in GRUB trennt ein unmaskiertes `;` zwei Befehle.
|
||||||
|
|
||||||
|
## Compose
|
||||||
|
|
||||||
|
`compose/docker-compose.vorlage.yml` ist für alle Kunden identisch und enthält genau einen Platzhalter: `__FQDN__`.
|
||||||
|
`bin/espo-compose-rendern <fqdn>` setzt ihn ein und schreibt `/srv/espo/docker-compose.yml`. Diese Datei auf der VM ist
|
||||||
|
ein **Erzeugnis** – sie wird bei jedem Lauf neu geschrieben, Änderungen von Hand gehen verloren. Geändert wird die
|
||||||
|
Vorlage hier im Repository.
|
||||||
|
|
||||||
## Kundenspezifisches
|
## Kundenspezifisches
|
||||||
|
|
||||||
Gehört **nicht** hierher, sondern in die Custom Fields des RMM (FQDN, Ring, Wartungsfenster) und in die `.env` auf der
|
Gehört **nicht** hierher, sondern in die Custom Fields des RMM (FQDN, Ring, Wartungsfenster). Geheimnisse entstehen auf
|
||||||
jeweiligen VM. Geheimnisse entstehen auf der VM und verlassen sie nicht.
|
der VM (`/srv/espo/secrets/`, 0600) und verlassen sie nicht.
|
||||||
|
|||||||
57
bin/espo-compose-rendern
Executable file
57
bin/espo-compose-rendern
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# espo-compose-rendern - erzeugt /srv/espo/docker-compose.yml aus der versionierten Vorlage.
|
||||||
|
# Einziger kundenspezifischer Wert ist der FQDN; er kommt aus dem Custom Field espo_fqdn.
|
||||||
|
# Aufruf: espo-compose-rendern <fqdn> (von ESPO-10 und ESPO-30)
|
||||||
|
# Idempotent: gleiche Eingabe, gleiche Datei; geschrieben wird nur bei Unterschied.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
VORLAGE_URL=https://gitea.subcura.net/EspoCRM_Public/espo-deploy/raw/branch/main/compose/docker-compose.vorlage.yml
|
||||||
|
ZIEL=/srv/espo/docker-compose.yml
|
||||||
|
FQDN="${1:-}"
|
||||||
|
|
||||||
|
# 1. Ohne gueltigen FQDN waeren Site-URL und WebSocket-Adresse falsch - hier abbrechen, nicht spaeter raten.
|
||||||
|
if ! printf '%s' "$FQDN" | grep -Eq '^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$'; then
|
||||||
|
echo "FEHLER: kein gueltiger FQDN uebergeben ('$FQDN')"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: FEHLER"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Vorlage holen - geheimnisfrei und ohne Anmeldung abrufbar.
|
||||||
|
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
|
||||||
|
curl -fsSL "$VORLAGE_URL" -o "$TMP/vorlage.yml" || {
|
||||||
|
echo "FEHLER: Vorlage nicht abrufbar ($VORLAGE_URL)"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: FEHLER"; exit 1; }
|
||||||
|
|
||||||
|
# 3. Platzhalter ersetzen.
|
||||||
|
sed "s|__FQDN__|${FQDN}|g" "$TMP/vorlage.yml" > "$TMP/neu.yml"
|
||||||
|
|
||||||
|
# 4. Kein Platzhalter darf uebrig bleiben, sonst startete Docker mit einer unfertigen Datei.
|
||||||
|
OFFEN="$(grep -oE '__[A-Z][A-Z0-9_]*__' "$TMP/neu.yml" | sort -u | tr '\n' ' ')"
|
||||||
|
if [ -n "$OFFEN" ]; then
|
||||||
|
echo "FEHLER: unersetzte Platzhalter: $OFFEN"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: FEHLER"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Die drei EspoCRM-Dienste teilen ueber volumes_from denselben Code - verschiedene Images waeren ein Fehler.
|
||||||
|
if [ "$(grep -E '^[[:space:]]+image: espocrm' "$TMP/neu.yml" | sort -u | wc -l)" -ne 1 ]; then
|
||||||
|
echo "FEHLER: die EspoCRM-Dienste verwenden verschiedene Images"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: FEHLER"; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. Nur bei Unterschied schreiben, damit wiederholte Laeufe nichts anfassen.
|
||||||
|
install -d -m 0755 /srv/espo
|
||||||
|
if [ -f "$ZIEL" ] && cmp -s "$TMP/neu.yml" "$ZIEL"; then
|
||||||
|
STATUS=UNVERAENDERT
|
||||||
|
else
|
||||||
|
install -m 0644 "$TMP/neu.yml" "$ZIEL"
|
||||||
|
STATUS=GEAENDERT
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. Docker selbst pruefen lassen, sobald die Geheimnisdateien liegen - startet nichts.
|
||||||
|
if [ -r /srv/espo/secrets/db_password ]; then
|
||||||
|
docker compose -f "$ZIEL" config -q || {
|
||||||
|
echo "FEHLER: Compose-Datei nicht gueltig"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: FEHLER"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$ZIEL $STATUS (FQDN $FQDN)"
|
||||||
|
echo "ESPO_COMPOSE_ERGEBNIS: OK"
|
||||||
67
compose/config-override.php
Normal file
67
compose/config-override.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subcura – Grundeinstellungen für EspoCRM-Instanzen (deutscher Markt), unabhängig von Extensions.
|
||||||
|
*
|
||||||
|
* Mechanismus: EspoCRMs eigene data/config-override.php (ab v8.2) – wird nach config.php und config-internal.php geladen,
|
||||||
|
* überschreibt nur die hier gesetzten Schlüssel und wird von der Anwendung nie geschrieben (Änderungen an diesen
|
||||||
|
* Parametern über die Admin-UI bleiben wirkungslos). getenv() ist erlaubt.
|
||||||
|
* Anwendung: Kunden-VM: ESPO-10 legt die Datei vor dem ersten Start nach /srv/espo/data/config-override.php
|
||||||
|
* (Bind-Mount des EspoCRM-data-Verzeichnisses); danach Cache leeren.
|
||||||
|
* Entwicklungs- und Integrationsinstanzen: siehe espo-dev-env, Abschnitt 10.
|
||||||
|
* Nicht hier: instanzspezifische Werte (siteUrl, Datenbank, Mail-/SMTP-Daten, LDAP-Server, version).
|
||||||
|
* Geprüft gegen EspoCRM 10.0.8 (Settings.json / defaults/config.php).
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
// Sprache, Zeit, Zahlen
|
||||||
|
'language' => 'de_DE',
|
||||||
|
'timeZone' => 'Europe/Berlin',
|
||||||
|
'dateFormat' => 'DD.MM.YYYY',
|
||||||
|
'timeFormat' => 'HH:mm',
|
||||||
|
'weekStart' => 1, // Montag
|
||||||
|
'fiscalYearShift' => 0, // Geschäftsjahr = Kalenderjahr
|
||||||
|
'thousandSeparator' => '.',
|
||||||
|
'decimalMark' => ',',
|
||||||
|
|
||||||
|
// Währung
|
||||||
|
'currencyList' => ['EUR'],
|
||||||
|
'defaultCurrency' => 'EUR',
|
||||||
|
'baseCurrency' => 'EUR',
|
||||||
|
'currencyFormat' => 3, // "1.000,00 €" (Symbol nach dem Betrag)
|
||||||
|
'currencyDecimalPlaces' => 2,
|
||||||
|
|
||||||
|
// Adressen und Telefon
|
||||||
|
'addressFormat' => 2, // Straße / PLZ Ort / Land
|
||||||
|
'addressStateList' => [
|
||||||
|
'Baden-Württemberg', 'Bayern', 'Berlin', 'Brandenburg', 'Bremen', 'Hamburg', 'Hessen',
|
||||||
|
'Mecklenburg-Vorpommern', 'Niedersachsen', 'Nordrhein-Westfalen', 'Rheinland-Pfalz', 'Saarland',
|
||||||
|
'Sachsen', 'Sachsen-Anhalt', 'Schleswig-Holstein', 'Thüringen',
|
||||||
|
],
|
||||||
|
'addressCityList' => [],
|
||||||
|
'phoneNumberPreferredCountryList' => ['de'],
|
||||||
|
'phoneNumberInternational' => true, // E.164-Speicherung/-Validierung
|
||||||
|
|
||||||
|
// Listen
|
||||||
|
'recordsPerPage' => 50,
|
||||||
|
'recordsPerPageSmall' => 10,
|
||||||
|
'recordsPerPageSelect' => 20,
|
||||||
|
'recordsPerPageKanban' => 10,
|
||||||
|
|
||||||
|
// E-Mail und Export
|
||||||
|
'emailMessageMaxSize' => 25, // MB
|
||||||
|
'exportDelimiter' => ';', // CSV-Export für deutsches Excel
|
||||||
|
|
||||||
|
// Sicherheit (betrifft Espo-Passwörter: Admins, API-Benutzer; bei LDAP-Kunden die Nicht-LDAP-Konten)
|
||||||
|
'passwordStrengthLength' => 12,
|
||||||
|
'passwordStrengthBothCases' => true,
|
||||||
|
'passwordStrengthNumberCount' => 1,
|
||||||
|
'passwordStrengthSpecialCharacterCount' => 1,
|
||||||
|
'passwordGenerateLength' => 16,
|
||||||
|
'passwordRecoveryNoExposure' => true, // Passwort-Reset verrät nicht, ob eine Adresse existiert
|
||||||
|
'auth2FA' => true, // 2FA anbieten (nicht erzwingen: auth2FAForced)
|
||||||
|
|
||||||
|
// Authentifizierung: 'LDAP' nur zusammen mit den ldap*-Parametern der Kundeninstanz setzen
|
||||||
|
// (ldapHost, ldapPort, ldapSecurity, ldapUsername, ldapPassword, ldapBaseDn, ldapUserLoginFilter, ...).
|
||||||
|
// Ohne erreichbaren LDAP-Server können sich nur noch Admins anmelden. Dev-/Integrationsinstanz: 'Espo'.
|
||||||
|
// 'authenticationMethod' => 'LDAP',
|
||||||
|
];
|
||||||
128
compose/docker-compose.vorlage.yml
Normal file
128
compose/docker-compose.vorlage.yml
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Vorlage fuer die Kunden-VM. Einziger kundenspezifischer Wert: __FQDN__.
|
||||||
|
# Gerendert von bin/espo-compose-rendern nach /srv/espo/docker-compose.yml.
|
||||||
|
# Aenderungen ausschliesslich hier im Repository, nie auf der VM.
|
||||||
|
name: espocrm
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
espocrm-db:
|
||||||
|
image: mariadb:12.3
|
||||||
|
container_name: espocrm-db
|
||||||
|
restart: unless-stopped
|
||||||
|
stop_grace_period: 120s
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
environment:
|
||||||
|
MARIADB_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
|
||||||
|
MARIADB_DATABASE: espocrm
|
||||||
|
MARIADB_USER: espocrm
|
||||||
|
MARIADB_PASSWORD_FILE: /run/secrets/db_password
|
||||||
|
MARIADB_AUTO_UPGRADE: "1"
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
secrets: [db_root_password, db_password]
|
||||||
|
volumes:
|
||||||
|
- /srv/espo/mariadb:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||||
|
interval: 20s
|
||||||
|
start_period: 60s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
espocrm:
|
||||||
|
image: espocrm:10.0.8
|
||||||
|
container_name: espocrm
|
||||||
|
restart: unless-stopped
|
||||||
|
stop_grace_period: 30s
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
depends_on:
|
||||||
|
espocrm-db:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
ESPOCRM_DATABASE_PLATFORM: Mysql
|
||||||
|
ESPOCRM_DATABASE_HOST: espocrm-db
|
||||||
|
ESPOCRM_DATABASE_NAME: espocrm
|
||||||
|
ESPOCRM_DATABASE_USER: espocrm
|
||||||
|
ESPOCRM_DATABASE_PASSWORD_FILE: /run/secrets/db_password
|
||||||
|
ESPOCRM_ADMIN_USERNAME: admin
|
||||||
|
ESPOCRM_ADMIN_PASSWORD: admin
|
||||||
|
ESPOCRM_SITE_URL: https://__FQDN__
|
||||||
|
ESPOCRM_CONFIG_SITE_URL: https://__FQDN__
|
||||||
|
ESPOCRM_CONFIG_ADMIN_UPGRADE_DISABLED: "true"
|
||||||
|
ESPOCRM_CONFIG_USE_WEB_SOCKET: "true"
|
||||||
|
ESPOCRM_CONFIG_WEB_SOCKET_URL: wss://__FQDN__/wss
|
||||||
|
ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBSCRIBER_DSN: "tcp://*:7777"
|
||||||
|
ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBMISSION_DSN: tcp://espocrm-websocket:7777
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
secrets: [db_password]
|
||||||
|
configs:
|
||||||
|
- source: recordId
|
||||||
|
target: /var/www/html/custom/Espo/Custom/Resources/metadata/app/recordId.json
|
||||||
|
volumes:
|
||||||
|
- /srv/espo/data:/var/www/html/data
|
||||||
|
- /srv/espo/custom:/var/www/html/custom
|
||||||
|
- /srv/espo/client-custom:/var/www/html/client/custom
|
||||||
|
ports:
|
||||||
|
- 8080:80
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "bin/command", "app-check"]
|
||||||
|
start_period: 300s
|
||||||
|
interval: 60s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
espocrm-daemon:
|
||||||
|
image: espocrm:10.0.8
|
||||||
|
container_name: espocrm-daemon
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: docker-daemon.sh
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
volumes_from: [espocrm]
|
||||||
|
configs:
|
||||||
|
- source: recordId
|
||||||
|
target: /var/www/html/custom/Espo/Custom/Resources/metadata/app/recordId.json
|
||||||
|
depends_on:
|
||||||
|
espocrm:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "bin/command", "app-check"]
|
||||||
|
start_period: 60s
|
||||||
|
interval: 180s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
espocrm-websocket:
|
||||||
|
image: espocrm:10.0.8
|
||||||
|
container_name: espocrm-websocket
|
||||||
|
restart: unless-stopped
|
||||||
|
entrypoint: docker-websocket.sh
|
||||||
|
security_opt: ["no-new-privileges:true"]
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
volumes_from: [espocrm]
|
||||||
|
configs:
|
||||||
|
- source: recordId
|
||||||
|
target: /var/www/html/custom/Espo/Custom/Resources/metadata/app/recordId.json
|
||||||
|
depends_on:
|
||||||
|
espocrm:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- 8081:8080
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "bin/command", "app-check"]
|
||||||
|
start_period: 60s
|
||||||
|
interval: 180s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
db_root_password:
|
||||||
|
file: /srv/espo/secrets/db_root_password
|
||||||
|
db_password:
|
||||||
|
file: /srv/espo/secrets/db_password
|
||||||
|
|
||||||
|
configs:
|
||||||
|
recordId:
|
||||||
|
content: |
|
||||||
|
{"type": "uuid4", "length": 36}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ESPO-02-Docker – installiert Docker in gepinnter Version und setzt die Daemon-Grundlagen.
|
# ESPO-02-Docker - installiert Docker in gepinnter Version, setzt die Daemon-Grundlagen und
|
||||||
|
# beschraenkt den Zugang zu veroeffentlichten Container-Ports auf das eigene Subnetz.
|
||||||
# Laeuft als root ueber einen RMM-Bash-Task, idempotent.
|
# Laeuft als root ueber einen RMM-Bash-Task, idempotent.
|
||||||
# Firewallregeln (ufw, DOCKER-USER) kommen in einem eigenen Task, weil dafuer die Adresse
|
|
||||||
# der OPNsense als Parameter gebraucht wird.
|
|
||||||
set -u
|
set -u
|
||||||
FEHLER=0
|
FEHLER=0
|
||||||
|
|
||||||
@ -34,24 +33,64 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|||||||
docker-compose-plugin="$COMPOSE_VERSION" docker-buildx-plugin="$BUILDX_VERSION" || { echo "FEHLER: Installation"; exit 1; }
|
docker-compose-plugin="$COMPOSE_VERSION" docker-buildx-plugin="$BUILDX_VERSION" || { echo "FEHLER: Installation"; exit 1; }
|
||||||
apt-mark hold docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin >/dev/null
|
apt-mark hold docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin >/dev/null
|
||||||
|
|
||||||
# 3. Daemon-Grundlagen: Logrotation (der Standardtreiber rotiert nicht) und veroeffentlichte
|
# 3. Daemon-Grundlagen: Logrotation, weil der Standardtreiber nicht rotiert.
|
||||||
# Ports ohne Adressangabe nur auf die Loopback-Adresse binden.
|
# Keine abweichende Bindeadresse: die Ports muessen fuer den Reverse Proxy erreichbar sein,
|
||||||
install -d -m 755 /etc/docker
|
# die Beschraenkung uebernimmt Schritt 4.
|
||||||
|
install -d -m 0755 /etc/docker
|
||||||
cat > /etc/docker/daemon.json <<'CONF'
|
cat > /etc/docker/daemon.json <<'CONF'
|
||||||
{
|
{
|
||||||
"log-driver": "local",
|
"log-driver": "local",
|
||||||
"log-opts": { "max-size": "20m", "max-file": "5" },
|
"log-opts": { "max-size": "20m", "max-file": "5" },
|
||||||
"ip": "127.0.0.1",
|
|
||||||
"live-restore": true
|
"live-restore": true
|
||||||
}
|
}
|
||||||
CONF
|
CONF
|
||||||
systemctl enable --now docker >/dev/null 2>&1
|
systemctl enable --now docker >/dev/null 2>&1
|
||||||
systemctl restart docker || { echo "FEHLER: docker startet nicht"; exit 1; }
|
systemctl restart docker || { echo "FEHLER: docker startet nicht"; exit 1; }
|
||||||
|
|
||||||
# 4. Ergebnis zeigen und pruefen.
|
# 4. Firewall: Docker setzt seine Weiterleitungsregeln vor die INPUT-Kette und umgeht damit ufw.
|
||||||
|
# DOCKER-USER ist die dafuer vorgesehene Stelle. Das Subnetz ermittelt die VM selbst,
|
||||||
|
# es ist also kein kundenspezifischer Wert noetig.
|
||||||
|
command -v iptables >/dev/null || { echo "FEHLER: iptables fehlt"; exit 1; }
|
||||||
|
cat > /usr/local/sbin/espo-docker-firewall <<'CONF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Laesst an veroeffentlichte Container-Ports nur das eigene Subnetz heran, verwirft den Rest.
|
||||||
|
set -u
|
||||||
|
IFACE="$(ip -4 route show default | awk '{print $5; exit}')"
|
||||||
|
SUBNETZ="$(ip -4 route show dev "$IFACE" proto kernel scope link | awk '{print $1; exit}')"
|
||||||
|
[ -n "$IFACE" ] && [ -n "$SUBNETZ" ] || { echo "FEHLER: Schnittstelle oder Subnetz nicht ermittelbar"; exit 1; }
|
||||||
|
iptables -F DOCKER-USER
|
||||||
|
iptables -A DOCKER-USER -i "$IFACE" -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
|
||||||
|
iptables -A DOCKER-USER -i "$IFACE" -s "$SUBNETZ" -j RETURN
|
||||||
|
iptables -A DOCKER-USER -i "$IFACE" -j DROP
|
||||||
|
iptables -A DOCKER-USER -j RETURN
|
||||||
|
echo "DOCKER-USER: nur $SUBNETZ ueber $IFACE"
|
||||||
|
CONF
|
||||||
|
chmod 0755 /usr/local/sbin/espo-docker-firewall
|
||||||
|
cat > /etc/systemd/system/espo-docker-firewall.service <<'CONF'
|
||||||
|
[Unit]
|
||||||
|
Description=Zugang zu veroeffentlichten Docker-Ports auf das eigene Subnetz beschraenken
|
||||||
|
After=docker.service
|
||||||
|
Requires=docker.service
|
||||||
|
PartOf=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/usr/local/sbin/espo-docker-firewall
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
CONF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable espo-docker-firewall.service >/dev/null 2>&1
|
||||||
|
systemctl restart espo-docker-firewall.service || { echo "FEHLER: Firewallregeln nicht gesetzt"; FEHLER=1; }
|
||||||
|
|
||||||
|
# 5. Ergebnis zeigen und pruefen.
|
||||||
docker --version
|
docker --version
|
||||||
docker compose version
|
docker compose version
|
||||||
docker info --format 'Logtreiber: {{.LoggingDriver}} Live-Restore: {{.LiveRestoreEnabled}}'
|
docker info --format 'Logtreiber: {{.LoggingDriver}} Live-Restore: {{.LiveRestoreEnabled}}'
|
||||||
|
iptables -S DOCKER-USER
|
||||||
[ "$(docker info --format '{{.LoggingDriver}}')" = "local" ] || { echo "FEHLER: Logtreiber nicht gesetzt"; FEHLER=1; }
|
[ "$(docker info --format '{{.LoggingDriver}}')" = "local" ] || { echo "FEHLER: Logtreiber nicht gesetzt"; FEHLER=1; }
|
||||||
|
iptables -S DOCKER-USER | grep -q -- '-j DROP' || { echo "FEHLER: DOCKER-USER ohne DROP-Regel"; FEHLER=1; }
|
||||||
|
|
||||||
if [ "$FEHLER" -eq 0 ]; then echo "ESPO02_ERGEBNIS: OK"; exit 0; else echo "ESPO02_ERGEBNIS: FEHLER"; exit 1; fi
|
if [ "$FEHLER" -eq 0 ]; then echo "ESPO02_ERGEBNIS: OK"; exit 0; else echo "ESPO02_ERGEBNIS: FEHLER"; exit 1; fi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user