Assessor_2.0/docs/DEPLOYMENT.md

120 lines
5.2 KiB
Markdown

# Parallel deployment
## Environments
The Gitea workflow deploys two permanent, isolated environments on the target
server. The old HTTPS installation is outside this workflow and remains
unchanged until a separately approved cutover.
| Branch | Compose project | VPN address | Secret |
|---|---|---|---|
| `main` | `assessor_prod` | `http://10.0.0.154:4173` | `PROD_ENV_FILE` |
| `experimental` | `assessor_experimental` | `http://10.0.0.154:4174` | `STAGING_ENV_FILE` |
`assessor_prod` already exists on the server. Keep its current `PROD_ENV_FILE`
database name, user and password unchanged so the existing PostgreSQL volume
remains accessible. Do not rotate only the secret; password rotation requires a
coordinated PostgreSQL role change.
`STAGING_ENV_FILE` contains a complete environment file with credentials that
are distinct from stable:
```text
ASSESSOR_POSTGRES_DB=assessor_experimental
ASSESSOR_POSTGRES_USER=assessor_experimental
ASSESSOR_POSTGRES_PASSWORD=replace-with-a-long-random-experimental-password
```
Do not reuse the stable database name, user, or password for experimental.
Secret values must never be printed into an Actions log.
Optional CVE enrichment settings may be added to each environment secret:
```text
NVD_API_KEY=<optional NVD API key for higher rate limits>
ASSESSOR_NVD_TIMEOUT_MS=15000
```
The key is sent only by the backend to the fixed NVD CVE 2.0 endpoint. Imports
remain offline and deterministic; a user must start enrichment for an already
stored Defender-for-IoT CVE.
## Mandatory server inventory
Before the first push-triggered deployment, connect to the target server over
SSH and perform a read-only inventory. Record only technical identifiers; do
not inspect database rows, uploaded files, application pages, or report text.
```sh
docker version
docker compose version
docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Label "com.docker.compose.project"}}'
docker volume ls
docker network ls
docker ps --filter publish=4173 --format '{{.Names}}\t{{.Ports}}'
docker ps --filter publish=4174 --format '{{.Names}}\t{{.Ports}}'
```
Also identify the existing reverse proxy and firewall policy without changing
them. Confirm that `10.0.0.154` is a host address, port 4174 is unused, the
runner uses this Docker daemon, and `assessor_experimental` does not belong to
an unrelated stack. Port 4173 and project `assessor_prod` are intentionally
adopted as stable; only its expected `app` and `postgres` services may exist.
Any other conflict blocks deployment and is never resolved by stopping or
deleting the existing resource.
## Deployment behavior
Pushes to a branch are serialized per environment with an atomic lock container
on the shared Docker host. This is necessary because Gitea currently ignores the
GitHub-compatible workflow `concurrency` key. A normal job removes its lock; a
crashed job's `--rm` lock expires after two hours. The workflow installs
dependencies, runs the complete test suite and production build, validates the
resolved Compose configuration, checks port/project isolation, backs up an
existing target environment, then builds and starts only that environment.
Compose project scoping produces independent PostgreSQL and artifact volumes.
PostgreSQL has no host-published port. `restart: unless-stopped` preserves both
environments across Docker daemon and server restarts.
Feature promotion is a merge from `experimental` to `main`. Stable is rebuilt
from the merged commit; databases and artifacts are never copied between the
environments.
## Backups and restore checks
Before every update of an existing environment, the workflow writes a custom
PostgreSQL dump, an artifact archive and SHA-256 checksums to the external
Docker volume `assessor_deployment_backups`:
```text
<compose-project>/<UTC timestamp>/postgres.dump
<compose-project>/<UTC timestamp>/artifacts.tar.gz
<compose-project>/<UTC timestamp>/SHA256SUMS
```
The workflow never deletes backups. Retention and off-host copying require a
separate approved operations policy. The backup volume must be included in the
server's protected backup system; a volume on the same Docker host is not a
complete disaster-recovery copy.
Restore tests must use an isolated temporary Compose project and synthetic data.
Verify `SHA256SUMS`, restore `postgres.dump` with `pg_restore --exit-on-error`
into a fresh database, extract the artifact archive into a fresh artifact
volume, and confirm only content-free counts and health state. Never restore an
experimental backup into stable or a stable backup into experimental. Never
run `docker compose down --volumes` for either permanent environment.
## Acceptance and later cutover
After deployment, verify both in-container health checks and VPN access. Import
different synthetic fixtures into stable and experimental, redeploy each branch,
and confirm persistence and isolation. Confirm the old HTTPS site remains
available without reading its domain content.
Changing `assessor.soc-ops.de` is not part of the deployment workflow. After
explicit production acceptance, validate the existing proxy configuration,
preserve its old upstream, point it to stable, reload it, and verify TLS and
health. Rollback restores the saved upstream; the archived old stack and its
data must remain recoverable until a later retention decision.