Skip to content

Server Security Operations Runbook

Scope

Runbook для эксплуатации serveradmin (prod/dev), security мониторинга и безопасных ручных действий.

Контур

  • Repo: /home/admin/workspace/serveradmin
  • Prod URL: https://serveradmin.designcorp.eu
  • Dev URL: https://dev.serveradmin.designcorp.eu
  • Service units:
    • serveradmin-ui.service
    • serveradmin-agent.service

Быстрый healthcheck

bash
curl -skI https://serveradmin.designcorp.eu
curl -skI https://dev.serveradmin.designcorp.eu
sudo -n systemctl status serveradmin-ui --no-pager
sudo -n systemctl status serveradmin-agent --no-pager

Критичные файлы

  • Env: /home/admin/.config/serveradmin/serveradmin.env
  • Events: /home/admin/logs/serveradmin/events.jsonl
  • Audit snapshots: /home/admin/logs/serveradmin/audit.jsonl
  • Operator audit: /home/admin/logs/serveradmin/operator_audit.jsonl
  • Jobs queue: /home/admin/logs/serveradmin/jobs.json
  • Approvals queue: /home/admin/logs/serveradmin/approvals.json
  • Runtime web dist: /opt/designcorp/serveradmin/prod/web-dist
  • Daily Telegram report script: /home/admin/bin/nightly-security-report.sh
  • Security guard script: /home/admin/bin/sec-guard.sh

Scheduled Monitoring

serveradmin uses systemd timers for host-level scheduled monitoring that must not depend on the mutable user crontab:

bash
systemctl list-timers --all --no-pager | rg 'serveradmin-(daily-report|sec-guard)'
systemctl status serveradmin-daily-report.timer --no-pager
systemctl status serveradmin-sec-guard.timer --no-pager
  • serveradmin-daily-report.timer runs /home/admin/bin/nightly-security-report.sh every day at 08:00 UTC and sends the 24h Telegram security report.
  • serveradmin-sec-guard.timer runs /home/admin/bin/sec-guard.sh every minute and checks host/container IOC markers.
  • If Telegram reports stop, first verify serveradmin-daily-report.timer, then run sudo -n systemctl start serveradmin-daily-report.service and check systemctl show serveradmin-daily-report.service -p Result -p ExecMainStatus.

Telegram alert scope:

  • Daily report includes public TLS expiry checks for active prod sites from /home/admin/logs/serveradmin/sites.json.
  • TLS thresholds are controlled by SERVERADMIN_SSL_WARN_DAYS (default 21) and SERVERADMIN_SSL_CRITICAL_DAYS (default 7).
  • TLS scope is controlled by SERVERADMIN_SSL_ALERT_ENVS (default prod; use all only if dev certificates must be reported too).
  • serveradmin-agent site-down Telegram alerts are scoped by SERVERADMIN_SITE_ALERT_ENVS (default prod). This prevents intentionally paused dev services from paging Telegram while still protecting prod.

Deploy frontend

bash
cd /home/admin/workspace/serveradmin
npm run web:deploy
sudo -n systemctl restart serveradmin-ui

Проверка:

bash
curl -s -I http://127.0.0.1:3099/

Безопасные ручные действия

Предпочтительно выполнять из UI (Эксперт -> Действия), т.к. там есть audit trail.

High-risk операции (restart_nginx, nginx_apply, backup_restore) выполняются через:

  1. Создание заявки (/api/v2/jobs без risk_ack) -> requires_approval=true.
  2. Решение админа (/api/v2/approvals/:id/decision).
  3. Запуск approved заявки (/api/v2/approvals/:id/execute).

Для прод-контура включен scheduler эскалаций:

  • pending approvals получают Telegram reminders;
  • заявка auto-expire после SERVERADMIN_APPROVAL_PENDING_TTL_MIN.

CLI fallback:

bash
sudo -n nginx -t
sudo -n systemctl reload nginx
sudo -n systemctl restart serveradmin-ui
sudo -n systemctl restart serveradmin-agent

Инцидент: P0

  1. Зафиксировать тип инцидента (P0) и время.
  2. При необходимости включить global lockdown или host lockdown.
  3. Проверить:
    • активность подозрительных процессов,
    • последние записи events.jsonl/audit.jsonl,
    • статус nginx/fail2ban.
  4. После стабилизации:
    • откатить временные ограничения,
    • оформить post-incident запись,
    • обновить docs (runbook + baseline + backlog).

Incident Room V1 (UI triage)

Во вкладке Incidents в serveradmin используется единый triage поток:

  1. Фильтры потока:
    • severity (p0|p1|lockdown|all)
    • project/host
    • time window (1h|6h|24h|7d)
    • status (open|acked|resolved|all)
  2. Для каждого инцидента доступны quick actions:
    • Ack / Resolve / Reopen
    • Lock host / Unlock host
    • Nginx test / Nginx reload / Restart nginx
    • Open Cockpit -> project-specific diagnosis panel без CLI
  3. Для каждого инцидента есть прямая ссылка в релевантный runbook.

Incident Cockpit V1 показывает:

  • live health probe по healthUrl;
  • linked runtime targets и их state;
  • runtime logs preview;
  • nginx -t snapshot и путь vhost;
  • SSL snapshot;
  • recent related jobs/actions;
  • probable cause для 502/down по rule-based heuristics.

One-click Workflows V1 в Actions:

  • сценарии: publish_project, ssl_issue, rollback_release, grant_access;
  • сначала запускать Run preflight, затем Start workflow;
  • если apply-stage создал approval, дальше действие продолжается через linked approval прямо из того же run;
  • история workflow хранит correlation id и step status, но не сохраняет секреты из apply результата.

Recovery Center V1:

  • открывается из Incidents -> Recovery или из Actions -> Recovery Center;
  • собирает runtime, health, releases, backups и workflows в один recovery context;
  • Rollback last healthy теперь целится в последний успешный deploy, а не в прошлый rollback-event;
  • Restore dry-run использовать первым, если нужно проверить целостность backup без live restore;
  • для serveradmin.designcorp.eu self-deploy теперь может быть завершён новой копией UI после собственного restart, поэтому после restart нужно проверять не только service status, но и release state в /api/deploy/releases.

Recovery Center

Использовать как primary path для 502/down, если проблема уже customer-visible:

  1. Verify now
  2. Restart runtime если binding настроен
  3. Rollback last healthy если restart не помог
  4. Restore dry-run, затем live restore только при подтверждённой необходимости
  5. Повторный health verify и Resolve инцидента

Approval Inbox V2:

  • approvals теперь triage'ятся через lanes Needs decision, Ready to execute, Recent outcomes;
  • Needs decision — зона админа, Ready to execute — зона оператора;
  • detail pane показывает justification, site context, payload summary, decision notes и linked job;
  • raw JSON оставлен как secondary debug view, а не как основной интерфейс.

Approval Inbox

Порядок работы:

  1. Проверить justification и scope.
  2. Approve или Reject.
  3. Если request уже approved, выполнить его через Execute approved.
  4. Проверить linked job и follow-up screen (Recovery, Projects/Sites, Monitoring).

Health Policy / Onboarding:

  • в Projects/Sites probe теперь читается не только как код, а как policy + explanation;
  • если сайт discovery-managed и root не подходит как health endpoint, agent сам переводит его на canonical /health или auth-expected, не перетирая explicit настройку оператора;
  • колонка Managed показывает missing/warning metadata для частично подключённых проектов;
  • новый сайт нужно считать завершённым только после заполнения runtime, nginxPath, canonical healthUrl/healthPolicy, owner, criticality и backup mode.

Требование по трассировке:

  • действия из Incident Room должны идти с reason-префиксом incident_room:<incident_key>;
  • все такие действия фиксируются в operator_audit.jsonl.

Инцидент: UI недоступен

  1. Проверить local backend:
bash
curl -s -I http://127.0.0.1:3099/
  1. Проверить nginx конфиг/статус:
bash
sudo -n nginx -t
sudo -n systemctl status nginx --no-pager
  1. Проверить serveradmin-ui:
bash
sudo -n systemctl status serveradmin-ui --no-pager
sudo -n systemctl restart serveradmin-ui

Credentials / Rotation

Минимум для ротации:

  • Basic Auth (/etc/nginx/.htpasswd-serveradmin)
  • SERVERADMIN_API_TOKEN
  • SERVERADMIN_TELEGRAM_TOKEN (по необходимости)

После ротации:

  1. sudo -n nginx -t && sudo -n systemctl reload nginx
  2. sudo -n systemctl restart serveradmin-ui serveradmin-agent
  3. Проверка prod/dev URL + API доступа.

SSL Operations

Если ssl_issue блокируется:

  1. Не запускать ручной certbot наугад.
  2. В Actions -> One-click workflows сначала смотреть Run preflight.
  3. Исправить DNS или HTTP challenge path blockers.
  4. Только после этого запускать Start workflow.
  5. Затем проверить SSL status и Monitoring Workspace.

API smoke для jobs/approvals

bash
curl -sS -H "x-serveradmin-token: $SERVERADMIN_TOKEN" http://127.0.0.1:3099/api/v2/ops/summary | jq
curl -sS -H "x-serveradmin-token: $SERVERADMIN_TOKEN" http://127.0.0.1:3099/api/v2/jobs?limit=20 | jq
curl -sS -H "x-serveradmin-token: $SERVERADMIN_TOKEN" http://127.0.0.1:3099/api/v2/approvals?limit=20 | jq

API smoke для Hosting Modules (V2)

Использовать встроенный smoke-скрипт:

bash
SERVERADMIN_TOKEN="$SERVERADMIN_TOKEN" \
/home/admin/workspace/serveradmin/scripts/smoke-api-v2.sh http://127.0.0.1:3099

Проверяет:

  • core/rbac (/api/config, /api/me);
  • jobs/approvals (/api/v2/*);
  • files/ftp, db, mail, cron, resources, access;
  • incidents feed и managed files list.

Примечание по backend/frontend decomposition phase 2:

  • V3 route-level handlers теперь живут в src/ui-api/*.js, а src/ui.js собирает их через modularApiHandlers.
  • Hosting/system surfaces вынесены в src/ui-api/hosting-modules.js и src/ui-api/system.js.
  • Legacy inline admin HTML вынесен в src/ui-html.js.
  • web/src/App.jsx теперь тонкий shell; state/effects вынесены в web/src/hooks/useServerAdminApp.js, а секции UI живут в web/src/sections/*.jsx.
  • После route-level refactor достаточно повторить smoke по доменам, которые были вынесены:
    • /api/workflows/catalog
    • /api/v2/jobs?limit=1
    • /api/sites/<site-id>/runtime
    • /api/incidents/feed?limit=1
    • /api/backup/status
    • /api/ssl/status?site_id=<site-id>
    • /api/resources/sites?limit=1
    • /api/admin/actions

noVNC desktop for code-server

Browser VNC URL:

bash
https://vscode.designcorp.eu/proxy/6080/vnc.html?path=proxy/6080

Runtime units:

bash
sudo -n systemctl status vscode-vnc-xvfb.service --no-pager
sudo -n systemctl status vscode-vnc-fluxbox.service --no-pager
sudo -n systemctl status vscode-vnc-x11vnc.service --no-pager
sudo -n systemctl status vscode-novnc.service --no-pager

Expected process chain:

  • Xvfb :100 owns the virtual display.
  • x11vnc bridges :100 to 127.0.0.1:5901.
  • websockify exposes local noVNC on 127.0.0.1:6080.
  • vscode-vnc-fluxbox.service must start /usr/bin/startfluxbox, not /usr/bin/fluxbox directly.
  • startfluxbox reads /home/admin/.fluxbox/startup, which starts pcmanfm --desktop --profile LXDE for desktop icons and file manager integration.

If desktop icons or File Explorer disappear but VNC still connects, first check:

bash
ps -eo pid,ppid,user,stat,cmd | grep -Ei 'pcmanfm|fluxbox|x11vnc|websockify' | grep -v grep
DISPLAY=:100 xlsclients -l

Root cause seen on 2026-05-31: Xvfb had restarted on 2026-05-30, killing the old pcmanfm --desktop; the Fluxbox unit restarted only /usr/bin/fluxbox, bypassing /home/admin/.fluxbox/startup, so pcmanfm did not come back. Fix was to point vscode-vnc-fluxbox.service at /usr/bin/startfluxbox, run sudo -n systemctl daemon-reload, then restart only vscode-vnc-fluxbox.service.

Примечание по V3 registry:

  • GET /api/sites теперь возвращает не только legacy top-level поля проекта, но и нормализованные блоки runtime, backupPolicy и capabilities.
  • При диагностике нового проекта сначала смотреть именно эту модель, а не пытаться вручную угадать runtime/health/ownership из nginx.
  • Новый guided onboarding идёт через Projects/Sites wizard, а не через ручное заполнение всех полей одним экраном.
  • Перед созданием/обновлением wizard вызывает POST /api/sites/preview; именно этот preview является источником правды для managedStatus и remaining blockers.
  • Runtime controls читаются через:
    • GET /api/sites/:id/runtime
    • GET /api/sites/:id/runtime/logs?target=<unit|container>&lines=80
  • Safe restart runtime выполняется через jobs:
    • POST /api/v2/jobs
    • body: {"operation":"service_control","reason":"...","payload":{"site_id":"...","action":"restart"}}
  • Важно: restart не принимает unit/container names из запроса. Backend берёт targets только из runtime.units / runtime.containers project registry или из controlled bootstrap map для core-проектов.

Операционные правила V2

  • High-risk direct execution только для admin + risk_ack=true.
  • По умолчанию high-risk действия идут через approvals queue.
  • Для всех mutating API обязателен reason (audit trail).
  • Новые managed token значения возвращаются только в момент создания и далее в API не отображаются.