Add deployment scripts
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
*.exe
|
*.exe
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Deploying mediator
|
||||||
|
|
||||||
|
Target: Ubuntu 22.04 VPS, nginx in front, app in `/opt/mediator`.
|
||||||
|
|
||||||
|
The app runs as an unprivileged `mediator` system user, listens only on
|
||||||
|
`127.0.0.1:8080`, and nginx proxies the public domain to it. Polls live in
|
||||||
|
`/opt/mediator/data/polls.json` — that one file is the whole backup.
|
||||||
|
|
||||||
|
## One-time setup (on the server)
|
||||||
|
|
||||||
|
Copy the deploy files over and run the setup script as root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scp deploy/mediator.service deploy/mediator.nginx.conf deploy/setup-server.sh himalia:/tmp/
|
||||||
|
ssh himalia
|
||||||
|
cd /tmp
|
||||||
|
sudo ./setup-server.sh <your-ssh-user> <your-domain>
|
||||||
|
```
|
||||||
|
|
||||||
|
The script creates the `mediator` user, installs the systemd unit and nginx
|
||||||
|
site, and adds a sudoers rule so your user can `systemctl restart mediator`
|
||||||
|
without a password (that keeps deploys to a single password prompt).
|
||||||
|
|
||||||
|
Then get a certificate:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo certbot --nginx -d <your-domain>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Every deploy (from your machine)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./deploy/deploy.sh # or: ./deploy/deploy.sh otherhost
|
||||||
|
```
|
||||||
|
|
||||||
|
Cross-compiles a static linux/amd64 binary, streams it to the server over
|
||||||
|
one ssh connection (one password prompt), swaps it in atomically, restarts
|
||||||
|
the service, and prints `active` on success.
|
||||||
|
|
||||||
|
## Useful commands on the server
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl status mediator
|
||||||
|
journalctl -u mediator -f
|
||||||
|
cp /opt/mediator/data/polls.json ~/polls-backup.json # backup
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build mediator for linux/amd64 and push it to the server.
|
||||||
|
# Run from your machine (Git Bash on Windows works):
|
||||||
|
#
|
||||||
|
# ./deploy/deploy.sh [host] default host: himalia
|
||||||
|
#
|
||||||
|
# Requires the one-time server setup (setup-server.sh) to have been run.
|
||||||
|
# Asks for the ssh password once; the binary is streamed over that same
|
||||||
|
# connection, swapped in atomically, and the service restarted.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST="${1:-himalia}"
|
||||||
|
APP_DIR=/opt/mediator
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
echo "Building linux/amd64 binary..."
|
||||||
|
mkdir -p dist
|
||||||
|
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
||||||
|
go build -trimpath -ldflags "-s -w" -o dist/mediator-linux-amd64 .
|
||||||
|
|
||||||
|
echo "Uploading to $HOST and restarting mediator..."
|
||||||
|
ssh "$HOST" "set -e
|
||||||
|
cat > $APP_DIR/mediator.new
|
||||||
|
chmod 755 $APP_DIR/mediator.new
|
||||||
|
mv -f $APP_DIR/mediator.new $APP_DIR/mediator
|
||||||
|
sudo systemctl restart mediator
|
||||||
|
sleep 1
|
||||||
|
systemctl is-active mediator
|
||||||
|
" < dist/mediator-linux-amd64
|
||||||
|
|
||||||
|
echo "Deployed $(git rev-parse --short HEAD) to $HOST."
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# nginx site for mediator. Installed by setup-server.sh to
|
||||||
|
# /etc/nginx/sites-available/mediator with the real domain substituted.
|
||||||
|
# For HTTPS run `certbot --nginx -d <domain>` afterwards; certbot rewrites
|
||||||
|
# this file to add the TLS server block and the HTTP->HTTPS redirect.
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name mediator.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8080;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=mediator - date polls for friend groups
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=mediator
|
||||||
|
Group=mediator
|
||||||
|
ExecStart=/opt/mediator/mediator -addr 127.0.0.1:8080 -data /opt/mediator/data
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
# Sandboxing: the service only needs to read its binary and write its data dir.
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/opt/mediator/data
|
||||||
|
ProtectHome=true
|
||||||
|
PrivateTmp=true
|
||||||
|
PrivateDevices=true
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
LockPersonality=true
|
||||||
|
MemoryDenyWriteExecute=true
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# One-time server setup for mediator on Ubuntu. Run as root ON THE SERVER,
|
||||||
|
# from a directory containing mediator.service and mediator.nginx.conf:
|
||||||
|
#
|
||||||
|
# sudo ./setup-server.sh <deploy-user> <domain>
|
||||||
|
#
|
||||||
|
# <deploy-user> the account you ssh in as; it gets write access to
|
||||||
|
# /opt/mediator and passwordless `systemctl restart mediator`
|
||||||
|
# <domain> the public hostname nginx should answer on
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEPLOY_USER="${1:?usage: setup-server.sh <deploy-user> <domain>}"
|
||||||
|
DOMAIN="${2:?usage: setup-server.sh <deploy-user> <domain>}"
|
||||||
|
APP_DIR=/opt/mediator
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
id -u "$DEPLOY_USER" >/dev/null
|
||||||
|
|
||||||
|
# Unprivileged system user the service runs as.
|
||||||
|
id -u mediator >/dev/null 2>&1 ||
|
||||||
|
useradd --system --home "$APP_DIR" --shell /usr/sbin/nologin mediator
|
||||||
|
|
||||||
|
# Deploy user owns the app dir (to replace the binary); the service user
|
||||||
|
# owns only the data dir (the single thing it writes).
|
||||||
|
mkdir -p "$APP_DIR/data"
|
||||||
|
chown "$DEPLOY_USER" "$APP_DIR"
|
||||||
|
chmod 755 "$APP_DIR"
|
||||||
|
chown mediator:mediator "$APP_DIR/data"
|
||||||
|
chmod 750 "$APP_DIR/data"
|
||||||
|
|
||||||
|
install -m 644 "$SCRIPT_DIR/mediator.service" /etc/systemd/system/mediator.service
|
||||||
|
|
||||||
|
sed "s/mediator\.example\.com/$DOMAIN/" "$SCRIPT_DIR/mediator.nginx.conf" \
|
||||||
|
> /etc/nginx/sites-available/mediator
|
||||||
|
ln -sf /etc/nginx/sites-available/mediator /etc/nginx/sites-enabled/mediator
|
||||||
|
|
||||||
|
# Let the deploy user restart the service without a sudo password,
|
||||||
|
# so deploy.sh needs exactly one (ssh) password prompt.
|
||||||
|
printf '%s ALL=(root) NOPASSWD: /usr/bin/systemctl restart mediator\n' "$DEPLOY_USER" \
|
||||||
|
> /etc/sudoers.d/mediator-deploy
|
||||||
|
chmod 440 /etc/sudoers.d/mediator-deploy
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable mediator
|
||||||
|
nginx -t
|
||||||
|
systemctl reload nginx
|
||||||
|
|
||||||
|
echo "Setup done. Now push a binary from your machine: ./deploy/deploy.sh"
|
||||||
|
echo "For HTTPS: certbot --nginx -d $DOMAIN"
|
||||||
Reference in New Issue
Block a user