236 lines
5.8 KiB
Markdown
236 lines
5.8 KiB
Markdown
# Deploying the audit endpoint on the IONOS VPS
|
|
|
|
The website itself stays on the Bitpalast webspace (static files via FTP). Only
|
|
this endpoint runs on the VPS, alongside the existing Git and Nextcloud
|
|
services. Nothing here modifies their configuration.
|
|
|
|
Assumes Debian or Ubuntu with `sudo`. Adjust package commands for other
|
|
distributions.
|
|
|
|
---
|
|
|
|
## 0. DNS
|
|
|
|
Create an **A record** `api.sascha-bach.de` pointing at the VPS IP address
|
|
(and an AAAA record if the VPS has IPv6). Wait until it resolves:
|
|
|
|
```bash
|
|
dig +short api.sascha-bach.de
|
|
```
|
|
|
|
Nothing below works until this answers.
|
|
|
|
---
|
|
|
|
## 1. Survey the machine first
|
|
|
|
```bash
|
|
ssh <user>@<vps>
|
|
|
|
# Which web server is in front? The nginx config below assumes nginx.
|
|
systemctl is-active nginx apache2 2>/dev/null
|
|
|
|
# Is port 3001 free? Gitea commonly sits on 3000, so check before assuming.
|
|
sudo ss -tlnp | grep -E ':(3000|3001)\b' || echo "3001 frei"
|
|
|
|
# Node 20 or newer present?
|
|
node --version 2>/dev/null || echo "Node fehlt"
|
|
```
|
|
|
|
If **Apache** is in front instead of nginx, skip step 6 and set up an Apache
|
|
`ProxyPass` for `api.sascha-bach.de` instead — the rest is unchanged.
|
|
|
|
If **3001 is taken**, pick another free port and change it in three places:
|
|
`.env` (`PORT`), the nginx `proxy_pass` lines, and nothing else.
|
|
|
|
---
|
|
|
|
## 2. Install Node (only if missing)
|
|
|
|
```bash
|
|
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
|
sudo apt-get install -y nodejs
|
|
node --version # must be >= 20
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Dedicated service user
|
|
|
|
The endpoint must not run as root and must not share the Git user's account.
|
|
|
|
```bash
|
|
sudo useradd --system --create-home --home-dir /var/lib/auditapi \
|
|
--shell /usr/sbin/nologin auditapi
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Fetch the code
|
|
|
|
```bash
|
|
sudo mkdir -p /opt/audit-endpoint
|
|
sudo chown auditapi:auditapi /opt/audit-endpoint
|
|
|
|
sudo -u auditapi git clone https://git.sascha-bach.de/saschabach/portfolio-page.git \
|
|
/opt/audit-endpoint
|
|
```
|
|
|
|
If the repository is private, use a deploy token or an SSH key belonging to
|
|
`auditapi`.
|
|
|
|
```bash
|
|
cd /opt/audit-endpoint/backend
|
|
sudo -u auditapi npm install --omit=dev
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Configuration
|
|
|
|
```bash
|
|
sudo -u auditapi cp /opt/audit-endpoint/backend/.env.example \
|
|
/opt/audit-endpoint/backend/.env
|
|
sudo -u auditapi nano /opt/audit-endpoint/backend/.env
|
|
```
|
|
|
|
Fill in:
|
|
|
|
| Variable | Value |
|
|
| --- | --- |
|
|
| `PORT` | `3001` (or the free port from step 1) |
|
|
| `ALLOWED_ORIGINS` | `https://sascha-bach.de,https://www.sascha-bach.de` — the **website** origin, not the API origin |
|
|
| `AUDIT_RECIPIENT` | where requests are delivered |
|
|
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` | mailbox credentials |
|
|
| `SMTP_FROM` | an address **on your own domain**, so SPF and DMARC pass |
|
|
|
|
Lock the file down — it holds the mailbox password:
|
|
|
|
```bash
|
|
sudo chmod 600 /opt/audit-endpoint/backend/.env
|
|
sudo chown auditapi:auditapi /opt/audit-endpoint/backend/.env
|
|
```
|
|
|
|
Smoke-test before wiring up systemd:
|
|
|
|
```bash
|
|
cd /opt/audit-endpoint/backend && sudo -u auditapi node server.js
|
|
# expect: Audit endpoint listening on 127.0.0.1:3001
|
|
# Ctrl+C
|
|
```
|
|
|
|
---
|
|
|
|
## 6. systemd service
|
|
|
|
```bash
|
|
sudo cp /opt/audit-endpoint/backend/deploy/audit-endpoint.service \
|
|
/etc/systemd/system/
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now audit-endpoint
|
|
|
|
systemctl status audit-endpoint --no-pager
|
|
curl -s http://127.0.0.1:3001/health # → {"status":"ok"}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. nginx vhost
|
|
|
|
Adding a file and reloading never touches the running Git or Nextcloud vhosts.
|
|
|
|
```bash
|
|
sudo cp /opt/audit-endpoint/backend/deploy/nginx-api.sascha-bach.de.conf \
|
|
/etc/nginx/sites-available/api.sascha-bach.de
|
|
sudo ln -s /etc/nginx/sites-available/api.sascha-bach.de \
|
|
/etc/nginx/sites-enabled/
|
|
|
|
# Validates the WHOLE config. If this fails, nothing has changed yet.
|
|
sudo nginx -t
|
|
|
|
# reload, not restart - existing connections to git/nextcloud survive
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
---
|
|
|
|
## 8. TLS certificate
|
|
|
|
```bash
|
|
sudo apt-get install -y certbot python3-certbot-nginx # if missing
|
|
sudo certbot --nginx -d api.sascha-bach.de
|
|
```
|
|
|
|
certbot only edits the `api.sascha-bach.de` block. Verify renewal works:
|
|
|
|
```bash
|
|
sudo certbot renew --dry-run
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Verify from outside
|
|
|
|
```bash
|
|
curl -sI https://api.sascha-bach.de/health # → HTTP/2 200
|
|
|
|
curl -s -X POST https://api.sascha-bach.de/api/audit-request \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Origin: https://sascha-bach.de' \
|
|
-d '{"name":"Test","company":"Test GmbH","email":"du@example.de",
|
|
"url":"https://example.de","role":"client","motivation":"Test",
|
|
"businessConfirmation":"true","gdprConsent":"true","website":""}'
|
|
# → {"success":true,...} and an email arrives
|
|
```
|
|
|
|
Then confirm the protections actually bite:
|
|
|
|
```bash
|
|
# Foreign origin → rejected
|
|
curl -s -X POST https://api.sascha-bach.de/api/audit-request \
|
|
-H 'Content-Type: application/json' -H 'Origin: https://evil.example' \
|
|
-d '{}' -o /dev/null -w '%{http_code}\n'
|
|
|
|
# Fourth request within 15 minutes → 429
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Point the website at it
|
|
|
|
Back on the workstation, in the project root:
|
|
|
|
```bash
|
|
echo 'VITE_AUDIT_ENDPOINT=https://api.sascha-bach.de/api/audit-request' > .env
|
|
npm run deploy
|
|
```
|
|
|
|
Vite bakes the value in at build time, so this must happen **before** the
|
|
upload. `.env` is gitignored.
|
|
|
|
The hostname must match in three places or the browser blocks the request:
|
|
|
|
- `public/.htaccess` → `connect-src 'self' https://api.sascha-bach.de`
|
|
- `VITE_AUDIT_ENDPOINT`
|
|
- the nginx `server_name` and its certificate
|
|
|
|
---
|
|
|
|
## Updating later
|
|
|
|
```bash
|
|
cd /opt/audit-endpoint
|
|
sudo -u auditapi git pull
|
|
cd backend && sudo -u auditapi npm install --omit=dev
|
|
sudo systemctl restart audit-endpoint
|
|
```
|
|
|
|
## Logs
|
|
|
|
```bash
|
|
journalctl -u audit-endpoint -f
|
|
```
|
|
|
|
Request contents are never logged, only that a request was forwarded — see the
|
|
note in `services/emailService.js`.
|