server-up/.forgejo/workflows/deploy-prod.yml
Ramon 916e54a229 v0.5.00-beta - authenticatie + beveiligingsfixes
Server Up beheerde de Docker-daemon als root zonder enige vorm van
authenticatie: elke /api/*-route was gelijk aan root-toegang op de host.

- Authenticatie toegevoegd (core/auth.py): lokale accounts met scrypt-hash,
  sessiecookie (HttpOnly, SameSite=Strict) en optionele trusted-proxy-header
  SSO die alleen vanaf geconfigureerde proxy-IP's wordt vertrouwd.
- before_request-guard schermt alle API-routes af; loginscherm en
  eerste-account-setup in de UI.
- CSRF-token verplicht op elke mutatie; GET-varianten van state-wijzigende
  routes verwijderd (o.a. /api/docker/restart was via <img> te triggeren).
- Path traversal in /api/store/install gedicht; gedeelde safe_name()-validatie
  voor stack-, instantie- en repo-namen.
- Git-tokens worden niet meer teruggegeven via /api/repos en /api/settings
  (has_token-vlag); settings-PUT wist een bestaand token niet meer en kan AUTH
  niet overschrijven.
- Git-URL's beperkt tot http(s)/ssh/scp-syntax; ext::-transport (voert een
  shell-commando uit) en file:// worden geweigerd.
- Boilerplate-templates renderen in een SandboxedEnvironment (SSTI).
- Automatisch syncen van repo's bij boot standaard uit (AUTO_SYNC_ON_BOOT),
  optionele commit-pinning per repo.
- Waitress in plaats van de Flask-ontwikkelserver, MAX_CONTENT_LENGTH,
  ProxyFix, en CSP/X-Frame-Options/nosniff/Referrer-Policy headers.
- Front-end libraries (Tailwind, Alpine, htmx) lokaal meegeleverd i.p.v. CDN;
  Google Fonts verwijderd. Werkt nu ook offline.
- SSH host-key-verificatie aan (accept-new + /data/known_hosts).
- Lichte /healthz voor de healthcheck i.p.v. `docker info`.
- config.json en secret.key met 0600-rechten.
- Poort standaard op 127.0.0.1 gebonden.
- Audit-log gebruikt één gedeelde SQLite-verbinding (fd-lek per job verholpen);
  joblogs afgekapt op 2000 regels.
- VERSION-bestand is de enige bron voor het versienummer.
- pytest-suite toegevoegd (74 tests) en als stap in beide deploy-workflows.
- fix-config.sh verwijderd (hardgecodeerd intern IP, overschreef config).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
2026-07-26 14:40:05 +02:00

102 lines
3 KiB
YAML

name: Deploy server-up (prod)
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
concurrency:
group: server-up-deploy-prod
cancel-in-progress: false
jobs:
deploy:
runs-on: [self-hosted, prod]
timeout-minutes: 20
env:
DEPLOY_DIR: /opt/server-up
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Bepaal versie
id: version
run: |
set -euo pipefail
BASE="$(tr -d '[:space:]' < VERSION 2>/dev/null || echo 0.0.0)"
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
VERSION="${GITHUB_REF_NAME#v}"
CHANNEL="release"
else
VERSION="${BASE}"
CHANNEL="main"
fi
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "CHANNEL=${CHANNEL}" >> "$GITHUB_ENV"
echo "SU_VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "Deploying version: ${VERSION} (${CHANNEL})"
- name: Tests draaien
run: |
set -euo pipefail
python3 -m venv /tmp/su-test-venv
/tmp/su-test-venv/bin/pip install -q -r server-up/requirements.txt pytest
/tmp/su-test-venv/bin/python -m pytest tests -q
- name: Sync code naar deploy-directory
run: |
set -euo pipefail
rsync -a --delete \
--exclude='.git/' \
--exclude='.forgejo/' \
./ "$DEPLOY_DIR/"
- name: Schrijf VERSION-bestand
working-directory: ${{ env.DEPLOY_DIR }}
run: echo "${VERSION}" > VERSION
- name: Build image
working-directory: ${{ env.DEPLOY_DIR }}
run: |
docker compose build --pull
# docker compose bouwt als server-up:latest; voeg versie- en channel-tags toe
docker tag server-up:latest "server-up:${VERSION}"
docker tag server-up:latest "server-up:${CHANNEL}"
- name: Deploy
working-directory: ${{ env.DEPLOY_DIR }}
run: |
set -euo pipefail
docker compose up -d --remove-orphans
docker compose ps
- name: Wachten op healthcheck
run: |
set -euo pipefail
for i in {1..40}; do
status=$(docker inspect --format '{{.State.Health.Status}}' server-up 2>/dev/null || echo "starting")
echo "poll $i: $status"
if [ "$status" = "healthy" ]; then
echo "server-up is healthy als versie ${VERSION}"
exit 0
fi
sleep 3
done
echo "server-up werd niet healthy in tijd — logs:"
cd "$DEPLOY_DIR" && docker compose logs --tail=200
exit 1
- name: Oude images opruimen (behoud laatste 5)
if: success()
run: |
docker images server-up --format '{{.Tag}} {{.ID}}' \
| grep -vE '^(latest|release|main) ' \
| tail -n +6 \
| awk '{print $2}' \
| xargs -r docker rmi -f || true
docker image prune -f