Der Abgleich mit docs.docker.com/engine/install/ubuntu zeigte eine fehlende Vorstufe: Die Doku verlangt, docker.io, docker-compose, docker-doc, docker-buildx, podman-docker sowie containerd und runc zu entfernen, bevor installiert wird - containerd.io buendelt containerd und runc und steht mit beiden in Conflicts. Genau das passt zum Symptom "unerfuellte Abhaengigkeiten". Ausserdem wie in der Doku ca-certificates und curl vor dem Schluessel. Die deb822-Quelle entspricht bereits woertlich der Doku; beim Pinning bleiben wir strenger als sie (sie pinnt nur docker-ce und docker-ce-cli). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
121 lines
5.8 KiB
Bash
Executable File
121 lines
5.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# 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-Task, idempotent.
|
|
# Ob der Lauf erlaubt ist, entscheidet der Task selbst per If/Then auf espo_zustand.
|
|
set -u
|
|
FEHLER=0
|
|
|
|
# Gepinnte Staende - hier wird der Flottenstand gesetzt, nicht auf der VM entschieden.
|
|
DOCKER_VERSION="5:29.8.1-1~ubuntu.24.04~noble"
|
|
CONTAINERD_VERSION="2.3.5-1~ubuntu.24.04~noble"
|
|
COMPOSE_VERSION="5.5.1-1~ubuntu.24.04~noble"
|
|
BUILDX_VERSION="0.37.1-1~ubuntu.24.04~noble"
|
|
|
|
# 1. Konkurrierende Pakete entfernen. Die offizielle Doku verlangt das ausdruecklich, bevor
|
|
# installiert wird: containerd.io buendelt containerd und runc und steht mit beiden in Conflicts.
|
|
ALTPAKETE="$(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null | awk '$2 != "deinstall" {print $1}')"
|
|
if [ -n "$ALTPAKETE" ]; then
|
|
echo "Konkurrierende Pakete gefunden: $ALTPAKETE"
|
|
DEBIAN_FRONTEND=noninteractive apt-get remove -y -q $ALTPAKETE || { echo "FEHLER: konkurrierende Pakete nicht entfernbar"; echo "ESPO02_ERGEBNIS: FEHLER"; exit 1; }
|
|
else
|
|
echo "Konkurrierende Pakete: keine installiert"
|
|
fi
|
|
|
|
# 2. Paketquelle von Docker einrichten (Schluessel und deb822-Quelle), falls noch nicht vorhanden.
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -q ca-certificates curl >/dev/null
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
if [ ! -s /etc/apt/keyrings/docker.asc ]; then
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc || { echo "FEHLER: Schluessel nicht ladbar"; exit 1; }
|
|
chmod a+r /etc/apt/keyrings/docker.asc
|
|
fi
|
|
cat > /etc/apt/sources.list.d/docker.sources <<CONF
|
|
Types: deb
|
|
URIs: https://download.docker.com/linux/ubuntu
|
|
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
|
|
Components: stable
|
|
Architectures: $(dpkg --print-architecture)
|
|
Signed-By: /etc/apt/keyrings/docker.asc
|
|
CONF
|
|
apt-get update -qq || { echo "FEHLER: apt-get update"; exit 1; }
|
|
|
|
# 3. Genau diese Versionen installieren und auf Hold setzen, damit nichts unbemerkt nachzieht.
|
|
# Scheitert die Installation, sagt das Skript woran - eine blosse Fehlermeldung hilft niemandem weiter.
|
|
PAKETE="docker-ce=$DOCKER_VERSION docker-ce-cli=$DOCKER_VERSION containerd.io=$CONTAINERD_VERSION"
|
|
PAKETE="$PAKETE docker-compose-plugin=$COMPOSE_VERSION docker-buildx-plugin=$BUILDX_VERSION"
|
|
if ! DEBIAN_FRONTEND=noninteractive apt-get install -y -q $PAKETE; then
|
|
echo "--- Diagnose: was apt dazu sagt"
|
|
DEBIAN_FRONTEND=noninteractive apt-get -s install $PAKETE 2>&1 | tail -n 30
|
|
echo "--- Diagnose: was verfuegbar ist"
|
|
apt-cache policy docker-ce containerd.io docker-compose-plugin docker-buildx-plugin iptables nftables
|
|
echo "--- Diagnose: was schon installiert ist"
|
|
dpkg -l | grep -Ei 'docker|containerd|runc' || echo "keine Docker-Pakete installiert"
|
|
echo "--- Diagnose: haengende Installationen"
|
|
dpkg -l | awk '$1 !~ /^ii/ && NR > 5 {print}' || true
|
|
echo "FEHLER: Installation"
|
|
echo "ESPO02_ERGEBNIS: FEHLER"; exit 1
|
|
fi
|
|
apt-mark hold docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-buildx-plugin >/dev/null
|
|
|
|
# 4. Daemon-Grundlagen: Logrotation, weil der Standardtreiber nicht rotiert.
|
|
# Keine abweichende Bindeadresse: die Ports muessen fuer den Reverse Proxy erreichbar sein,
|
|
# die Beschraenkung uebernimmt Schritt 4.
|
|
install -d -m 0755 /etc/docker
|
|
cat > /etc/docker/daemon.json <<'CONF'
|
|
{
|
|
"log-driver": "local",
|
|
"log-opts": { "max-size": "20m", "max-file": "5" },
|
|
"live-restore": true
|
|
}
|
|
CONF
|
|
systemctl enable --now docker >/dev/null 2>&1
|
|
systemctl restart docker || { echo "FEHLER: docker startet nicht"; exit 1; }
|
|
|
|
# 5. 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; }
|
|
|
|
# 6. Ergebnis zeigen und pruefen.
|
|
docker --version
|
|
docker compose version
|
|
docker info --format 'Logtreiber: {{.LoggingDriver}} Live-Restore: {{.LiveRestoreEnabled}}'
|
|
iptables -S DOCKER-USER
|
|
[ "$(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
|