Add deployment artifacts and rift config (Phase 13)

Dockerfiles for API server and web UI (multi-stage, alpine:3.21,
non-root mcr user). systemd units with security hardening. Idempotent
install script. Rift-specific config with MCIAS service token, TLS
paths, and Docker compose with loopback port bindings for mc-proxy
fronting (28443/29443/28080).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 22:03:36 -07:00
parent 75c8b110da
commit 7255bba890
10 changed files with 334 additions and 3 deletions

View File

@@ -0,0 +1,46 @@
# MCR on rift — container registry.
#
# Two containers: API server (mcrsrv) and web UI (mcr-web).
# Both bind to loopback; mc-proxy handles external TLS ingress.
#
# Usage:
# docker compose -f deploy/docker/docker-compose-rift.yml up -d
#
# Prerequisites:
# - /srv/mcr/mcr.toml (copy from deploy/mcr-rift.toml)
# - /srv/mcr/certs/ with TLS cert+key
# - MCIAS service token for the 'mcr' account
services:
mcr:
build:
context: ../..
dockerfile: Dockerfile.api
container_name: mcr
restart: unless-stopped
user: "0:0"
ports:
- "127.0.0.1:28443:8443"
- "127.0.0.1:29443:9443"
volumes:
- /srv/mcr:/srv/mcr
healthcheck:
test: ["CMD", "mcrsrv", "status", "--addr", "https://localhost:8443", "--ca-cert", "/srv/mcr/certs/ca.pem"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
mcr-web:
build:
context: ../..
dockerfile: Dockerfile.web
container_name: mcr-web
restart: unless-stopped
user: "0:0"
ports:
- "127.0.0.1:28080:8080"
volumes:
- /srv/mcr:/srv/mcr
depends_on:
- mcr

37
deploy/mcr-rift.toml Normal file
View File

@@ -0,0 +1,37 @@
# MCR configuration for rift.
#
# Container registry fronted by mc-proxy:
# :8443 → mcr API (L4 passthrough via mc-proxy)
# :443 → mcr-web (L7 via mc-proxy)
#
# Copy to /srv/mcr/mcr.toml on rift before starting.
[server]
listen_addr = ":8443"
grpc_addr = ":9443"
tls_cert = "/srv/mcr/certs/mcr.pem"
tls_key = "/srv/mcr/certs/mcr.key"
read_timeout = "30s"
write_timeout = "0s"
idle_timeout = "120s"
shutdown_timeout = "60s"
[database]
path = "/srv/mcr/mcr.db"
[storage]
layers_path = "/srv/mcr/layers"
uploads_path = "/srv/mcr/uploads"
[mcias]
server_url = "https://mcias.metacircular.net:8443"
ca_cert = "/srv/mcr/certs/ca.pem"
service_token = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL21jaWFzLm1ldGFjaXJjdWxhci5uZXQiLCJzdWIiOiIwYWM3NDk3ZS0wZTE5LTRhOWMtYWI3Yi03YWZjMzc0ZDU3NzIiLCJleHAiOjE4MDYwMzczNzMsIm5iZiI6MTc3NDUwMTM3MywiaWF0IjoxNzc0NTAxMzczLCJqdGkiOiI1NTM0ZDU0OS1kYzY5LTRiNzctYTY5MC0xNzQ3NjE0MDUzYzEiLCJyb2xlcyI6bnVsbH0.bsnoGMrFzJJCIanGuiAvpqmlO2OssvFjYynQgiSt_TPMuLxziRuwuRIL9C_kRnHdF7C6c1mTHncKVj1hkLPiCg"
[web]
listen_addr = ":8080"
grpc_addr = "mcr:9443"
ca_cert = "/srv/mcr/certs/ca.pem"
[log]
level = "info"

55
deploy/scripts/install.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/sh
set -eu
SERVICE="mcr"
BINARY_SRV="/usr/local/bin/mcrsrv"
BINARY_WEB="/usr/local/bin/mcr-web"
BINARY_CTL="/usr/local/bin/mcrctl"
DATA_DIR="/srv/${SERVICE}"
UNIT_DIR="/etc/systemd/system"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
# Create system user and group (idempotent).
if ! id -u "${SERVICE}" >/dev/null 2>&1; then
useradd --system --no-create-home --shell /usr/sbin/nologin "${SERVICE}"
echo "Created system user ${SERVICE}."
fi
# Install binaries.
install -m 0755 "${REPO_DIR}/mcrsrv" "${BINARY_SRV}"
install -m 0755 "${REPO_DIR}/mcr-web" "${BINARY_WEB}"
install -m 0755 "${REPO_DIR}/mcrctl" "${BINARY_CTL}"
echo "Installed binaries."
# Create data directory structure.
install -d -o "${SERVICE}" -g "${SERVICE}" -m 0700 "${DATA_DIR}"
install -d -o "${SERVICE}" -g "${SERVICE}" -m 0700 "${DATA_DIR}/backups"
install -d -o "${SERVICE}" -g "${SERVICE}" -m 0700 "${DATA_DIR}/certs"
install -d -o "${SERVICE}" -g "${SERVICE}" -m 0700 "${DATA_DIR}/layers"
install -d -o "${SERVICE}" -g "${SERVICE}" -m 0700 "${DATA_DIR}/uploads"
echo "Created ${DATA_DIR}/."
# Install example config if none exists.
if [ ! -f "${DATA_DIR}/${SERVICE}.toml" ]; then
install -o "${SERVICE}" -g "${SERVICE}" -m 0600 \
"${REPO_DIR}/deploy/examples/mcr.toml" \
"${DATA_DIR}/${SERVICE}.toml"
echo "Installed example config to ${DATA_DIR}/${SERVICE}.toml — edit before starting."
fi
# Install systemd units.
install -m 0644 "${REPO_DIR}/deploy/systemd/${SERVICE}.service" "${UNIT_DIR}/"
install -m 0644 "${REPO_DIR}/deploy/systemd/${SERVICE}-web.service" "${UNIT_DIR}/"
install -m 0644 "${REPO_DIR}/deploy/systemd/${SERVICE}-backup.service" "${UNIT_DIR}/"
install -m 0644 "${REPO_DIR}/deploy/systemd/${SERVICE}-backup.timer" "${UNIT_DIR}/"
systemctl daemon-reload
echo "Installed systemd units."
echo ""
echo "Done. Next steps:"
echo " 1. Edit ${DATA_DIR}/${SERVICE}.toml"
echo " 2. Place TLS certs in ${DATA_DIR}/certs/"
echo " 3. systemctl enable --now ${SERVICE}"
echo " 4. systemctl enable --now ${SERVICE}-web"
echo " 5. systemctl enable --now ${SERVICE}-backup.timer"

View File

@@ -0,0 +1,25 @@
[Unit]
Description=MCR Database Backup
[Service]
Type=oneshot
User=mcr
Group=mcr
ExecStart=/usr/local/bin/mcrsrv snapshot --config /srv/mcr/mcr.toml
ExecStartPost=/usr/bin/find /srv/mcr/backups -name 'mcr-*.db' -mtime +30 -delete
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
ReadWritePaths=/srv/mcr

View File

@@ -0,0 +1,10 @@
[Unit]
Description=MCR Daily Database Backup
[Timer]
OnCalendar=*-*-* 02:00:00 UTC
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target

View File

@@ -0,0 +1,31 @@
[Unit]
Description=MCR Container Registry Web UI
After=mcr.service
Wants=mcr.service
[Service]
Type=simple
User=mcr
Group=mcr
ExecStart=/usr/local/bin/mcr-web --config /srv/mcr/mcr.toml
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
ReadOnlyPaths=/srv/mcr
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,34 @@
[Unit]
Description=MCR Container Registry
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=mcr
Group=mcr
ExecStart=/usr/local/bin/mcrsrv server --config /srv/mcr/mcr.toml
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
ReadWritePaths=/srv/mcr
# Allow binding to privileged ports if needed
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target