Skip to content

Latest commit

 

History

History
193 lines (155 loc) · 11.7 KB

File metadata and controls

193 lines (155 loc) · 11.7 KB

New Features — Endpoint & Page Reference

Reference for the current UI, Docker, WAF, and TLS surfaces on dev. All endpoints are under /api/v1. "admin" requires an admin user; "edit" uses _can_edit_app (owner, admin, or an editor grant); "auth" accepts any valid JWT. Migration 093_container_scale_policies versions the scaling-policy table.


1. Frontend surfaces

Surface Where Notes
Layout switcher User menu (sidebar footer) → "Layout": Sidebar / Compact / Top bar No route; persisted to localStorage['layout'] (sidebar|rail|topbar), applied as data-layout on <html>. Desktop only.
GPU Monitor Route /gpu · sidebar nav "GPU Monitor" (System) Per-GPU cards + compute-process table; empty state when no GPU.
Dynamic DNS Route /dynamic-dns · sidebar nav "Dynamic DNS" (Infrastructure) Host CRUD; shows the one-time token + ready update URL on create.
Container Ops tab App detail page (Docker apps) Image-update check/apply, auto-sleep, auto-scale.
WAF tab App detail page (nginx-served apps: Docker + Python) Install banner, mode/paranoia/anomaly, disabled-rule editor, apply, events.

2. Endpoints by feature

Dynamic DNS — models: ddns_hosts · services/ddns_service.py

Method Path Auth Notes
GET /ddns/hosts auth List hosts (token masked).
POST /ddns/hosts admin Body {zone_id, record_name, label?, enabled?} → returns host incl. one-time token.
DELETE /ddns/hosts/<id> admin
POST /ddns/hosts/<id>/regenerate-token admin Returns host with a fresh token.
GET/POST /ddns/update?token=&ip= public (token) Updates the host's A/AAAA record. ip from ?ip= or request source. Wrong token → 401, bad IP → 400. Reuses DNSZoneService, so a configured provider (e.g. Cloudflare) syncs automatically.

Image-digest update — models: image_update_checks · services/image_update_service.py

Method Path Auth Notes
POST /image-updates/applications/<id>/check admin Compares local RepoDigest vs registry digest (docker buildx imagetools inspect).
GET /image-updates/applications/<id> auth Latest check (or null).
POST /apps/<id>/image-update/apply edit Pull + recreate (compose apps; compose_pull+compose_up, local/remote). Guarded to compose.

App badge: app.image_update = {status, update_available, checked_at}.

Container auto-sleep — models: container_sleep_policies · services/container_sleep_service.py

Method Path Auth Notes
GET /apps/<id>/sleep-policy auth
PUT /apps/<id>/sleep-policy edit Body {enabled?, idle_timeout_minutes?}.
POST /apps/<id>/sleep edit Stop the app, mark asleep.
POST /apps/<id>/wake edit Start the app, record activity.
POST /apps/sweep-idle admin Sleep all enabled apps idle past their timeout. Cron-drivable.

App badge: app.sleep = {enabled, asleep, idle_timeout_minutes}. Idle is measured from last_activity_at (bumped on wake / record_activity); a no-activity-baseline policy is never slept blind.

Container auto-scale — models: container_scale_policies · services/container_scale_service.py

Method Path Auth Notes
GET /apps/<id>/scale-policy auth
PUT /apps/<id>/scale-policy edit Body {enabled?, service_name?, min_replicas?, max_replicas?, cpu_high_percent?, cpu_low_percent?, cooldown_seconds?}. Enabling requires a service name; low CPU must remain below high CPU.
POST /apps/<id>/scale edit Body {replicas}; manual scale through docker compose --scale, with a minimum of one replica.
POST /apps/<id>/scale/evaluate edit One auto decision (returns action: scaled_up/down/hold/cooldown/disabled/unknown).
POST /apps/scale-sweep admin Evaluate every enabled policy. Cron-drivable.

Requires a scale-capable Compose service with no fixed host port or container_name. Local apps only. The configured minimum acts as a floor on the next evaluation, even when CPU metrics are unavailable. The feature does not configure a load balancer, shared storage, health checks, or failover, so it must not be presented as end-to-end high availability. current_replicas records the last successful ServerKit scale command; it does not reconcile changes made through Docker outside ServerKit.

Verification lives in frontend/src/components/apps/__tests__/autoScalePolicy.test.mjs, frontend/src/services/api/__tests__/containerOps.test.mjs, and backend/tests/test_container_scale.py. The backend workflow test covers policy read/write, evaluation, manual scale, and persistence. Docker execution and traffic continuity still require the real-host checklist in docs/HORIZONTAL_SCALING_SPEC.md.

GPU monitoring — services/gpu_service.py (no model)

Method Path Auth Notes
GET /gpu/ auth {available, gpus:[{index,name,utilization_gpu,memory_used,memory_total,memory_percent,temperature,power_draw,power_limit,fan_speed,driver_version}], processes:[{gpu_uuid,pid,process_name,used_memory,container}]}. Shells nvidia-smi; container resolved from /proc/<pid>/cgroup.

WAF (ModSecurity v3 + OWASP CRS) — models: waf_policies · services/waf_service.py

Method Path Auth Notes
GET /waf/applications/<id>/policy auth
PUT /waf/applications/<id>/policy admin Body {mode, paranoia_level, anomaly_threshold, disabled_rule_ids}; saves and best-effort applies. mode ∈ off|detect|block.
POST /waf/applications/<id>/apply admin Writes per-app rules + injects nginx include + reloads. May return manual_include when no vhost is found.
GET /waf/applications/<id>/events?limit= auth Parsed ModSecurity audit-log events.
GET /waf/status auth {installed}.
POST /waf/install admin Install libmodsecurity + connector + OWASP CRS (distro-aware, best-effort).

3. New models / tables

Table Model Key columns
ddns_hosts DdnsHost zone_id, record_name, token (unique), last_ip, enabled
image_update_checks ImageUpdateCheck application_id, current_digest, latest_digest, update_available, status
container_sleep_policies ContainerSleepPolicy application_id (unique), enabled, idle_timeout_minutes, last_activity_at, asleep
container_scale_policies ContainerScalePolicy application_id (unique), enabled, service_name, min/max_replicas, cpu_high/low_percent, cooldown_seconds, current_replicas
waf_policies WafPolicy application_id (unique), mode, paranoia_level, anomaly_threshold, disabled_rule_ids

Application.to_dict() gained lightweight image_update and sleep badges.


4. Config flags & environment

Setting Default Purpose
encrypt_backups (BackupService config) false Opt-in client-side backup encryption (Fernet, reuses SERVERKIT_ENCRYPTION_KEY). Encrypts each artifact (.enc) before _auto_upload; restore decrypts transparently. Key loss = unrecoverable backups.
SERVERKIT_WAF_DIR (env) /etc/nginx/serverkit-conf.d/waf Where per-app WAF rules/includes are written.
SERVERKIT_MODSEC_AUDIT_LOG (env) /var/log/modsec_audit.log ModSecurity audit log parsed for the WAF events view.
localStorage['layout'] (browser) sidebar Shell geometry: sidebar | rail | topbar.

5. Operational notes

  • Cron the sweeps: POST /apps/sweep-idle (auto-sleep) and POST /apps/scale-sweep (auto-scale) are admin endpoints meant to be hit periodically. Wiring them to a built-in scheduler is a follow-up; for now drive them from cron.
  • Public DDNS endpoint: /ddns/update is the only unauthenticated route added (the per-host token is the credential). Serve it over HTTPS.
  • WAF integration is additive: per-app rules go to serverkit-conf.d/waf and an include is injected into the app vhost behind a # serverkit-waf marker; existing nginx/site generation is untouched. Enforcement needs nginx built with the ModSecurity connector + libmodsecurity + OWASP CRS on the host.

6. SSL/TLS hardening & optional HTTPS (security follow-up — 02afdec … a09bf0f)

HTTPS is optional and best-effort — the installer never blocks on SSL, and the panel never forces it. Hardening is applied so that when TLS is served, it's strong; it stays inert in HTTP-only mode.

Install/update (install.sh, scripts/update.sh)

  • Best-effort cert: tries Let's Encrypt (webroot); on failure falls back to plain HTTP instead of failing the install. SERVERKIT_SKIP_SSL=1 forces HTTP.
  • Two panel vhosts: serverkit.conf (HTTPS + HTTP→HTTPS redirect) and serverkit-insecure.conf (HTTP-only). The choice persists to /etc/serverkit/ssl-mode and is preserved across updates.
  • Server-wide TLS floor: harden_global_tls() rewrites/injects ssl_protocols TLSv1.2 TLSv1.3 + AEAD-only ssl_ciphers in the system nginx.conf http{}, so the default server and any non-ServerKit vhost are covered too. Edited in place to avoid nginx's duplicate-directive error on Debian/Ubuntu. update.sh re-applies it to existing installs.
  • Cloudflare-aware configs (Origin CA option + dashboard guidance) in nginx/sites-available/*.

Per-app vhosts (services/nginx_service.py SSL_BLOCK) — HSTS (includeSubDomains; preload), CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and ssl_ecdh_curve, in addition to the TLS 1.2/1.3 + AEAD set.

Auto-CAA on certificate issuancessl_service.obtain_certificate calls DNSProviderService.ensure_caa_record(domain) (best-effort, never fails the cert). It creates CAA 0 issue "letsencrypt.org" at the zone apex via whichever connected provider manages the domain (Cloudflare / DigitalOcean / Route53), and degrades to manual instructions otherwise. Cloudflare/DigitalOcean need CAA's structured data object (not a flat string) — handled. The cert response gains a caa: {created, provider, zone, record, …} field. The default DNS zone web-hosting preset also seeds a CAA record.

HSTS gating (panel) — the Flask security middleware emits HSTS only when the deployment terminates real HTTPS, resolved as SSL_MODE / HSTS_ENABLED in config.py. Behind nginx/Cloudflare, Flask can't tell real TLS from a Cloudflare Flexible edge via X-Forwarded-Proto, so it trusts the operator's recorded choice rather than the request scheme. The nginx edge emits HSTS independently in its secure server block.

Setting Where Purpose
SERVERKIT_SKIP_SSL=1 env (installer) Skip HTTPS entirely; run on plain HTTP.
SERVERKIT_SSL_MODE env / .env secure|insecure — gates the panel's HSTS header.
/etc/serverkit/ssl-mode file Persisted SSL mode (read by installer, updater, and config._resolve_ssl_mode).
/etc/serverkit/panel-domain file Persisted panel domain so update.sh re-applies the cert path without the old (broken) .env scrape.

Proving tests: backend/tests/test_dns_caa.py, backend/tests/test_security_headers.py.


7. Known follow-ups

  • Migration import (cPanel/CyberPanel) — not started; needs real archive samples.
  • Backups: additional remote targets (WebDAV/Azure/Dropbox/SFTP), streaming encryption for very large archives, and restore drills.
  • Auto-sleep: traffic-based idle detection (feed record_activity from the nginx log) and request-triggered wake-on-demand; remote-server sleep.
  • Periodic scheduler wiring for the sleep/scale sweeps.
  • A human visual pass over all of this session's new UI in a running environment.