Native VM install kit: systemd units, launcher, install and upgrade scripts; backups, restore and doctor without docker exec; DEPLOY.md VM, NPM and NFS sections

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-30 18:08:48 -06:00
co-authored by Claude Opus 5.5
parent f025479358
commit 45a1d1a42a
22 changed files with 2357 additions and 413 deletions
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Run one packetstation command on this VM the way the services run it: as the packetstation user,
# with the env file, the secrets as systemd credentials and the same sandbox, through systemd-run.
# Output comes back to this terminal and the exit code is the command's.
#
# Installed as /usr/local/sbin/ps-run by deploy/vm/install.sh. Run it as root (sudo).
#
# Usage:
# ps-run doctor [--skip visual,mfiles,ldap,smtp]
# ps-run job run visual-feed
# ps-run ad-groups --list
# ps-run healthcheck worker
# ps-run version
#
# Every secret file present in /etc/packetstation/secrets is handed over as a credential; the
# folder itself stays out of reach of the command. No value is read or printed here.
set -euo pipefail
ENV_FILE="${PS_ENV_FILE_PATH:-/etc/packetstation/packetstation.env}"
SECRETS_DIR="${PS_SECRETS_DIR:-/etc/packetstation/secrets}"
CURRENT="${PS_CURRENT:-/opt/packetstation/current}"
SYSTEMD_RUN="${SYSTEMD_RUN:-systemd-run}"
SERVICE_USER="${PS_USER:-packetstation}"
ASSETS_DIR_RW="${PS_ASSETS_DIR:-/var/lib/packetstation/assets}"
ARCHIVE_DIR_RW="${PS_ARCHIVE_MOUNT:-/mnt/packetstation-archive}"
die() { printf 'ps-run: %s\n' "$*" >&2; exit 2; }
if [[ $# -eq 0 || "$1" == "-h" || "$1" == "--help" ]]; then
sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//' >&2
exit 2
fi
if [[ "${PS_RUN_ALLOW_NONROOT:-}" != "1" && "$(id -u)" != "0" ]]; then
die "run as root (sudo ps-run ...)"
fi
[[ -r "$ENV_FILE" ]] || die "env file $ENV_FILE is missing"
[[ -x "$CURRENT/deploy/vm/ps-launch.sh" ]] || die "no release at $CURRENT (run ps-upgrade first)"
props=(
-p "User=$SERVICE_USER"
-p "Group=$SERVICE_USER"
-p "EnvironmentFile=$ENV_FILE"
-p UMask=0027
-p NoNewPrivileges=yes
-p ProtectSystem=strict
-p ProtectHome=yes
-p PrivateTmp=yes
-p PrivateDevices=yes
-p ProtectKernelTunables=yes
-p ProtectKernelModules=yes
-p ProtectControlGroups=yes
-p RestrictSUIDSGID=yes
-p "ReadWritePaths=$ASSETS_DIR_RW"
-p "ReadWritePaths=-$ARCHIVE_DIR_RW"
-p "InaccessiblePaths=-$SECRETS_DIR"
)
for name in postgres_app_password postgres_migrate_password visualkit_password mfileskit_password \
ldap_bind_password session_key seal_key; do
if [[ -f "$SECRETS_DIR/$name" ]]; then
props+=(-p "LoadCredential=$name:$SECRETS_DIR/$name")
fi
done
exec "$SYSTEMD_RUN" --wait --pipe --collect --quiet --service-type=exec \
--description="Packet Station: packetstation $1" \
"${props[@]}" -- "$CURRENT/deploy/vm/ps-launch.sh" "$@"