diff --git a/.env.example b/.env.example index c956563..354d08c 100644 --- a/.env.example +++ b/.env.example @@ -39,10 +39,20 @@ FORAIL_NODE_TYPE=hybrid #FORAIL_TASK_CGROUP=host # ── Docker images ──────────────────────────────────────────────────────────── -# Pin to a release tag in production (e.g., 2026.04.0), not latest. +# Pin to a release tag in production, not latest. The tag has to exist for +# BOTH images -- 2026.07.1 was published for the backend only, so pinning it +# leaves the frontend unresolvable. +# +# Do not go below 2026.07.2-rc1 for a single-node install: 2026.07.0 registers +# the node as `control` and makes the `default` queue a Kubernetes container +# group, so every job sits in `pending` forever on Compose. FORAIL_BACKEND_IMAGE=ghcr.io/forail-platform/forail-backend FORAIL_FRONTEND_IMAGE=ghcr.io/forail-platform/forail-frontend -FORAIL_TAG=2026.07.0 +FORAIL_TAG=2026.07.2-rc1 + +# Subject name for the self-signed certificate generated on first start when +# nginx/ssl/ is empty. Ignored once a real certificate is in place. +FORAIL_TLS_CN=localhost # ── Nginx / Networking ────────────────────────────────────────────────────── #NGINX_HTTP_PORT=80 diff --git a/README.md b/README.md index e2fe489..f25d0a5 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ cd forail-devops # 2. Configure environment cp .env.example .env -# Edit .env with real values +# Set the four `changeme` values in .env -- the stack will not come up without +# them. The rest has working defaults. # 3. Start docker compose up -d @@ -33,6 +34,29 @@ docker compose up -d docker compose ps # every service should report (healthy) ``` +The UI is then on ; `http://` redirects to it. Log in with +`FORAIL_ADMIN_USER` / `FORAIL_ADMIN_PASSWORD` from your `.env`. + +On first start, when `nginx/ssl/` is empty, a **self-signed** certificate is +generated so nginx has something to serve — your browser will warn about it. +Drop a real certificate into `nginx/ssl/` and it is left alone; see +[README-prod.md](README-prod.md). + +### Running jobs + +The stack above serves the API and the UI, but it **cannot execute anything** — +projects will not sync and every job stays in `pending`. Job execution runs +playbooks through podman inside the task container, which needs cgroup, mount +and user-namespace privileges: + +```bash +FORAIL_TASK_PRIVILEGED=true FORAIL_TASK_CGROUP=host docker compose up -d +``` + +This is **off by default on purpose**: a privileged container is a trivial +escape to host root. Turn it on only on a host you are willing to treat as a +dedicated job runner. This is tracked as H4 in the security notes. + The scripts under `scripts/` are the container health probes — Compose mounts them into the containers and runs them there (`bash /etc/forail/healthcheck-web.sh`), so they call `forail-manage` and only work inside the image. Run one by hand with: diff --git a/docker-compose.yml b/docker-compose.yml index 8da3e61..fb40613 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -196,6 +196,33 @@ services: restart: unless-stopped # ── External nginx (TLS termination + routing) ───────── + # ── TLS certificate bootstrap ───────────────────────── + # nginx terminates TLS and refuses to start without a certificate, so a + # clean `docker compose up -d` used to leave it in a restart loop with + # nothing serving on 80 or 443. Generate a self-signed pair when none is + # present; an operator-supplied certificate in nginx/ssl is never touched. + # Self-signed is for getting started only -- see README-prod.md for the + # real thing. + nginx-cert-init: + image: alpine:3.20 + command: + - sh + - -c + - | + if [ -f /ssl/fullchain.pem ] && [ -f /ssl/privkey.pem ]; then + echo "TLS certificate present, leaving it alone." + exit 0 + fi + apk add --no-cache openssl >/dev/null + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /ssl/privkey.pem -out /ssl/fullchain.pem \ + -subj "/CN=${FORAIL_TLS_CN:-localhost}" >/dev/null 2>&1 + echo "WARNING: generated a self-signed TLS certificate for ${FORAIL_TLS_CN:-localhost}." + echo "WARNING: browsers will not trust it. Replace nginx/ssl/ before production." + volumes: + - ./nginx/ssl:/ssl + restart: "no" + nginx: image: nginx:1.27-alpine ports: @@ -207,6 +234,8 @@ services: networks: - forail depends_on: + nginx-cert-init: + condition: service_completed_successfully forail-web: condition: service_healthy forail-frontend: