NEURLFilter-Stack (iOS 26): Extension RebreakURLFilter -> URLFilterExtension umbenannt, url-filter-provider-Entitlement, Bloom-Prefilter-Extension, PIR-Client-Config (pirServerURL/pirAuthToken via Build-Env). PIR-Server-Ops unter ops/pir-server/ (Dockerfile, build-and-deploy, Patches, DTS-Report). backend/scripts/generate-pir-input.ts erzeugt die PIR-Datenbank. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
222 lines
7.7 KiB
Bash
222 lines
7.7 KiB
Bash
#!/usr/bin/env bash
|
|
# build-and-deploy.sh — PIR-Server auf Hetzner bauen und deployen
|
|
#
|
|
# Voraussetzungen (auf dem Server):
|
|
# - /srv/pir-build/pir-service-example/ → geklont
|
|
# - /srv/pir-build/swift-homomorphic-encryption/ → geklont
|
|
# - /srv/pir-server/data/ → existiert
|
|
# - /srv/pir-server/config/ → existiert
|
|
# - /etc/environment enthält INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET
|
|
#
|
|
# Was dieses Script macht:
|
|
# 1. Infisical-Token holen (Universal Auth)
|
|
# 2. PIR_AUTH_TOKEN aus Infisical staging lesen
|
|
# 3. service-config.json mit realem Token nach /srv/pir-server/config/ schreiben
|
|
# 3b. Quell-Patches auf pir-service-example anwenden (patches/*.patch)
|
|
# 4. Docker-Image bauen (Multi-Stage, ~20-30 Min wegen Swift-Compile)
|
|
# 5. Test-input.txtpb generieren falls keine echte DB vorhanden
|
|
# 6. PIRProcessDatabase ausführen (innerhalb Container)
|
|
# 7. Container starten / neu starten (inkl. PIR_ISSUER_REQUEST_URI env)
|
|
|
|
set -euo pipefail
|
|
|
|
BUILD_DIR="/srv/pir-build"
|
|
DATA_DIR="/srv/pir-server/data"
|
|
CONFIG_DIR="/srv/pir-server/config"
|
|
IMAGE_NAME="pir-service-staging"
|
|
CONTAINER_NAME="pir-service-staging"
|
|
PORT="8090"
|
|
INFISICAL_PROJECT_ID="14b11b35-ef59-4b8a-a16b-398f0cc3ad93"
|
|
PROCESS_CONFIG_SRC="$(dirname "$0")/process-config.json"
|
|
PATCH_DIR="$(dirname "$0")/patches"
|
|
PIR_SERVICE_SRC="${BUILD_DIR}/pir-service-example"
|
|
# Public-URL des PIR-Servers. issuer-request-uri MUSS absolut sein (RFC 9578 §6),
|
|
# sonst kann der NEURLFilter-Client keinen Privacy-Pass-Token holen.
|
|
PIR_PUBLIC_URL="https://pir.staging.rebreak.org"
|
|
PIR_ISSUER_REQUEST_URI="${PIR_PUBLIC_URL}/issue"
|
|
|
|
log() { echo "[pir-deploy] $(date '+%H:%M:%S') $*"; }
|
|
log_err() { echo "[pir-deploy:err] $(date '+%H:%M:%S') $*" >&2; }
|
|
|
|
log "=== PIR-Server Deploy gestartet ==="
|
|
|
|
# 0. Environment laden
|
|
if [[ -f /etc/environment ]]; then
|
|
export $(grep -v "^#" /etc/environment | grep -v "^$" | xargs)
|
|
fi
|
|
|
|
# 1. Infisical-Token holen
|
|
log "Step 1: Infisical Universal-Auth Login..."
|
|
INFISICAL_TOKEN=$(infisical login \
|
|
--method=universal-auth \
|
|
--client-id="${INFISICAL_CLIENT_ID}" \
|
|
--client-secret="${INFISICAL_CLIENT_SECRET}" \
|
|
--silent --plain 2>/dev/null)
|
|
|
|
if [[ -z "$INFISICAL_TOKEN" ]]; then
|
|
log_err "Infisical login fehlgeschlagen"
|
|
exit 1
|
|
fi
|
|
log "Infisical Login ok"
|
|
|
|
# 2. PIR_AUTH_TOKEN holen
|
|
log "Step 2: PIR_AUTH_TOKEN aus Infisical (staging) holen..."
|
|
PIR_AUTH_TOKEN=$(infisical secrets get PIR_AUTH_TOKEN \
|
|
--env=staging \
|
|
--projectId="${INFISICAL_PROJECT_ID}" \
|
|
--token="${INFISICAL_TOKEN}" \
|
|
--plain 2>/dev/null)
|
|
|
|
if [[ -z "$PIR_AUTH_TOKEN" ]]; then
|
|
log_err "PIR_AUTH_TOKEN nicht in Infisical gefunden (env=staging, project=${INFISICAL_PROJECT_ID})"
|
|
log_err "Bitte via Infisical-Dashboard anlegen: Key=PIR_AUTH_TOKEN, Env=staging"
|
|
exit 1
|
|
fi
|
|
log "PIR_AUTH_TOKEN geladen (${#PIR_AUTH_TOKEN} Zeichen)"
|
|
|
|
# 3. service-config.json schreiben (Token inline, nicht in Datei committed)
|
|
log "Step 3: service-config.json nach ${CONFIG_DIR}/ schreiben..."
|
|
|
|
# Shard-Count aus vorhandenen url-N.bin-Artifacts ableiten.
|
|
# Bugfix: vorher fix `SHARD_COUNT=1` — bei Re-Runs ohne PIRProcessDatabase (Step 6
|
|
# wird übersprungen wenn Artifacts existieren) regressierte das einen 4-Shard-Server
|
|
# fälschlich auf shardCount=1.
|
|
SHARD_COUNT=$(ls "${DATA_DIR}"/url-*.bin 2>/dev/null | wc -l | tr -d ' ')
|
|
[[ "$SHARD_COUNT" -lt 1 ]] && SHARD_COUNT=1
|
|
log "Shard-Count aus ${DATA_DIR}: ${SHARD_COUNT}"
|
|
|
|
cat > "${CONFIG_DIR}/service-config.json" << EOF
|
|
{
|
|
"users": [
|
|
{
|
|
"tier": "tier1",
|
|
"tokens": ["${PIR_AUTH_TOKEN}"]
|
|
}
|
|
],
|
|
"usecases": [
|
|
{
|
|
"name": "org.rebreak.app.url.filtering",
|
|
"fileStem": "/data/url",
|
|
"shardCount": ${SHARD_COUNT}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
log "service-config.json geschrieben"
|
|
|
|
# 3b. Quell-Patches auf pir-service-example anwenden (idempotent)
|
|
log "Step 3b: Quell-Patches anwenden..."
|
|
if [[ -d "$PATCH_DIR" ]] && compgen -G "${PATCH_DIR}/*.patch" > /dev/null; then
|
|
# Clone auf sauberen Stand bringen, dann alle Patches anwenden
|
|
git -C "$PIR_SERVICE_SRC" checkout -- . 2>/dev/null || true
|
|
for p in "${PATCH_DIR}"/*.patch; do
|
|
log " apply $(basename "$p")"
|
|
git -C "$PIR_SERVICE_SRC" apply "$p"
|
|
done
|
|
else
|
|
log " keine Patches gefunden — übersprungen"
|
|
fi
|
|
|
|
# 4. Docker-Image bauen
|
|
log "Step 4: Docker-Image bauen (Kontext: ${BUILD_DIR})..."
|
|
log "WARNUNG: Swift-Build dauert ~20-30 Min, RAM-Nutzung hoch (~2-3 GB). Swap wird benötigt."
|
|
|
|
docker build \
|
|
-t "${IMAGE_NAME}:latest" \
|
|
-f "$(dirname "$0")/Dockerfile" \
|
|
"${BUILD_DIR}"
|
|
|
|
log "Docker-Image gebaut: ${IMAGE_NAME}:latest"
|
|
|
|
# 5. Test-input.txtpb generieren falls keine echte DB
|
|
log "Step 5: Input-DB prüfen..."
|
|
bash "$(dirname "$0")/gen-test-input.sh" "${DATA_DIR}/input.txtpb"
|
|
|
|
# 6. PIRProcessDatabase ausführen (im Container, mounted /data)
|
|
log "Step 6: PIRProcessDatabase ausführen..."
|
|
|
|
# Prüfe ob DB-Artifacts schon vorhanden (von vorherigem Run)
|
|
if [[ -f "${DATA_DIR}/url-0.bin" ]] && [[ -f "${DATA_DIR}/url-0.params.txtpb" ]]; then
|
|
log "DB-Artifacts bereits vorhanden (url-0.bin + url-0.params.txtpb) — überspringe PIRProcessDatabase"
|
|
log "Für Rebuild: rm ${DATA_DIR}/url-0.bin ${DATA_DIR}/url-0.params.txtpb && re-run"
|
|
else
|
|
# process-config.json in data-dir kopieren (PIRProcessDatabase braucht absolute Pfade)
|
|
cp "${PROCESS_CONFIG_SRC}" "${DATA_DIR}/process-config.json"
|
|
|
|
docker run --rm \
|
|
-v "${DATA_DIR}:/data" \
|
|
"${IMAGE_NAME}:latest" \
|
|
PIRProcessDatabase /data/process-config.json
|
|
|
|
log "PIRProcessDatabase abgeschlossen"
|
|
|
|
# Shard-Count aus Output ermitteln
|
|
SHARD_COUNT=$(ls "${DATA_DIR}"/url-*.bin 2>/dev/null | wc -l)
|
|
log "Erkannte Shards: ${SHARD_COUNT}"
|
|
|
|
# service-config.json mit korrektem shardCount neu schreiben
|
|
if [[ "$SHARD_COUNT" -gt 1 ]]; then
|
|
log "Mehr als 1 Shard — service-config.json updaten..."
|
|
cat > "${CONFIG_DIR}/service-config.json" << EOF
|
|
{
|
|
"users": [
|
|
{
|
|
"tier": "tier1",
|
|
"tokens": ["${PIR_AUTH_TOKEN}"]
|
|
}
|
|
],
|
|
"usecases": [
|
|
{
|
|
"name": "org.rebreak.app.url.filtering",
|
|
"fileStem": "/data/url",
|
|
"shardCount": ${SHARD_COUNT}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
log "service-config.json mit shardCount=${SHARD_COUNT} aktualisiert"
|
|
fi
|
|
fi
|
|
|
|
# 7. Container starten / neu starten
|
|
log "Step 7: Container starten..."
|
|
|
|
# Alten Container stoppen falls vorhanden
|
|
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
log "Stoppe alten Container ${CONTAINER_NAME}..."
|
|
docker stop "${CONTAINER_NAME}" 2>/dev/null || true
|
|
docker rm "${CONTAINER_NAME}" 2>/dev/null || true
|
|
fi
|
|
|
|
docker run -d \
|
|
--name "${CONTAINER_NAME}" \
|
|
--restart unless-stopped \
|
|
-p "127.0.0.1:${PORT}:8090" \
|
|
-v "${DATA_DIR}:/data:ro" \
|
|
-v "${CONFIG_DIR}:/config:ro" \
|
|
-e "PIR_ISSUER_REQUEST_URI=${PIR_ISSUER_REQUEST_URI}" \
|
|
"${IMAGE_NAME}:latest"
|
|
|
|
log "Container ${CONTAINER_NAME} gestartet auf 127.0.0.1:${PORT}"
|
|
|
|
# 8. Health-Check
|
|
log "Step 8: Health-Check (warte 5s auf Start)..."
|
|
sleep 5
|
|
|
|
# Issuer-Directory muss erreichbar sein UND eine ABSOLUTE issuer-request-uri liefern.
|
|
# (PrivateToken-Auth lässt sich ohne echten Privacy-Pass-Token nicht testen — der
|
|
# PIR_AUTH_TOKEN ist nur der User-Token für /issue, nicht der /config-Token.)
|
|
DIRECTORY=$(curl -s "http://127.0.0.1:${PORT}/.well-known/private-token-issuer-directory" 2>/dev/null || echo "")
|
|
|
|
if echo "$DIRECTORY" | grep -q '"issuer-request-uri":"https://'; then
|
|
log "Health-Check OK — Issuer-Directory liefert absolute issuer-request-uri"
|
|
log "=== Deploy erfolgreich abgeschlossen ==="
|
|
else
|
|
log_err "Health-Check fehlgeschlagen — Issuer-Directory unerwartet:"
|
|
log_err "$DIRECTORY"
|
|
log_err "Container-Logs:"
|
|
docker logs "${CONTAINER_NAME}" --tail 30
|
|
log_err "Deploy FEHLGESCHLAGEN — manueller Check erforderlich"
|
|
exit 1
|
|
fi
|