SquidGuard-Listen self-managed via GitLab (Shell-Runner)
Beispiel-Setup: ein Repo je AG fuer Zugriffskontrolle, zentrales Deploy-Repo mit Validierung, Rollout auf beide Proxys, DB-Rebuild je Gruppe und Auto-Rollback auf den letzten guten Stand. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# Zentrale Deploy-Pipeline. Liegt im NUR fuer Ops beschreibbaren Repo.
|
||||
# Laeuft auf einem dedizierten Shell-Runner (Tag: squidguard-shell),
|
||||
# der ausschliesslich diesem Projekt zugeordnet ist.
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
tags:
|
||||
- squidguard-shell
|
||||
rules:
|
||||
# Zeitplan (z.B. alle 5 Min) -> regelmaessiger Abgleich
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
# von einem AG-Repo per Trigger ausgeloest -> sofortiger Abgleich
|
||||
- if: '$CI_PIPELINE_SOURCE == "trigger"'
|
||||
# manuell ueber die GitLab-Oberflaeche
|
||||
- if: '$CI_PIPELINE_SOURCE == "web"'
|
||||
# Aenderung an der Deploy-Logik selbst
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
script:
|
||||
- chmod +x scripts/*.sh
|
||||
- ./scripts/deploy.sh
|
||||
@@ -0,0 +1,7 @@
|
||||
# Ziel-Proxies (SSH-Hostnamen) und der Deploy-User auf den Proxies.
|
||||
# Wird von deploy.sh eingelesen.
|
||||
PROXIES="proxy1.intern proxy2.intern"
|
||||
SSH_USER="deploy"
|
||||
|
||||
# GitLab-Gruppe/Namespace, unter der die AG-Repos liegen (ag01 .. ag08)
|
||||
NAMESPACE="squidguard"
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy.sh
|
||||
# Laeuft im zentralen Deploy-Repo auf dem Shell-Runner:
|
||||
# 1. klont alle AG-Repos (read-only Deploy-Token)
|
||||
# 2. validiert alle Listen
|
||||
# 3. rollt sie NUR bei fehlerfreier Validierung auf beide Proxies aus
|
||||
# 4. baut je Gruppe die .db neu und laedt Squid einmal pro Proxy neu
|
||||
#
|
||||
# Erwartete CI/CD-Variablen (im Deploy-Repo, maskiert/protected):
|
||||
# GIT_USER - Name des Group-Deploy-Tokens (read_repository)
|
||||
# GIT_TOKEN - Wert des Group-Deploy-Tokens
|
||||
# SSH-Key + known_hosts liegen im Home des Runner-Users (nicht als CI-Variable).
|
||||
set -euo pipefail
|
||||
|
||||
here=$(cd "$(dirname "$0")/.." && pwd)
|
||||
# shellcheck source=/dev/null
|
||||
source "$here/config/proxies.env"
|
||||
|
||||
: "${GIT_USER:?GIT_USER fehlt}"
|
||||
: "${GIT_TOKEN:?GIT_TOKEN fehlt}"
|
||||
: "${CI_SERVER_HOST:?CI_SERVER_HOST fehlt (von GitLab CI gesetzt)}"
|
||||
|
||||
groups="AG01 AG02 AG03 AG04 AG05 AG06 AG07 AG08"
|
||||
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT
|
||||
|
||||
# --- 1) + 2) Klonen und validieren --------------------------------------
|
||||
for ag in $groups; do
|
||||
echo "== Pruefe $ag =="
|
||||
repo="https://${GIT_USER}:${GIT_TOKEN}@${CI_SERVER_HOST}/${NAMESPACE}/${ag,,}.git"
|
||||
git clone --depth 1 -q "$repo" "$workdir/$ag"
|
||||
|
||||
"$here/scripts/validate.sh" domain "$workdir/$ag/${ag}domain"
|
||||
"$here/scripts/validate.sh" url "$workdir/$ag/${ag}url"
|
||||
done
|
||||
|
||||
# --- 3) + 4) Ausrollen ---------------------------------------------------
|
||||
SSH="ssh -o BatchMode=yes -o StrictHostKeyChecking=yes"
|
||||
failed="" # sammelt "proxy/AG" der fehlgeschlagenen Rebuilds
|
||||
|
||||
for proxy in $PROXIES; do
|
||||
echo "== Deploy nach $proxy =="
|
||||
|
||||
for ag in $groups; do
|
||||
# Dateien in den Staging-Bereich des Proxys legen
|
||||
$SSH "${SSH_USER}@${proxy}" "mkdir -p /var/lib/squidguard/incoming/${ag}"
|
||||
rsync -e "$SSH" -a --checksum \
|
||||
"$workdir/$ag/${ag}domain" "$workdir/$ag/${ag}url" \
|
||||
"${SSH_USER}@${proxy}:/var/lib/squidguard/incoming/${ag}/"
|
||||
|
||||
# Uebernahme + DB-Rebuild dieser Gruppe (privilegiert, aber eng begrenzt).
|
||||
# Schlaegt der Rebuild fehl, rollt das Proxy-Skript die Gruppe auf ihren
|
||||
# letzten guten Stand zurueck. Wir merken uns den Fehler, machen aber
|
||||
# mit den restlichen Gruppen weiter, damit deren gute Aenderungen live gehen.
|
||||
if $SSH "${SSH_USER}@${proxy}" "sudo /usr/local/sbin/squidguard-rebuild.sh ${ag}"; then
|
||||
:
|
||||
else
|
||||
echo "WARNUNG: Rebuild ${ag} auf ${proxy} fehlgeschlagen (Rollback aktiv)" >&2
|
||||
failed="${failed} ${proxy}/${ag}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Squid einmal pro Proxy neu laden, damit die neuen .db aktiv werden.
|
||||
# Auch bei Einzelfehlern: die erfolgreich gebauten Gruppen sollen live gehen.
|
||||
$SSH "${SSH_USER}@${proxy}" "sudo /usr/local/sbin/squidguard-reload.sh"
|
||||
done
|
||||
|
||||
if [ -n "$failed" ]; then
|
||||
echo "Deploy mit Fehlern abgeschlossen. Zurueckgerollt:${failed}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploy abgeschlossen."
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# validate.sh <domain|url> <datei>
|
||||
# Prueft eine squidGuard-Domain- bzw. URL-Liste auf gueltiges Format,
|
||||
# damit eine kaputte Zeile nicht den DB-Rebuild auf dem Proxy stoert.
|
||||
set -euo pipefail
|
||||
|
||||
type=${1:?Typ fehlt (domain|url)}
|
||||
file=${2:?Datei fehlt}
|
||||
|
||||
[ -f "$file" ] || { echo "Datei fehlt: $file" >&2; exit 1; }
|
||||
|
||||
domain_re='^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$'
|
||||
url_re='^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}(/[^[:space:]]*)?$'
|
||||
|
||||
errors=0
|
||||
lineno=0
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
lineno=$((lineno + 1))
|
||||
|
||||
# Leerzeilen sind erlaubt und werden ignoriert
|
||||
[ -z "${line//[[:space:]]/}" ] && continue
|
||||
|
||||
# squidGuard-DB-Listen kennen KEINE Kommentare -> '#' waere eine echte Domain
|
||||
case "$line" in
|
||||
\#*) echo "$file:$lineno: Kommentare (#) sind in squidGuard-Listen nicht erlaubt" >&2
|
||||
errors=$((errors + 1)); continue ;;
|
||||
esac
|
||||
|
||||
# kein Schema
|
||||
if printf '%s' "$line" | grep -qiE '://'; then
|
||||
echo "$file:$lineno: Schema (z.B. http://) nicht erlaubt: $line" >&2
|
||||
errors=$((errors + 1)); continue
|
||||
fi
|
||||
|
||||
# keine Grossbuchstaben (squidGuard matcht case-sensitiv auf lowercase)
|
||||
lower=$(printf '%s' "$line" | tr 'A-Z' 'a-z')
|
||||
if [ "$lower" != "$line" ]; then
|
||||
echo "$file:$lineno: Grossbuchstaben nicht erlaubt: $line" >&2
|
||||
errors=$((errors + 1)); continue
|
||||
fi
|
||||
|
||||
case "$type" in
|
||||
domain)
|
||||
printf '%s' "$line" | grep -qE "$domain_re" \
|
||||
|| { echo "$file:$lineno: keine gueltige Domain: $line" >&2; errors=$((errors + 1)); } ;;
|
||||
url)
|
||||
printf '%s' "$line" | grep -qE "$url_re" \
|
||||
|| { echo "$file:$lineno: keine gueltige URL: $line" >&2; errors=$((errors + 1)); } ;;
|
||||
*) echo "Unbekannter Typ: $type" >&2; exit 2 ;;
|
||||
esac
|
||||
done < "$file"
|
||||
|
||||
if [ "$errors" -gt 0 ]; then
|
||||
echo "Validierung fehlgeschlagen: $errors Fehler in $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $file"
|
||||
Reference in New Issue
Block a user