Files
mcias/deploy/scripts/install.sh
Kyle Isom b0afe3b993 Align with engineering standards (steps 1-5)
- Rename dist/ -> deploy/ with subdirs examples/, scripts/,
  systemd/ per standard repository layout
- Update .gitignore: gitignore all of dist/ (build output only)
- Makefile: all target is now vet->lint->test->build; add vet,
  proto-lint, devserver targets; CGO_ENABLED=0 for builds
  (modernc.org/sqlite is pure-Go, no C toolchain needed);
  CGO_ENABLED=1 retained for tests (race detector)
- Dockerfile: builder -> golang:1.26-alpine, runtime ->
  alpine:3.21; drop libc6 dep; add /srv/mcias/certs and
  /srv/mcias/backups to image
- deploy/systemd/mcias.service: add RestrictSUIDSGID=true
- deploy/systemd/mcias-backup.service: new oneshot backup unit
- deploy/systemd/mcias-backup.timer: daily 02:00 UTC, 5m jitter
- deploy/scripts/install.sh: install backup units and enable
  timer; create certs/ and backups/ subdirs in /srv/mcias
- buf.yaml: add proto linting config for proto-lint target
- internal/db: add Snapshot and SnapshotDir methods (VACUUM INTO)
- cmd/mciasdb: add snapshot subcommand; no master key required
2026-03-16 20:26:43 -07:00

231 lines
7.4 KiB
Bash

#!/bin/sh
# install.sh — MCIAS first-time and upgrade installer
#
# Usage: sh deploy/scripts/install.sh
#
# This script must be run as root. It:
# 1. Creates the mcias system user and group (idempotent).
# 2. Copies binaries to /usr/local/bin/.
# 3. Creates /srv/mcias/ with correct permissions.
# 4. Installs the systemd service and backup units.
# 5. Prints post-install instructions.
#
# The script does NOT start or enable the service automatically. Review the
# configuration files and set the master key passphrase before starting.
#
# Idempotent: safe to re-run after upgrades. Existing config files are not
# overwritten; new example files are placed alongside them with a .new suffix
# so you can review and merge changes.
#
# POSIX sh compatible — no bash-isms.
set -eu
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
BIN_DIR="/usr/local/bin"
SRV_DIR="/srv/mcias"
MAN_DIR="/usr/share/man/man1"
SYSTEMD_DIR="/etc/systemd/system"
SERVICE_USER="mcias"
SERVICE_GROUP="mcias"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DEPLOY_DIR="$(dirname "$SCRIPT_DIR")"
REPO_ROOT="$(dirname "$DEPLOY_DIR")"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
info() {
printf '==> %s\n' "$*"
}
warn() {
printf 'WARNING: %s\n' "$*" >&2
}
die() {
printf 'ERROR: %s\n' "$*" >&2
exit 1
}
require_root() {
if [ "$(id -u)" -ne 0 ]; then
die "This script must be run as root."
fi
}
# install_file SRC DST MODE OWNER
# Installs SRC to DST. If DST already exists, installs SRC as DST.new
# so the operator can review changes.
install_file() {
src="$1"
dst="$2"
mode="$3"
owner="$4"
if [ -e "$dst" ]; then
info "Existing file found: $dst (installing as $dst.new)"
install -m "$mode" -o "$owner" "$src" "$dst.new"
else
install -m "$mode" -o "$owner" "$src" "$dst"
fi
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
require_root
# Step 1: Create system user and group.
info "Creating system user and group: $SERVICE_USER"
if getent group "$SERVICE_GROUP" > /dev/null 2>&1; then
info "Group $SERVICE_GROUP already exists — skipping."
else
groupadd --system "$SERVICE_GROUP"
fi
if getent passwd "$SERVICE_USER" > /dev/null 2>&1; then
info "User $SERVICE_USER already exists — skipping."
else
useradd \
--system \
--gid "$SERVICE_GROUP" \
--no-create-home \
--shell /usr/sbin/nologin \
--comment "MCIAS authentication server" \
"$SERVICE_USER"
fi
# Step 2: Install binaries.
info "Installing binaries to $BIN_DIR"
for bin in mciassrv mciasctl mciasdb mciasgrpcctl; do
src="$REPO_ROOT/bin/$bin"
if [ ! -f "$src" ]; then
warn "Binary not found: $bin — skipping. Run 'make build' first."
continue
fi
info " Installing $bin"
install -m 0755 -o root -g root "$src" "$BIN_DIR/$bin"
done
# Step 3: Create service directory structure.
info "Creating $SRV_DIR"
install -d -m 0750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$SRV_DIR"
install -d -m 0750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$SRV_DIR/certs"
install -d -m 0750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$SRV_DIR/backups"
# Install example config files; never overwrite existing configs.
for f in mcias.conf.example mcias.env.example; do
src="$DEPLOY_DIR/examples/$f"
dst="$SRV_DIR/$f"
if [ -f "$src" ]; then
install -m 0640 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$src" "$dst" 2>/dev/null || true
fi
done
# Step 4: Install systemd units.
if [ -d "$SYSTEMD_DIR" ]; then
info "Installing systemd units to $SYSTEMD_DIR"
for unit in mcias.service mcias-backup.service mcias-backup.timer; do
src="$DEPLOY_DIR/systemd/$unit"
if [ -f "$src" ]; then
install -m 0644 -o root -g root "$src" "$SYSTEMD_DIR/$unit"
info " Installed $unit"
fi
done
info "Reloading systemd daemon"
systemctl daemon-reload 2>/dev/null || warn "systemctl not available; reload manually."
info "Enabling backup timer"
systemctl enable mcias-backup.timer 2>/dev/null || warn "Could not enable timer; enable manually with: systemctl enable mcias-backup.timer"
else
warn "systemd not found at $SYSTEMD_DIR; skipping service unit installation."
fi
# Step 5: Install man pages.
if [ -d "$REPO_ROOT/man/man1" ]; then
install -d -m 0755 -o root -g root "$MAN_DIR"
info "Installing man pages to $MAN_DIR"
for page in "$REPO_ROOT/man/man1"/*.1.gz; do
[ -f "$page" ] || continue
install -m 0644 -o root -g root "$page" "$MAN_DIR/"
done
# Also install uncompressed pages if no gz versions exist.
for page in "$REPO_ROOT/man/man1"/*.1; do
[ -f "$page" ] || continue
gzname="${MAN_DIR}/$(basename "$page").gz"
if [ ! -f "$gzname" ]; then
gzip -c "$page" > "$gzname"
chmod 0644 "$gzname"
fi
done
fi
# ---------------------------------------------------------------------------
# Post-install instructions
# ---------------------------------------------------------------------------
cat <<EOF
==========================================================================
MCIAS installed successfully.
==========================================================================
Next steps:
1. Generate a TLS certificate and key:
# Self-signed (development / personal use):
openssl req -x509 -newkey ed25519 -days 3650 \\
-keyout /srv/mcias/certs/server.key \\
-out /srv/mcias/certs/server.crt \\
-subj "/CN=auth.example.com" \\
-nodes
chmod 0640 /srv/mcias/certs/server.key
chown mcias:mcias /srv/mcias/certs/server.key /srv/mcias/certs/server.crt
2. Copy and edit the configuration file:
cp /srv/mcias/mcias.conf.example /srv/mcias/mcias.toml
\$EDITOR /srv/mcias/mcias.toml
chmod 0640 /srv/mcias/mcias.toml
chown mcias:mcias /srv/mcias/mcias.toml
3. Set the master key passphrase:
cp /srv/mcias/mcias.env.example /srv/mcias/env
\$EDITOR /srv/mcias/env # replace the placeholder passphrase
chmod 0640 /srv/mcias/env
chown mcias:mcias /srv/mcias/env
IMPORTANT: Back up the passphrase to a secure offline location.
Losing it means losing access to all encrypted data in the database.
4. Enable and start the service:
systemctl enable mcias
systemctl start mcias
systemctl status mcias
The backup timer was enabled automatically. Verify with:
systemctl status mcias-backup.timer
5. Create the first admin account using mciasdb (while the server is
running, or before first start):
MCIAS_MASTER_PASSPHRASE=\$(grep MCIAS_MASTER_PASSPHRASE /srv/mcias/env | cut -d= -f2) \\
mciasdb --config /srv/mcias/mcias.toml account create \\
--username admin --type human
Then set a password:
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /srv/mcias/mcias.toml \\
account set-password --id <UUID>
And grant the admin role:
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /srv/mcias/mcias.toml \\
role grant --id <UUID> --role admin
For full documentation, see: man mciassrv
==========================================================================
EOF