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:
@@ -0,0 +1,70 @@
|
||||
# Packet Station: the web app (uvicorn on port 8000, the API and the built frontend).
|
||||
# Installed by deploy/vm/install.sh into /etc/systemd/system. See docs/DEPLOY.md, "Native VM install".
|
||||
# NPM on docker-1 is the only client allowed on port 8000 (packetstation-firewall.service).
|
||||
[Unit]
|
||||
Description=Packet Station web app
|
||||
Documentation=file:///opt/packetstation/current/docs/DEPLOY.md
|
||||
Requires=packetstation-migrate.service
|
||||
# Wants, not Requires: a restart of nftables.service must not restart the app. ExecStartPre checks the table.
|
||||
Wants=packetstation-firewall.service
|
||||
After=packetstation-migrate.service packetstation-firewall.service [email protected] network-online.target
|
||||
Wants=[email protected] network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=packetstation
|
||||
Group=packetstation
|
||||
EnvironmentFile=/etc/packetstation/packetstation.env
|
||||
# The app's secrets only. It never gets the VISUAL or M-Files password.
|
||||
LoadCredential=postgres_app_password:/etc/packetstation/secrets/postgres_app_password
|
||||
LoadCredential=ldap_bind_password:/etc/packetstation/secrets/ldap_bind_password
|
||||
LoadCredential=session_key:/etc/packetstation/secrets/session_key
|
||||
LoadCredential=seal_key:/etc/packetstation/secrets/seal_key
|
||||
# The app never listens without the firewall table in place ("+": this check runs as root).
|
||||
ExecStartPre=+/usr/sbin/nft list table inet packetstation
|
||||
ExecStart=/opt/packetstation/current/deploy/vm/ps-launch.sh app
|
||||
SyslogIdentifier=packetstation-app
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
TimeoutStopSec=30s
|
||||
WorkingDirectory=/
|
||||
UMask=0027
|
||||
MemoryMax=1G
|
||||
LimitNOFILE=16384
|
||||
|
||||
# Sandbox. Read-only file system except the assets and the archive mount.
|
||||
# The leading "-" lets the app start while the archive is not mounted (ARCHIVE_ROOT unset).
|
||||
ReadWritePaths=/var/lib/packetstation/assets
|
||||
ReadWritePaths=-/mnt/packetstation-archive
|
||||
InaccessiblePaths=/etc/packetstation/secrets
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
ProtectProc=invisible
|
||||
# AF_NETLINK: glibc's getaddrinfo asks the kernel for the local addresses through it.
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
RemoveIPC=yes
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
# Not set: MemoryDenyWriteExecute. pypdfium2 goes through ctypes, whose callbacks may need
|
||||
# writable and executable memory. Turn it on only after a filing and a print pass on the test VM.
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,21 @@
|
||||
# Packet Station: only NPM on docker-1 (TRUSTED_PROXY_HOST) and the VM itself may reach port 8000.
|
||||
# Loads /etc/packetstation/packetstation.nft, which deploy/vm/install.sh writes from the env file.
|
||||
# The table is Packet Station's own (inet packetstation); other rules on the VM are left alone.
|
||||
[Unit]
|
||||
Description=Packet Station firewall for the app port
|
||||
Documentation=file:///opt/packetstation/current/docs/DEPLOY.md
|
||||
Before=network-pre.target packetstation-app.service
|
||||
Wants=network-pre.target
|
||||
# A restart of nftables.service flushes the whole ruleset. Load the table again after it.
|
||||
After=nftables.service
|
||||
PartOf=nftables.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/sbin/nft -f /etc/packetstation/packetstation.nft
|
||||
ExecReload=/usr/sbin/nft -f /etc/packetstation/packetstation.nft
|
||||
ExecStop=-/usr/sbin/nft delete table inet packetstation
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,52 @@
|
||||
# Packet Station: SQL migrations, once per boot and once per upgrade, before the app and the worker.
|
||||
# Installed by deploy/vm/install.sh into /etc/systemd/system. See docs/DEPLOY.md, "Native VM install".
|
||||
# ps-upgrade restarts this unit after switching /opt/packetstation/current to a new release.
|
||||
[Unit]
|
||||
Description=Packet Station database migrations
|
||||
Documentation=file:///opt/packetstation/current/docs/DEPLOY.md
|
||||
Wants=[email protected] network-online.target
|
||||
After=[email protected] network-online.target
|
||||
Before=packetstation-app.service packetstation-worker.service
|
||||
ConditionPathExists=/opt/packetstation/current/deploy/vm/ps-launch.sh
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
User=packetstation
|
||||
Group=packetstation
|
||||
EnvironmentFile=/etc/packetstation/packetstation.env
|
||||
# migrate needs both passwords: it logs in as ps_migrate and grants to ps_app.
|
||||
LoadCredential=postgres_migrate_password:/etc/packetstation/secrets/postgres_migrate_password
|
||||
LoadCredential=postgres_app_password:/etc/packetstation/secrets/postgres_app_password
|
||||
ExecStart=/opt/packetstation/current/deploy/vm/ps-launch.sh migrate
|
||||
SyslogIdentifier=packetstation-migrate
|
||||
TimeoutStartSec=15min
|
||||
WorkingDirectory=/
|
||||
UMask=0027
|
||||
|
||||
# Sandbox. The whole file system is read-only; migrate writes nothing outside PostgreSQL.
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
InaccessiblePaths=/etc/packetstation/secrets
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
ProtectProc=invisible
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
RemoveIPC=yes
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
# No [Install]: the app and the worker pull this unit in (Requires=).
|
||||
@@ -0,0 +1,67 @@
|
||||
# Packet Station: the worker (VISUAL feed, drawings, AD reconcile, integrity, backup check,
|
||||
# notifications, archive copies). Installed by deploy/vm/install.sh into /etc/systemd/system.
|
||||
# On production its first start is Spencer's approval of the scheduled VISUAL polling.
|
||||
[Unit]
|
||||
Description=Packet Station worker
|
||||
Documentation=file:///opt/packetstation/current/docs/DEPLOY.md
|
||||
Requires=packetstation-migrate.service
|
||||
After=packetstation-migrate.service [email protected] network-online.target
|
||||
Wants=[email protected] network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=packetstation
|
||||
Group=packetstation
|
||||
EnvironmentFile=/etc/packetstation/packetstation.env
|
||||
LoadCredential=postgres_app_password:/etc/packetstation/secrets/postgres_app_password
|
||||
LoadCredential=visualkit_password:/etc/packetstation/secrets/visualkit_password
|
||||
LoadCredential=mfileskit_password:/etc/packetstation/secrets/mfileskit_password
|
||||
LoadCredential=ldap_bind_password:/etc/packetstation/secrets/ldap_bind_password
|
||||
LoadCredential=seal_key:/etc/packetstation/secrets/seal_key
|
||||
ExecStart=/opt/packetstation/current/deploy/vm/ps-launch.sh worker
|
||||
SyslogIdentifier=packetstation-worker
|
||||
# /run/packetstation holds the heartbeat file (WORKER_BEAT_FILE, set by ps-launch.sh).
|
||||
RuntimeDirectory=packetstation
|
||||
RuntimeDirectoryMode=0755
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
TimeoutStopSec=60s
|
||||
WorkingDirectory=/
|
||||
UMask=0027
|
||||
MemoryMax=1G
|
||||
|
||||
# Sandbox. Read-only file system except the assets and the archive mount. The backup folder
|
||||
# (BACKUP_DIR) is only read, so it needs no entry here.
|
||||
ReadWritePaths=/var/lib/packetstation/assets
|
||||
ReadWritePaths=-/mnt/packetstation-archive
|
||||
InaccessiblePaths=/etc/packetstation/secrets
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
ProtectProc=invisible
|
||||
# AF_NETLINK: glibc's getaddrinfo asks the kernel for the local addresses through it.
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
LockPersonality=yes
|
||||
RemoveIPC=yes
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
# Not set: MemoryDenyWriteExecute (see packetstation-app.service).
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user