diff --git a/servers/psmf/dns-configuration.md b/servers/psmf/dns-configuration.md new file mode 100644 index 0000000..496ddfb --- /dev/null +++ b/servers/psmf/dns-configuration.md @@ -0,0 +1,157 @@ +## disable systemd-resolved + +```bash +sudo mkdir -p /etc/systemd/resolved.conf.d +sudo tee /etc/systemd/resolved.conf.d/disable-stub.conf < horst-1 Apache (10.0.0.5) +# Deployed during the horst-1 -> horst-2 migration. +# See servers/psmf/migration-plan.md. + +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + + # Long bodies / uploads from the legacy PHP intranet app. + client_max_body_size 64m; + + location / { + proxy_pass http://10.0.0.5; + + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_connect_timeout 10s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/servers/psmf/files/nginx/psmf-new.intranet b/servers/psmf/files/nginx/psmf-new.intranet new file mode 100644 index 0000000..8b24e9d --- /dev/null +++ b/servers/psmf/files/nginx/psmf-new.intranet @@ -0,0 +1,27 @@ +# Reverse proxy: psmf-new.intranet -> dockerized PHP intranet app on horst-2 (127.0.0.1:8080) +# See servers/psmf/migration-plan.md and servers/psmf/dns-configuration.md. + +server { + listen 80; + listen [::]:80; + + server_name psmf-new.intranet; + + # Long bodies / uploads from the PHP intranet app. + client_max_body_size 64m; + + location / { + proxy_pass http://127.0.0.1:8080; + + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_connect_timeout 10s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/servers/psmf/files/nginx/psmf.intranet b/servers/psmf/files/nginx/psmf.intranet new file mode 100644 index 0000000..9549828 --- /dev/null +++ b/servers/psmf/files/nginx/psmf.intranet @@ -0,0 +1,28 @@ +# Reverse proxy: psmf.intranet -> horst-1 Apache (10.0.0.5) +# Same target as the default site, but bound to the psmf.intranet host header. +# See servers/psmf/migration-plan.md and servers/psmf/dns-configuration.md. + +server { + listen 80; + listen [::]:80; + + server_name psmf.intranet; + + # Long bodies / uploads from the legacy PHP intranet app. + client_max_body_size 64m; + + location / { + proxy_pass http://10.0.0.5; + + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_connect_timeout 10s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} diff --git a/servers/psmf/files/srv/psmf/docker-compose.yml b/servers/psmf/files/srv/psmf/docker-compose.yml new file mode 100644 index 0000000..f0e09cb --- /dev/null +++ b/servers/psmf/files/srv/psmf/docker-compose.yml @@ -0,0 +1,15 @@ +services: + app: + image: gitea.home.hrajfrisbee.cz/psmf/psmf-intranet:1.03 + ports: + - "8080:8080" + environment: + DB_HOST: ${DB_HOST:-host.docker.internal} + DB_NAME: ${DB_NAME:-psmf} + DB_USER: ${DB_USER:-psmf} + DB_PASS: ${DB_PASS:-psmf} + # PSMF_BASE_URL: http://127.0.0.1:8080 # override for PDF export when behind a proxy + PSMF_HEADER_TEXT: "Vítejte na intranetu PSMF (PHP {PHP_VERSION})" + PSMF_HEADER_BG: "#a0b000" + PSMF_SHOW_PHP_VERSION: "1" + restart: unless-stopped diff --git a/servers/psmf/files/tailscaled-ts2.service b/servers/psmf/files/tailscaled-ts2.service new file mode 100644 index 0000000..f6db8c3 --- /dev/null +++ b/servers/psmf/files/tailscaled-ts2.service @@ -0,0 +1,21 @@ +# location: /etc/systemd/system/tailscaled-ts2.service +[Unit] +Description=tailscaled in ts2 netns +After=ts2-netns.service network-online.target +Requires=ts2-netns.service +BindsTo=ts2-netns.service + +[Service] +ExecStart=/usr/sbin/ip netns exec ts2 /usr/sbin/tailscaled \ + --state=/var/lib/tailscale-ts2/tailscaled.state \ + --socket=/run/tailscale-ts2/tailscaled.sock \ + --port=41642 \ + --tun=tailscale0 +Restart=on-failure +RestartSec=5s + +RuntimeDirectory=tailscale-ts2 +StateDirectory=tailscale-ts2 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/servers/psmf/files/ts2-netns-down b/servers/psmf/files/ts2-netns-down new file mode 100644 index 0000000..61571b0 --- /dev/null +++ b/servers/psmf/files/ts2-netns-down @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# best-effort cleanup, don't fail on missing pieces +# location: /usr/local/sbin/ts2-netns-down + +iptables -t nat -D POSTROUTING -s 10.200.0.0/30 -o br0 -j MASQUERADE 2>/dev/null || true +iptables -D FORWARD -i veth-ts2 -o br0 -j ACCEPT 2>/dev/null || true +iptables -D FORWARD -o veth-ts2 -i br0 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true + +ip link del veth-ts2 2>/dev/null || true +ip netns del ts2 2>/dev/null || true +rm -rf /etc/netns/ts2 \ No newline at end of file diff --git a/servers/psmf/files/ts2-netns-up b/servers/psmf/files/ts2-netns-up new file mode 100644 index 0000000..4fd4da5 --- /dev/null +++ b/servers/psmf/files/ts2-netns-up @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# location: /usr/local/sbin/ts2-netns-up +set -e + +# bail out clean if anything already exists from a previous run +ip netns list | grep -qw ts2 || ip netns add ts2 + +ip link show veth-ts2 &>/dev/null || \ + ip link add veth-ts2 type veth peer name veth-ts2-ns + +# move peer into ns only if it's still on the host +ip link show veth-ts2-ns &>/dev/null && \ + ip link set veth-ts2-ns netns ts2 + +ip addr add 10.200.0.1/30 dev veth-ts2 2>/dev/null || true +ip link set veth-ts2 up + +ip -n ts2 addr add 10.200.0.2/30 dev veth-ts2-ns 2>/dev/null || true +ip -n ts2 link set veth-ts2-ns up +ip -n ts2 link set lo up +ip -n ts2 route add default via 10.200.0.1 2>/dev/null || true + +mkdir -p /etc/netns/ts2 +echo "nameserver 1.1.1.1" > /etc/netns/ts2/resolv.conf + +sysctl -wq net.ipv4.ip_forward=1 + +# -C checks if rule exists, -A appends only if missing +iptables -t nat -C POSTROUTING -s 10.200.0.0/30 -o br0 -j MASQUERADE 2>/dev/null || \ + iptables -t nat -A POSTROUTING -s 10.200.0.0/30 -o br0 -j MASQUERADE + +iptables -C FORWARD -i veth-ts2 -o br0 -j ACCEPT 2>/dev/null || \ + iptables -A FORWARD -i veth-ts2 -o br0 -j ACCEPT + +iptables -C FORWARD -o veth-ts2 -i br0 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ + iptables -A FORWARD -o veth-ts2 -i br0 -m state --state RELATED,ESTABLISHED -j ACCEPT + +# iptables rules + sysctl inside ts2 ns +# Forwarding inside ts2 +ip netns exec ts2 sysctl -wq net.ipv4.ip_forward=1 + +# DNAT incoming ssh to host +ip netns exec ts2 iptables -t nat -C PREROUTING -i tailscale0 -p tcp --dport 22 -j DNAT --to-destination 10.200.0.1:22 2>/dev/null || \ + ip netns exec ts2 iptables -t nat -A PREROUTING -i tailscale0 -p tcp --dport 22 -j DNAT --to-destination 10.200.0.1:22 + +# DNAT incoming http to host nginx +ip netns exec ts2 iptables -t nat -C PREROUTING -i tailscale0 -p tcp --dport 80 -j DNAT --to-destination 10.200.0.1:80 2>/dev/null || \ + ip netns exec ts2 iptables -t nat -A PREROUTING -i tailscale0 -p tcp --dport 80 -j DNAT --to-destination 10.200.0.1:80 + +# DNAT incoming 8080 to docker web app in root ns +ip netns exec ts2 iptables -t nat -C PREROUTING -i tailscale0 -p tcp --dport 8080 -j DNAT --to-destination 10.200.0.1:8080 2>/dev/null || \ + ip netns exec ts2 iptables -t nat -A PREROUTING -i tailscale0 -p tcp --dport 8080 -j DNAT --to-destination 10.200.0.1:8080 + +ip netns exec ts2 iptables -t nat -C POSTROUTING -o veth-ts2-ns -j MASQUERADE 2>/dev/null || \ + ip netns exec ts2 iptables -t nat -A POSTROUTING -o veth-ts2-ns -j MASQUERADE \ No newline at end of file diff --git a/servers/psmf/files/ts2-netns.service b/servers/psmf/files/ts2-netns.service new file mode 100644 index 0000000..f1a0fda --- /dev/null +++ b/servers/psmf/files/ts2-netns.service @@ -0,0 +1,17 @@ +# /etc/systemd/system/ts2-netns.service + +[Unit] +Description=ts2 netns + veth + NAT +After=network-online.target +Wants=network-online.target +# tie tailscaled lifecycle to this +Before=tailscaled-ts2.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/local/sbin/ts2-netns-up +ExecStop=/usr/local/sbin/ts2-netns-down + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/servers/psmf/files/ts2-resolv.conf b/servers/psmf/files/ts2-resolv.conf new file mode 100644 index 0000000..430e5d2 --- /dev/null +++ b/servers/psmf/files/ts2-resolv.conf @@ -0,0 +1,2 @@ +# location: /etc/netns/ts2/resolv.conf +nameserver 1.1.1.1 \ No newline at end of file diff --git a/servers/psmf/horst-2/crontab b/servers/psmf/horst-2/crontab new file mode 100644 index 0000000..e694283 --- /dev/null +++ b/servers/psmf/horst-2/crontab @@ -0,0 +1,6 @@ +# backups +15 11,15,19,22 * * * root /srv/bin/psmf-cli db backup --env-file ~/.psmf/horst-2.env --db psmf --dir /srv/backups/horst-2 -v >> /var/log/psmf-backup.log 2>&1 +17 11,15,19,22 * * * root /srv/bin/psmf-cli db backup --env-file ~/.psmf/horst.env --db psmf --dir /srv/backups/horst -v >> /var/log/psmf-backup.log 2>&1 +# prune backups +20 11,15,19,22 * * * root /srv/bin/psmf-cli db prune-backups --db psmf --dir /srv/backups/horst-2 --keep-within 2w --keep-weekly 8 --keep-monthly 120 >> /var/log/psmf-backup.log 2>&1 +21 11,15,19,22 * * * root /srv/bin/psmf-cli db prune-backups --db psmf --dir /srv/backups/horst --keep-within 2w --keep-weekly 8 --keep-monthly 120 >> /var/log/psmf-backup.log 2>&1 \ No newline at end of file diff --git a/servers/psmf/migration-plan.md b/servers/psmf/migration-plan.md new file mode 100644 index 0000000..f94cc25 --- /dev/null +++ b/servers/psmf/migration-plan.md @@ -0,0 +1,93 @@ +# Migration from horst-1 to horst-2 + +## postgres database + +```bash +# [DONE] install postgres +apt update +apt install -y curl ca-certificates gnupg lsb-release + +# PGDG signing key (keyring-style, not the deprecated apt-key) +sudo install -d /usr/share/postgresql-common/pgdg +sudo curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ + -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc + +# repo +echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ +https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \ +| sudo tee /etc/apt/sources.list.d/pgdg.list + +sudo apt update +sudo apt install -y postgresql-18 postgresql-contrib-18 + +# backup database from horst-1 +# users backup +pg_dumpall --globals-only -h 10.0.0.5 -U postgres > /tmp/pg-dumpall-globals +# data backup +pg_dump -h 10.0.0.5 -U postgres -C psmf > /tmp/psmf.pgsql + +# restore backup of database from horst-1 +# create users +cat /tmp/pg-dumpall-globals | psql +# restore psmf database +cat /tmp/psmf.pgsql | psql + +# install docker oin horst-2 +# Docker's GPG key (keyring, not apt-key) +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ + -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# repo +echo \ +"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ +https://download.docker.com/linux/ubuntu \ +$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +sudo apt-get update +sudo apt-get install -y \ + docker-ce docker-ce-cli containerd.io \ + docker-buildx-plugin docker-compose-plugin + + +# [DONE] dockerize php intranet app +# dockerized + upgraded to PHP 8.3.31 + +# [DONE] start psmf app on horst-2 and point it to database on horst + +# [TODO] point php app on horst-1 to horst-2 + +# validate application is still working +# [OK] - wkhtml - PDF printing of html pages +# prepare local backups +# validate if pg sync to api still works from horst-1 +# set sync to api on horst-2 +# install nginx and configure default site as reverse proxy to horst-1 +sudo apt-get update +sudo apt-get install -y nginx + +# drop the proxy config in place (file lives in this repo at servers/psmf/files/nginx/default) +sudo install -m 0644 -o root -g root \ + servers/psmf/files/nginx/default /etc/nginx/sites-available/default + +# sites-enabled/default is already a symlink to ../sites-available/default on a stock Ubuntu install +sudo nginx -t +sudo systemctl reload nginx +sudo systemctl enable nginx + +# intranet vhosts: psmf.intranet -> horst-1, psmf-new.intranet -> local docker app +# (depends on the dnsmasq setup in servers/psmf/dns-configuration.md) +sudo install -m 0644 -o root -g root \ + servers/psmf/files/nginx/psmf.intranet /etc/nginx/sites-available/psmf.intranet +sudo install -m 0644 -o root -g root \ + servers/psmf/files/nginx/psmf-new.intranet /etc/nginx/sites-available/psmf-new.intranet + +sudo ln -sf ../sites-available/psmf.intranet /etc/nginx/sites-enabled/psmf.intranet +sudo ln -sf ../sites-available/psmf-new.intranet /etc/nginx/sites-enabled/psmf-new.intranet + +sudo nginx -t +sudo systemctl reload nginx + +``` \ No newline at end of file diff --git a/servers/psmf/readme.md b/servers/psmf/readme.md new file mode 100644 index 0000000..eb56453 --- /dev/null +++ b/servers/psmf/readme.md @@ -0,0 +1,33 @@ +# Servers + +### Horst + +- ip: 10.0.0.5 +- os: CentOS release 6.9 (Final) +- services: + - apache tcp/80 + - postgres tcp/5432 + - samba (not really in use) + - cronjobs + - /srv/bin/pg_psmf_dump.sh + - /srv/bin/psmf-prepare-tables.sh 2023 2027 + - /srv/bin/psmf-api-sync.sh + +- specials: + - some binaries/libraries relevant to PDF printing from the app WKHTML + +```bash +# crontab content +00 11,15,19,22 * * * root /srv/bin/pg_psmf_dump.sh +55 10,14,18,21 * * * root /srv/bin/psmf-prepare-tables.sh 2023 2027 +05 11,15,19,22 * * * root /srv/bin/psmf-api-sync.sh +``` + +#### Problems + +- operating system is already that old, that there is not reasonable and safe upgrade path to something current + + +### Horst-2 + +- ip: 10.0.0.6 diff --git a/servers/psmf/tailscale-second-instance.md b/servers/psmf/tailscale-second-instance.md new file mode 100644 index 0000000..ff89626 --- /dev/null +++ b/servers/psmf/tailscale-second-instance.md @@ -0,0 +1,39 @@ +```bash +# Create namespace +sudo ip netns add ts2 + +# veth pair: host side <-> ns side +sudo ip link add veth-ts2 type veth peer name veth-ts2-ns +sudo ip link set veth-ts2-ns netns ts2 + +# Host side +sudo ip addr add 10.200.0.1/30 dev veth-ts2 +sudo ip link set veth-ts2 up + +# Namespace side +sudo ip -n ts2 addr add 10.200.0.2/30 dev veth-ts2-ns +sudo ip -n ts2 link set veth-ts2-ns up +sudo ip -n ts2 link set lo up +sudo ip -n ts2 route add default via 10.200.0.1 + +# DNS inside the netns +sudo mkdir -p /etc/netns/ts2 +echo "nameserver 1.1.1.1" | sudo tee /etc/netns/ts2/resolv.conf + +# NAT from the netns out to the world (replace eth0 with your egress iface) +sudo sysctl -w net.ipv4.ip_forward=1 +sudo iptables -t nat -A POSTROUTING -s 10.200.0.0/30 -o br0 -j MASQUERADE +sudo iptables -A FORWARD -i veth-ts2 -o br0 -j ACCEPT +sudo iptables -A FORWARD -o veth-ts2 -i br0 -m state --state RELATED,ESTABLISHED -j ACCEPT + + +# quick sanity check +sudo ip netns exec ts2 curl https://controlplane.tailscale.com +# expect: "OK" +``` + +```bash +# ts2 alias +echo 'alias ts2="sudo ip netns exec ts2 tailscale --socket=/run/tailscale-ts2/tailscaled.sock"' \ + | sudo tee /etc/profile.d/tailscale-ts2.sh +``` \ No newline at end of file diff --git a/vms/docker-dev-22/psmf-data-sync/.env b/vms/docker-dev-22/psmf-data-sync/.env new file mode 100644 index 0000000..b0396ea --- /dev/null +++ b/vms/docker-dev-22/psmf-data-sync/.env @@ -0,0 +1,20 @@ +# Image (built by .gitea/workflows/build.yaml) +# IMAGE_REPO is the Gitea repo path, e.g. "jan/psmf-data-sync" +IMAGE_REPO=gitea.home.hrajfrisbee.cz/psmf/psmf-data-syncer +IMAGE_TAG=0.2 + +# Host port to publish the syncer on (container listens on 8002 internally) +HOST_PORT=8003 + +# External Postgres the syncer restores into +DB_HOST=192.168.123.23 +DB_PORT=5432 +DB_NAME=psmf-dev +DB_USER=psmf-dev +DB_PASSWORD=** masked even when it is so secret ** + +# Which Redis DB index to use (0-15) +REDIS_DB=0 + +# info | debug +LOG_LEVEL=debug \ No newline at end of file diff --git a/vms/docker-dev-22/psmf-data-sync/docker-compose.yml b/vms/docker-dev-22/psmf-data-sync/docker-compose.yml new file mode 100644 index 0000000..ca20067 --- /dev/null +++ b/vms/docker-dev-22/psmf-data-sync/docker-compose.yml @@ -0,0 +1,28 @@ +services: + redis: + image: redis:7-alpine + restart: unless-stopped + command: ["redis-server", "--appendonly", "yes"] + volumes: + - redis-data:/data + + psmf-data-sync: + image: ${IMAGE_REPO}:${IMAGE_TAG:-latest} + restart: unless-stopped + depends_on: + - redis + ports: + - "${HOST_PORT:-8003}:8002" + environment: + DB_HOST: ${DB_HOST} + DB_PORT: ${DB_PORT:-5432} + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + REDIS_HOST: redis + REDIS_PORT: 6379 + REDIS_DB: ${REDIS_DB:-0} + LOG_LEVEL: ${LOG_LEVEL:-info} + +volumes: + redis-data: \ No newline at end of file diff --git a/vms/docker-dev-22/readme.md b/vms/docker-dev-22/readme.md new file mode 100644 index 0000000..2a42564 --- /dev/null +++ b/vms/docker-dev-22/readme.md @@ -0,0 +1,22 @@ +## docker + +```bash +# prereqs + keyring +sudo apt-get update +sudo apt-get install -y ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# repo +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +# install +sudo apt-get update +sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + + +``` \ No newline at end of file diff --git a/vms/storage-23/pg_hba.conf b/vms/storage-23/pg_hba.conf new file mode 100644 index 0000000..b83bb99 --- /dev/null +++ b/vms/storage-23/pg_hba.conf @@ -0,0 +1,22 @@ +# Database administrative login by Unix domain socket +local all postgres peer + +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all peer +# IPv4 local connections: +host all all 127.0.0.1/32 scram-sha-256 +# IPv6 local connections: +host all all ::1/128 scram-sha-256 +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all peer +host replication all 127.0.0.1/32 scram-sha-256 +host replication all ::1/128 scram-sha-256 + +# TYPE DATABASE USER ADDRESS METHOD +host psmf psmf 192.168.123.21/32 md5 +host psmfapi psmf 192.168.123.21/32 md5 +host postgres psmf 192.168.123.21/32 md5 +host psmf-dev psmf-dev 192.168.123.22/32 md5 \ No newline at end of file