diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 759f081..e965ac6 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -333,7 +333,7 @@ jobs: # Log dump must precede teardown (which removes the containers). - name: Dump container logs on failure if: failure() - run: docker compose -f infra/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat flowable-db flowable-rest flowable-init keycloak acl bff domain projection-db event-subscriber projection-api self-service openbaar behandel beheer objecttypen-db objecttypen-redis objecttypen-init objecttypen objecten-db objecten-redis objecten-init objecten objecten-celery registerrecord-init tempo prometheus grafana 2>&1 || true + run: docker compose -f infra/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat flowable-db flowable-rest flowable-init keycloak acl bff domain projection-db event-subscriber projection-api self-service openbaar behandel beheer objecttypen-db objecttypen-redis objecttypen-init objecttypen objecten-db objecten-redis objecten-init objecten objecten-celery registerrecord-init clamav tempo prometheus grafana 2>&1 || true - name: Tear down if: always() run: make down diff --git a/Makefile b/Makefile index f4a397b..c50e297 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ COMPOSE := infra/docker-compose.yml # Long-running services with a healthcheck — the smoke polls these for readiness # (infra/wait-healthy.sh). One-shot init jobs (oz-init, nrc-init, flowable-init) # are not polled; they only need to have run. See docs/runbooks/gitea-actions-gotchas.md. -WAIT_SVCS := openzaak nrc-web acl bff domain event-subscriber projection-api self-service openbaar behandel beheer objecttypen objecten +WAIT_SVCS := openzaak nrc-web acl bff domain event-subscriber projection-api self-service openbaar behandel beheer objecttypen objecten clamav # Config files (OpenZaak data.yaml, Keycloak realms, Flowable BPMN) are streamed # into external named volumes via `docker cp` (infra/seed-config.sh) instead of # bind-mounted, because bind mounts don't reach sibling containers on the diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index 6ac6fae..c528455 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -785,6 +785,26 @@ services: condition: service_completed_successfully networks: [cg] + # ClamAV daemon (S-28, ADR-0036): the domain scans uploaded diplomas over clamd's INSTREAM + # protocol on :3310 before they reach OpenZaak (S-29). The first start downloads ~300 MB of + # signatures with freshclam; the volume keeps them across restarts. clamd holds them in memory + # (~1.2 GB), and a reload would briefly hold two copies — ConcurrentDatabaseReload off prevents + # that, at the cost of clamd pausing scans during a signature reload. + clamav: + image: docker.io/clamav/clamav:1.4.6 + environment: + CLAMD_CONF_ConcurrentDatabaseReload: "no" + # The image's own healthcheck (clamdcheck.sh: PING → PONG) polls every 30s; poll faster so + # wait-healthy sees it as soon as the signatures are loaded. + healthcheck: + test: ["CMD-SHELL", "clamdcheck.sh"] + interval: 5s + start_period: 360s + mem_limit: 2g + volumes: + - clamav-db:/var/lib/clamav + networks: [cg] + # ── Observability backplane (S-16a, ADR-0023) ────────────────────────────── # Grafana-native stack: Tempo ingests OTLP traces (the .NET services export # straight to it — no collector hop, S-16b), Prometheus scrapes service @@ -836,6 +856,7 @@ volumes: projection-db: objecttypen-db: objecten-db: + clamav-db: # Config volumes — created and populated out-of-band by infra/seed-config.sh # (docker cp), because bind mounts don't reach sibling containers on the CI # runner. `external` keeps the names deterministic; the seed step manages them. diff --git a/infra/helm/big-reference/values.yaml b/infra/helm/big-reference/values.yaml index a3b057b..6da465e 100644 --- a/infra/helm/big-reference/values.yaml +++ b/infra/helm/big-reference/values.yaml @@ -588,6 +588,24 @@ workloads: envFrom: [objecten] waitFor: [objecten-db:5432, objecten-redis:6379] + # ── ClamAV (S-28, ADR-0036) ───────────────────────────────────────────────── + # The domain scans uploaded diplomas over clamd's INSTREAM protocol (S-29). + # First start pulls ~300 MB of signatures, so the node needs outbound internet + # (like seed-zaaktype); the data volume keeps them when persistence is on. + clamav: + image: docker.io/clamav/clamav:1.4.6 + env: + CLAMD_CONF_ConcurrentDatabaseReload: "no" + ports: [{ name: clamd, port: 3310 }] + data: { mountPath: /var/lib/clamav, size: 1Gi } + probe: + exec: { command: [clamdcheck.sh] } + periodSeconds: 5 + failureThreshold: 72 + resources: + requests: { memory: 1200Mi } + limits: { memory: 2Gi } + # ── Bootstrap the flow, like the local compose stack does (S-B04, ADR-0020) ── # Seeds + publishes the BIG zaaktype through the same FQDN the ACL uses, so the # server-assigned URLs are host-consistent. The ACL then resolves them by