216 backups encrypted on 216: pgBackRest (AES-256) + age-encrypted payment CSVs, pushed write-only to 247 with rrsync; health watches pg_stat_archiver and OFFSITE_SYNC_STATUS_FILE; 216 scripts/configs in ops/server216; plaintext pg216 pipeline removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,10 +4,8 @@
|
||||
# --checkpoint=spread و --max-rate بار روی ۲۴۹ را کم نگه میدارند (آنجا سرورِ اصلی است).
|
||||
# نسخه تا pg_verifybackup قبولش نکند جای قبلیها را نمیگیرد و چیزی پاک نمیشود.
|
||||
set -euo pipefail
|
||||
# ROOT/BIN/PORT از یونیت میآیند؛ پیشفرضها همان بکاپِ ۲۴۹ است (یونیتهای zareian-pg216-* برای سرورِ جدید).
|
||||
ROOT="${PGB_ROOT:-/home/administrator/zareian-pgbackup}"
|
||||
BIN="${PGB_BIN:-/usr/lib/postgresql/14/bin}"
|
||||
PORT="${PGB_PORT:-15440}"
|
||||
ROOT=/home/administrator/zareian-pgbackup
|
||||
BIN=/usr/lib/postgresql/14/bin
|
||||
KEEP=7
|
||||
|
||||
cd "$ROOT"
|
||||
@@ -16,7 +14,7 @@ ts=$(date +%Y%m%d-%H%M%S)
|
||||
rm -rf base/.inprogress-*
|
||||
tmp="base/.inprogress-$ts"
|
||||
|
||||
"$BIN/pg_basebackup" -h 127.0.0.1 -p "$PORT" -U zareian_replicator --no-password \
|
||||
"$BIN/pg_basebackup" -h 127.0.0.1 -p 15440 -U zareian_replicator --no-password \
|
||||
-D "$tmp" -Fp -X stream --checkpoint=spread --max-rate=20M
|
||||
"$BIN/pg_verifybackup" "$tmp"
|
||||
mv "$tmp" "base/$ts"
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
# خروجی: payment-log/payment-events-<UTC>.csv، orders-…csv، transactions-…csv؛ ۱۴ فایلِ آخرِ هر نوع میماند.
|
||||
# توجه: نسخهی کامل تا چند دقیقهی WAL عقبتر از ۲۴۹ میشود، ولی WALِ رسیده همه اعمال میشود.
|
||||
set -euo pipefail
|
||||
# ROOT/BIN از یونیت میآیند؛ پیشفرضها همان بکاپِ ۲۴۹ است (یونیتهای zareian-pg216-* برای سرورِ جدید).
|
||||
ROOT="${PGB_ROOT:-/home/administrator/zareian-pgbackup}"
|
||||
BIN="${PGB_BIN:-/usr/lib/postgresql/14/bin}"
|
||||
ROOT=/home/administrator/zareian-pgbackup
|
||||
BIN=/usr/lib/postgresql/14/bin
|
||||
OUT="$ROOT/payment-log"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
# restore-check.sh '2026-10-10 16:30:00+03:30'
|
||||
# با KEEP=1 پوشه و Postgresِ موقت میمانند (برای بازیابیِ واقعی: از آن pg_dump بگیرید، بعد pg_ctl stop و rm).
|
||||
set -euo pipefail
|
||||
# ROOT/BIN از یونیت میآیند؛ پیشفرضها همان بکاپِ ۲۴۹ است (یونیتهای zareian-pg216-* برای سرورِ جدید).
|
||||
ROOT="${PGB_ROOT:-/home/administrator/zareian-pgbackup}"
|
||||
BIN="${PGB_BIN:-/usr/lib/postgresql/14/bin}"
|
||||
ROOT=/home/administrator/zareian-pgbackup
|
||||
BIN=/usr/lib/postgresql/14/bin
|
||||
TARGET="${1:-}"
|
||||
|
||||
latest=$(ls -1d "$ROOT"/base/2* | tail -1)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). نسخهی کاملِ روزانه از ۲۱۶؛ همان basebackup.sh با مسیر و پورتِ ۲۱۶.
|
||||
[Unit]
|
||||
Description=Zareian MathApp: daily base backup from 216
|
||||
Requires=zareian-pg216-tunnel.service
|
||||
After=zareian-pg216-tunnel.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=administrator
|
||||
Environment=PGB_ROOT=/home/administrator/zareian-pgbackup-216 PGB_BIN=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/postgresql/16/bin PGB_PORT=15441 LD_LIBRARY_PATH=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/x86_64-linux-gnu
|
||||
ExecStart=/home/administrator/zareian-pgbackup-216/bin/basebackup.sh
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Zareian MathApp: daily base backup from 216
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 04:50:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,8 +0,0 @@
|
||||
[Unit]
|
||||
Description=Zareian MathApp: export payment events from the 216 backup copy to CSV
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=administrator
|
||||
Environment=PGB_ROOT=/home/administrator/zareian-pgbackup-216 PGB_BIN=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/postgresql/16/bin LD_LIBRARY_PATH=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/x86_64-linux-gnu
|
||||
ExecStart=/home/administrator/zareian-pgbackup-216/bin/export-payment-events.sh
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Zareian MathApp: payment events CSV from 216 every 6 hours
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 01,07,13,19:35:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,20 +0,0 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). تونلِ SSH از 127.0.0.1:15441ِ اینجا به Postgresِ سرورِ اختصاصی (۲۱۶، 127.0.0.1:5440).
|
||||
# طرفِ ۲۱۶ کاربرِ سیستمیِ pgbackup است: فقط همین کلید، فقط از 192.168.200.167، بیشِل، و در authorized_keys
|
||||
# permitopen="127.0.0.1:5440" (هیچ پورتِ دیگری). رمزِ zareian_replicator فقط در ~/.pgpassِ ۲۴۷ است؛ ۲۱۶ فقط هشِ SCRAM آن را دارد.
|
||||
# (forwardingِ سوکت ممکن نیست: AllowTcpForwarding no در sshd آن را هم میبندد.)
|
||||
[Unit]
|
||||
Description=Zareian MathApp: SSH tunnel to the app Postgres on 216
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
User=administrator
|
||||
ExecStart=/usr/bin/ssh -N -i /home/administrator/.ssh/zareian_pgbackup_216 \
|
||||
-o BatchMode=yes -o ExitOnForwardFailure=yes \
|
||||
-o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
|
||||
-L 127.0.0.1:15441:127.0.0.1:5440 pgbackup@192.168.200.163
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,19 +0,0 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). هر تغییرِ دیتابیسِ ۲۱۶ را لحظهای در ~/zareian-pgbackup-216/wal مینویسد.
|
||||
# ابزارهای Postgres 16 از مخزنِ رسمی در ~/zareian-pgbackup-216/pg16 باز شدهاند (نصبِ سیستمی نیست؛ ۲۴۷ فقط 14 دارد).
|
||||
# اسلاتِ zareian_247 روی ۲۱۶، سقف max_slot_wal_keep_size=5GB. اپ منتظرِ اینجا نمیماند.
|
||||
[Unit]
|
||||
Description=Zareian MathApp: live WAL stream from 216
|
||||
After=zareian-pg216-tunnel.service
|
||||
BindsTo=zareian-pg216-tunnel.service
|
||||
|
||||
[Service]
|
||||
User=administrator
|
||||
Environment=LD_LIBRARY_PATH=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/x86_64-linux-gnu
|
||||
ExecStart=/home/administrator/zareian-pgbackup-216/pg16/root/usr/lib/postgresql/16/bin/pg_receivewal -h 127.0.0.1 -p 15441 -U zareian_replicator \
|
||||
--no-password --slot=zareian_247 --synchronous --no-loop \
|
||||
-D /home/administrator/zareian-pgbackup-216/wal
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
13
ops/server216/10-tadrisino.conf
Normal file
13
ops/server216/10-tadrisino.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
# تدریسینو: سختسازیِ SSH. فقط کاربرهای نامبرده وارد میشوند؛ temp تا حذفش به دستِ ادمینِ شبکه.
|
||||
# ورود با رمز هنوز باز است (تا رفتن به کلید SSH)؛ fail2ban جلوی حدس زدن را میگیرد.
|
||||
PermitRootLogin no
|
||||
AllowUsers zareian temp
|
||||
MaxAuthTries 4
|
||||
MaxSessions 10
|
||||
LoginGraceTime 30
|
||||
X11Forwarding no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PermitTunnel no
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
18
ops/server216/80-tadrisino-hardening.conf
Normal file
18
ops/server216/80-tadrisino-hardening.conf
Normal file
@@ -0,0 +1,18 @@
|
||||
# تدریسینو: تنظیماتِ شبکهی استاندارد (CIS). سرور مسیریاب نیست.
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
net.ipv4.conf.all.rp_filter = 1
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
net.ipv4.conf.all.accept_redirects = 0
|
||||
net.ipv4.conf.default.accept_redirects = 0
|
||||
net.ipv6.conf.all.accept_redirects = 0
|
||||
net.ipv6.conf.default.accept_redirects = 0
|
||||
net.ipv4.conf.all.send_redirects = 0
|
||||
net.ipv4.conf.default.send_redirects = 0
|
||||
net.ipv4.conf.all.accept_source_route = 0
|
||||
net.ipv6.conf.all.accept_source_route = 0
|
||||
net.ipv4.conf.all.log_martians = 1
|
||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||
kernel.kptr_restrict = 2
|
||||
kernel.dmesg_restrict = 1
|
||||
fs.protected_hardlinks = 1
|
||||
fs.protected_symlinks = 1
|
||||
4
ops/server216/backup.conf
Normal file
4
ops/server216/backup.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
# تدریسینو: آرشیوِ پیوستهی WAL با pgBackRest (رمزنگاریشده). archive_timeout یعنی حداکثر ۶۰ ثانیه تغییرِ آرشیونشده.
|
||||
archive_mode = on
|
||||
archive_command = 'pgbackrest --stanza=zareian archive-push %p'
|
||||
archive_timeout = 60
|
||||
20
ops/server216/gunicorn-mathapp.service
Normal file
20
ops/server216/gunicorn-mathapp.service
Normal file
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=Gunicorn MathApp (tadrisino API)
|
||||
After=network.target postgresql@16-zareian.service zareian-redis.service
|
||||
Wants=postgresql@16-zareian.service zareian-redis.service
|
||||
|
||||
[Service]
|
||||
User=mathapp
|
||||
Group=www-data
|
||||
WorkingDirectory=/home/mathapp/mathapp-v2
|
||||
EnvironmentFile=/home/mathapp/mathapp-v2/.env
|
||||
# /run/gunicorn بعد از هر ریبوت ساخته میشود (روی ۲۴۹ دستی ساخته شده بود و بعد از ریبوت نبود).
|
||||
RuntimeDirectory=gunicorn
|
||||
RuntimeDirectoryMode=0750
|
||||
ExecStart=/home/mathapp/mathapp-v2/venv/bin/gunicorn --bind unix:/run/gunicorn/mathapp.sock config.wsgi:application
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=zareian-mathapp.service
|
||||
17
ops/server216/pgbackrest.conf
Normal file
17
ops/server216/pgbackrest.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
# روی ۲۱۶: /etc/pgbackrest.conf (root:postgres 640). کلیدِ واقعی فقط روی سرور و در /etc/tadrisino-backup/RECOVERY-KEYS است.
|
||||
# تدریسینو: بکاپِ رمزنگاریشده. مخزنِ محلی /var/lib/pgbackrest؛ نسخهی بیرونی با tadrisino-offsite-sync به ۲۴۷ (فقط فایلِ رمزشده).
|
||||
[global]
|
||||
repo1-path=/var/lib/pgbackrest
|
||||
repo1-cipher-type=aes-256-cbc
|
||||
repo1-cipher-pass=<RECOVERY-KEYS، خطِ کلیدِ ۱>
|
||||
repo1-retention-full=2
|
||||
compress-type=zst
|
||||
process-max=2
|
||||
start-fast=y
|
||||
log-level-console=info
|
||||
log-level-file=detail
|
||||
|
||||
[zareian]
|
||||
pg1-path=/var/lib/postgresql/16/zareian
|
||||
pg1-port=5440
|
||||
pg1-socket-path=/var/run/postgresql
|
||||
44
ops/server216/tadrisino-blocklist
Executable file
44
ops/server216/tadrisino-blocklist
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# تدریسینو: IPهای شناختهشدهی مخرب را پیش از اولین تلاش روی همهی پورتها میبندد.
|
||||
# فهرست: FireHOL level1 (Spamhaus DROP/EDROP، DShield، Feodo، bogons)، روزی یک بار (tadrisino-blocklist.timer).
|
||||
# شبکههای داخلی، رنجِ IPِ شرکت و رنجهای ArvanCloud (از snippetِ nginx) هرگز بسته نمیشوند.
|
||||
# اگر دانلود نشد، آخرین فهرستِ سالم (/var/lib/tadrisino-blocklist/list) دوباره بار میشود.
|
||||
set -euo pipefail
|
||||
URL=https://iplists.firehol.org/files/firehol_level1.netset
|
||||
DIR=/var/lib/tadrisino-blocklist; SET=tadrisino-block
|
||||
mkdir -p "$DIR"
|
||||
tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT
|
||||
if curl -fsS -m 90 "$URL" -o "$tmp" && [ "$(grep -c '^[0-9]' "$tmp")" -gt 1000 ]; then
|
||||
python3 - "$tmp" "$DIR/list" <<'PY'
|
||||
import ipaddress, re, sys
|
||||
src, dst = sys.argv[1], sys.argv[2]
|
||||
keep = ['127.0.0.0/8','10.0.0.0/8','172.16.0.0/12','192.168.0.0/16','100.64.0.0/10','169.254.0.0/16','78.38.35.0/24']
|
||||
try:
|
||||
keep += re.findall(r'^allow\s+([0-9./]+);', open('/etc/nginx/snippets/zareian-arvan-only.conf').read(), re.M)
|
||||
except OSError:
|
||||
pass
|
||||
keep = [ipaddress.ip_network(k) for k in keep if ':' not in k]
|
||||
out, dropped = [], 0
|
||||
for line in open(src):
|
||||
line = line.strip()
|
||||
if not line or line[0] == '#':
|
||||
continue
|
||||
n = ipaddress.ip_network(line, strict=False)
|
||||
if any(n.overlaps(k) for k in keep):
|
||||
dropped += 1
|
||||
continue
|
||||
out.append(str(n))
|
||||
open(dst, 'w').write('\n'.join(out) + '\n')
|
||||
print(f'{len(out)} networks, {dropped} skipped (internal/company/Arvan)')
|
||||
PY
|
||||
else
|
||||
echo "download failed; reloading last good list" >&2
|
||||
fi
|
||||
[ -s "$DIR/list" ] || { echo "no list available" >&2; exit 1; }
|
||||
ipset create "$SET" hash:net maxelem 262144 -exist
|
||||
ipset create "$SET-new" hash:net maxelem 262144 -exist
|
||||
ipset flush "$SET-new"
|
||||
sed "s/^/add $SET-new /" "$DIR/list" | ipset restore -exist
|
||||
ipset swap "$SET-new" "$SET"; ipset destroy "$SET-new"
|
||||
iptables -C INPUT -m set --match-set "$SET" src -j DROP 2>/dev/null || iptables -I INPUT 1 -m set --match-set "$SET" src -j DROP
|
||||
echo "loaded $(ipset list "$SET" -t | sed -n 's/^Number of entries: //p') entries into $SET"
|
||||
11
ops/server216/tadrisino-blocklist.service
Normal file
11
ops/server216/tadrisino-blocklist.service
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: block known-malicious IPs (FireHOL level1)
|
||||
After=network-online.target ufw.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/tadrisino-blocklist
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
10
ops/server216/tadrisino-blocklist.timer
Normal file
10
ops/server216/tadrisino-blocklist.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: refresh malicious-IP blocklist daily
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 05:20:00
|
||||
RandomizedDelaySec=30m
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
19
ops/server216/tadrisino-export-payment-events
Executable file
19
ops/server216/tadrisino-export-payment-events
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# تدریسینو: دفترِ خرید (payment_paymentevent)، سفارشها و تراکنشها به CSV، هر ۶ ساعت. هر فایل پیش از نوشتهشدن روی دیسک
|
||||
# با age برای کلیدِ /etc/tadrisino-backup/age-recipient.txt رمز میشود؛ متنِ خوانا هیچجا ذخیره نمیشود.
|
||||
# بازکردن: age -d -i age-identity.txt payment-events-….csv.age (کلید در RECOVERY-KEYS). ۱۴ فایلِ آخرِ هر نوع میماند.
|
||||
set -euo pipefail
|
||||
OUT=/var/lib/tadrisino-backup/payment-log
|
||||
REC=/etc/tadrisino-backup/age-recipient.txt
|
||||
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
q() {
|
||||
psql -p 5440 -d zareian_mathapp -v ON_ERROR_STOP=1 -Atqc "\\copy ($1) to stdout csv header" | age -R "$REC" -o "$OUT/$2-$stamp.csv.age"
|
||||
}
|
||||
q "select id, created_at, order_id, user_id, provider, event, detail from payment_paymentevent order by id" payment-events
|
||||
q "select id, created_at, user_id, plan_id, plan_title, amount, status, provider, gateway_authority, subscription_id from payment_order order by id" orders
|
||||
q "select id, created_at, order_id, gateway, gateway_ref_id, status, paid_at from payment_transaction order by id" transactions
|
||||
chmod 640 "$OUT"/*-"$stamp".csv.age
|
||||
for kind in payment-events orders transactions; do
|
||||
ls -1t "$OUT/$kind"-*.csv.age | tail -n +15 | xargs -r rm -f --
|
||||
done
|
||||
echo "wrote $OUT/*-$stamp.csv.age"
|
||||
8
ops/server216/tadrisino-export-payment-events.service
Normal file
8
ops/server216/tadrisino-export-payment-events.service
Normal file
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: encrypted payment CSVs every 6 hours
|
||||
After=postgresql@16-zareian.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=postgres
|
||||
ExecStart=/usr/local/sbin/tadrisino-export-payment-events
|
||||
9
ops/server216/tadrisino-export-payment-events.timer
Normal file
9
ops/server216/tadrisino-export-payment-events.timer
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: encrypted payment CSVs every 6 hours
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 01,07,13,19:15:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
11
ops/server216/tadrisino-offsite-sync
Executable file
11
ops/server216/tadrisino-offsite-sync
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
# تدریسینو: نسخهی بیرونیِ بکاپ. فقط فایلهای رمزشده (مخزنِ pgBackRest با AES-256 و CSVهای age) به ۲۴۷ میروند.
|
||||
# کلیدِ postgres روی ۲۴۷ با rrsync -wo محدود است: فقط نوشتن در ~/zareian-pgbackrest-216، نه خواندن، نه شِل.
|
||||
# پس از موفقیتِ هر دو، زمانش در offsite-synced-at ثبت میشود و /api/health از روی آن عقبماندگی را میسنجد.
|
||||
set -euo pipefail
|
||||
R=administrator@192.168.200.167
|
||||
SSH="ssh -i /var/lib/postgresql/.ssh/id_offsite -o BatchMode=yes -o ConnectTimeout=20"
|
||||
rsync -a --delete -e "$SSH" /var/lib/pgbackrest/ "$R:repo/"
|
||||
rsync -a --delete -e "$SSH" /var/lib/tadrisino-backup/payment-log/ "$R:payment-log/"
|
||||
date -u +%s > /var/lib/tadrisino-backup/offsite-synced-at.tmp
|
||||
mv /var/lib/tadrisino-backup/offsite-synced-at.tmp /var/lib/tadrisino-backup/offsite-synced-at
|
||||
8
ops/server216/tadrisino-offsite-sync.service
Normal file
8
ops/server216/tadrisino-offsite-sync.service
Normal file
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: copy encrypted backups to 247
|
||||
After=postgresql@16-zareian.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=postgres
|
||||
ExecStart=/usr/local/sbin/tadrisino-offsite-sync
|
||||
9
ops/server216/tadrisino-offsite-sync.timer
Normal file
9
ops/server216/tadrisino-offsite-sync.timer
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: copy encrypted backups to 247
|
||||
|
||||
[Timer]
|
||||
OnCalendar=minutely
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
8
ops/server216/tadrisino-pgbackrest-diff.service
Normal file
8
ops/server216/tadrisino-pgbackrest-diff.service
Normal file
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: daily differential encrypted backup
|
||||
After=postgresql@16-zareian.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=postgres
|
||||
ExecStart=/usr/bin/pgbackrest --stanza=zareian --type=diff backup
|
||||
9
ops/server216/tadrisino-pgbackrest-diff.timer
Normal file
9
ops/server216/tadrisino-pgbackrest-diff.timer
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: daily differential encrypted backup
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 03:40:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
8
ops/server216/tadrisino-pgbackrest-full.service
Normal file
8
ops/server216/tadrisino-pgbackrest-full.service
Normal file
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: weekly full encrypted backup
|
||||
After=postgresql@16-zareian.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=postgres
|
||||
ExecStart=/usr/bin/pgbackrest --stanza=zareian --type=full backup
|
||||
9
ops/server216/tadrisino-pgbackrest-full.timer
Normal file
9
ops/server216/tadrisino-pgbackrest-full.timer
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Tadrisino: weekly full encrypted backup
|
||||
|
||||
[Timer]
|
||||
OnCalendar=Sun *-*-* 03:00:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
26
ops/server216/tadrisino-restore-check
Executable file
26
ops/server216/tadrisino-restore-check
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
# تدریسینو: آزمونِ بازیابی از بکاپِ رمزشده (ماهی یک بار، دستی: sudo -u postgres tadrisino-restore-check).
|
||||
# آخرین بکاپ + همهی WALِ آرشیوشده را در پوشهی موقت بازیابی میکند، Postgresِ موقت را روی سوکتِ همان پوشه (بیپورتِ شبکه،
|
||||
# archive_mode=off تا چیزی به مخزن ننویسد) بالا میآورد، شمارشها را کنارِ دیتابیسِ اصلی چاپ میکند و همه را پاک میکند.
|
||||
set -euo pipefail
|
||||
BIN=/usr/lib/postgresql/16/bin
|
||||
work=$(mktemp -d /var/lib/postgresql/restore-check.XXXXXX)
|
||||
trap '"$BIN/pg_ctl" -D "$work/data" -m immediate stop >/dev/null 2>&1 || true; rm -rf "$work"' EXIT
|
||||
pgbackrest --stanza=zareian --pg1-path="$work/data" --type=default --archive-mode=off restore --log-level-console=warn
|
||||
cat > "$work/data/postgresql.conf" <<CONF
|
||||
listen_addresses = ''
|
||||
port = 5499
|
||||
unix_socket_directories = '$work'
|
||||
archive_mode = off
|
||||
shared_buffers = 128MB
|
||||
CONF
|
||||
echo 'local all all peer' > "$work/data/pg_hba.conf"
|
||||
"$BIN/pg_ctl" -D "$work/data" -l "$work/log" -w -t 600 start >/dev/null
|
||||
for _ in $(seq 120); do
|
||||
[ "$(psql -h "$work" -p 5499 -d zareian_mathapp -Atc 'select pg_is_in_recovery()')" = f ] && break; sleep 1
|
||||
done
|
||||
Q="select 'users', count(*) from accounts_user union all select 'questions', count(*) from content_question
|
||||
union all select 'orders', count(*) from payment_order union all select 'payment_events', count(*) from payment_paymentevent
|
||||
union all select 'login_events', count(*) from accounts_loginevent"
|
||||
echo "restored | live"
|
||||
paste -d'|' <(psql -h "$work" -p 5499 -d zareian_mathapp -Atc "$Q") <(psql -p 5440 -d zareian_mathapp -Atc "$Q" | cut -d'|' -f2)
|
||||
25
ops/server216/tadrisino.local
Normal file
25
ops/server216/tadrisino.local
Normal file
@@ -0,0 +1,25 @@
|
||||
# تدریسینو: SSH در برابرِ رباتهای حدسِ رمز، پلهای تا آدمِ واقعی (اشتباهِ تایپی، کیبوردِ فارسی) گیر نکند.
|
||||
# ۶ اشتباه در ۱۰ دقیقه → ۱۰ دقیقه بسته؛ هر بارِ تکرار دو برابر (۲۰، ۴۰، ...، حداکثر یک هفته).
|
||||
# شبکهی داخلیِ شرکت (ادمینها، ۲۴۷، ۲۴۹) هرگز بسته نمیشود.
|
||||
[DEFAULT]
|
||||
ignoreip = 127.0.0.1/8 ::1 192.168.200.0/24
|
||||
bantime.increment = true
|
||||
bantime.factor = 2
|
||||
bantime.maxtime = 1w
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
backend = systemd
|
||||
mode = aggressive
|
||||
maxretry = 6
|
||||
findtime = 10m
|
||||
bantime = 10m
|
||||
|
||||
# فقط رباتِ سمج: پنج بار بستهشدن در یک روز → یک هفته روی همهی پورتها.
|
||||
[recidive]
|
||||
enabled = true
|
||||
logpath = /var/log/fail2ban.log
|
||||
banaction = iptables-allports
|
||||
findtime = 1d
|
||||
maxretry = 5
|
||||
bantime = 1w
|
||||
13
ops/server216/zareian-redis.service
Normal file
13
ops/server216/zareian-redis.service
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Redis for zareian-mathapp only
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=redis
|
||||
Group=redis
|
||||
ExecStart=/usr/bin/redis-server /etc/zareian-redis.conf
|
||||
Restart=on-failure
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
5
ops/server216/zareian.conf
Normal file
5
ops/server216/zareian.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
# تدریسینو، سرور اختصاصی: فقط localhost. max_connections با workers × DB_POOL_MAX_SIZE + 6 در /api/health سنجیده میشود.
|
||||
listen_addresses = '127.0.0.1'
|
||||
max_connections = 60
|
||||
shared_buffers = 1GB
|
||||
effective_cache_size = 4GB
|
||||
Reference in New Issue
Block a user