deploy: join gitea netns instead of bridge (machine kernel lacks ip_tables for rootless netavark); pin image digests; push-to-create repo
This commit is contained in:
@@ -3,5 +3,5 @@
|
||||
# keeps the whole toolchain pinned and independent of host packages.
|
||||
# Built ONCE at install, never pulled again at runtime:
|
||||
# podman build -t localhost/ctao-portal-build:1 -f Containerfile.build .
|
||||
FROM docker.io/library/node:24-alpine
|
||||
FROM docker.io/library/node:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd
|
||||
RUN apk add --no-cache git
|
||||
|
||||
+5
-5
@@ -24,7 +24,7 @@ Uninstall restores the machine exactly (see bottom). No secrets in any file.
|
||||
|
||||
1. **[W]** `mkdir -p ~/ctao-portal-demo/{gitea-data,gitea-config,releases,state,bin,config} ~/.config/containers/systemd ~/.config/systemd/user`
|
||||
2. **[W]** Copy files from this dir (scp from the Mac):
|
||||
- `ctao-demo-gitea.container`, `ctao-demo-web.container`, `ctao-demo.network` → `~/.config/containers/systemd/`
|
||||
- `ctao-demo-gitea.container`, `ctao-demo-web.container` → `~/.config/containers/systemd/`
|
||||
- `ctao-portal-build.service`, `ctao-portal-build.timer` → `~/.config/systemd/user/`
|
||||
- `build.sh` → `~/ctao-portal-demo/bin/` (`chmod +x`)
|
||||
- `nginx.conf` → `~/ctao-portal-demo/config/`
|
||||
@@ -72,10 +72,8 @@ Uninstall restores the machine exactly (see bottom). No secrets in any file.
|
||||
systemctl --user disable --now ctao-portal-build.timer
|
||||
systemctl --user stop ctao-demo-web ctao-demo-gitea
|
||||
rm ~/.config/containers/systemd/ctao-demo-*.container \
|
||||
~/.config/containers/systemd/ctao-demo.network \
|
||||
~/.config/systemd/user/ctao-portal-build.{service,timer}
|
||||
systemctl --user daemon-reload
|
||||
podman network rm ctao-demo 2>/dev/null || true
|
||||
podman rmi localhost/ctao-portal-build:1 docker.io/gitea/gitea:1.27-rootless docker.io/library/nginx:stable-alpine
|
||||
rm -rf ~/ctao-portal-demo
|
||||
```
|
||||
@@ -90,7 +88,9 @@ rm -rf ~/ctao-portal-demo
|
||||
the build container is capped at 1 GB via `podman run --memory`. Watch the
|
||||
first `npm ci` + build in the journal — if it OOMs inside its cgroup
|
||||
(contained, just retries), raise the cap.
|
||||
- Build isolation: build containers run on the `ctao-demo` bridge (see
|
||||
`ctao-demo.network`) — repo/npm code cannot reach host loopback services.
|
||||
- Build isolation: build containers join the Gitea container's network
|
||||
namespace (`--network=container:ctao-demo-gitea`) — repo/npm code sees
|
||||
Gitea on localhost:3000 but cannot reach host loopback services. (Rootless
|
||||
netavark bridges don't work here: no `ip_tables` kernel module, no sudo.)
|
||||
- Secrets inventory: Gitea admin password (typed interactively, lives only
|
||||
in Gitea's DB) — that's the complete list. Build/poll/serve use none.
|
||||
|
||||
+10
-8
@@ -12,10 +12,12 @@ GITEA_URL="${GITEA_URL:-http://localhost:3000}" # published by ctao-demo-gitea
|
||||
REPO="${REPO:-ctao/portal}" # owner/repo in Gitea
|
||||
BRANCH="${BRANCH:-main}"
|
||||
BUILD_IMAGE="${BUILD_IMAGE:-localhost/ctao-portal-build:1}"
|
||||
BUILD_NET="${BUILD_NET:-ctao-demo}" # bridge from ctao-demo.network
|
||||
# Clone URL as seen from INSIDE the build network (container-name DNS);
|
||||
# the poll below uses $GITEA_URL because it runs on the host.
|
||||
REPO_INTERNAL="${REPO_INTERNAL:-http://ctao-demo-gitea:3000/$REPO.git}"
|
||||
# The build joins the Gitea container's network namespace: localhost inside
|
||||
# the build = Gitea's loopback (port 3000), host loopback stays unreachable.
|
||||
# (A netavark bridge would be equivalent, but rootless bridges need the
|
||||
# ip_tables kernel module, absent on the machine — pasta needs nothing.)
|
||||
BUILD_NETNS="${BUILD_NETNS:-container:ctao-demo-gitea}"
|
||||
REPO_INTERNAL="${REPO_INTERNAL:-http://localhost:3000/$REPO.git}"
|
||||
KEEP="${KEEP:-3}" # released builds to retain
|
||||
mkdir -p "$BASE/repo" "$BASE/releases" "$BASE/state"
|
||||
|
||||
@@ -35,11 +37,11 @@ t0=$(date +%s)
|
||||
|
||||
# --- 2. Build in the ephemeral container (git + pinned node live there).
|
||||
# SECURITY: the container runs npm lifecycle scripts from the repo, so it is
|
||||
# confined to the ctao-demo bridge — it reaches Gitea by container name and
|
||||
# the internet (for `npm ci` when the lockfile changed), but NOT the host's
|
||||
# loopback services (code-server, Strapi). Never use --network=host here.
|
||||
# confined to Gitea's netns — it reaches Gitea on localhost:3000 and the
|
||||
# internet (for `npm ci` when the lockfile changed), but NOT the host's
|
||||
# loopback services (code-server). Never use --network=host here.
|
||||
# node_modules and .deps-hash are untracked, so they survive checkouts.
|
||||
podman run --rm --network="$BUILD_NET" --memory=1g \
|
||||
podman run --rm --network="$BUILD_NETNS" --memory=1g \
|
||||
-e ASTRO_TELEMETRY_DISABLED=1 \
|
||||
-e SHA="$sha" -e REPO_URL="$REPO_INTERNAL" -e BRANCH="$BRANCH" \
|
||||
-v "$BASE/repo:/work/repo:z" \
|
||||
|
||||
@@ -11,16 +11,15 @@ After=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName=ctao-demo-gitea
|
||||
# Same major as the local demo (1.27). Re-pin to the exact digest at install:
|
||||
# podman pull docker.io/gitea/gitea:1.27-rootless
|
||||
# podman images --digests docker.io/gitea/gitea → paste sha256 here
|
||||
Image=docker.io/gitea/gitea:1.27-rootless
|
||||
# Pinned by digest at install (2026-07-28); tag kept for readability.
|
||||
Image=docker.io/gitea/gitea:1.27-rootless@sha256:36cce26be71609091e1236d5b5de2c66a81fb8a7d45756a5fd3b7a28c11733b7
|
||||
# The rootless image runs as uid 1000 inside; keep-id maps it to the host user
|
||||
# so the bind-mounted dirs stay owned by `strapi` (no chown, no root anywhere).
|
||||
UserNS=keep-id:uid=1000,gid=1000
|
||||
# Shared bridge with the build containers (name-resolvable as ctao-demo-gitea);
|
||||
# host loopback stays out of reach for build code — see ctao-demo.network.
|
||||
Network=ctao-demo.network
|
||||
# Networking: default rootless pasta (userspace — the machine's kernel lacks
|
||||
# ip_tables for rootless netavark bridges, and we have no sudo). The build
|
||||
# container joins THIS container's netns (--network=container:ctao-demo-gitea),
|
||||
# so it sees Gitea on localhost:3000 while the HOST loopback stays invisible.
|
||||
Volume=%h/ctao-portal-demo/gitea-data:/var/lib/gitea:Z
|
||||
Volume=%h/ctao-portal-demo/gitea-config:/etc/gitea:Z
|
||||
PublishPort=3000:3000
|
||||
@@ -37,6 +36,10 @@ Environment=GITEA__database__DB_TYPE=sqlite3
|
||||
Environment=GITEA__security__INSTALL_LOCK=true
|
||||
Environment=GITEA__service__DISABLE_REGISTRATION=true
|
||||
Environment=GITEA__mailer__ENABLED=false
|
||||
# First `git push` auto-creates the repo (no UI step); public so the build
|
||||
# pipeline can clone anonymously — content is the public site anyway.
|
||||
Environment=GITEA__repository__ENABLE_PUSH_CREATE_USER=true
|
||||
Environment=GITEA__repository__DEFAULT_PRIVATE=public
|
||||
# Sveltia is served from the portal vhost and calls the Gitea API cross-origin.
|
||||
# TODO(vhost): tighten * to the portal origin once known, and add
|
||||
# GITEA__cors__SCHEME=https (the [cors] SCHEME default is http).
|
||||
|
||||
@@ -9,8 +9,8 @@ After=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName=ctao-demo-web
|
||||
# Official image; re-pin to the exact digest at install (same drill as Gitea).
|
||||
Image=docker.io/library/nginx:stable-alpine
|
||||
# Pinned by digest at install (2026-07-28); tag kept for readability.
|
||||
Image=docker.io/library/nginx:stable-alpine@sha256:97d490c12ba55b4946b01546d1c3ed324e8d41ab1c9fcb2a616aa470620e5b46
|
||||
# releases/ is shared with the build container (:z shared label);
|
||||
# the config file is exclusive to nginx (:Z).
|
||||
Volume=%h/ctao-portal-demo/releases:/srv/releases:ro,z
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# CTAO portal demo — dedicated bridge network (quadlet .network unit).
|
||||
# Why it exists: the build container executes npm lifecycle scripts from the
|
||||
# repo, so it must NOT see the host's loopback (code-server, Strapi). On this
|
||||
# bridge, containers resolve each other by name (aardvark-dns) and have NATed
|
||||
# internet egress, but host 127.0.0.1 services are unreachable — unlike
|
||||
# --network=host, which this replaces.
|
||||
[Unit]
|
||||
Description=CTAO portal demo — internal bridge network
|
||||
|
||||
[Network]
|
||||
NetworkName=ctao-demo
|
||||
Reference in New Issue
Block a user