- Bake version info (commit, branch, date) into /app/version.json at build time
via Docker ARG GIT_COMMIT/GIT_BRANCH/GIT_COMMIT_DATE
- Mount source directory as /app-source for in-container git operations
- Add git config safe.directory for /app-source (ownership mismatch fix)
- Add SystemConfig fields: git_repo_url, git_branch, git_token_encrypted
- Add DB migrations for the three new columns
- Add git_token encryption in update_settings() handler
- New endpoints:
GET /api/settings/version — current version + latest from Gitea API
POST /api/settings/update — DB backup + git pull + docker compose rebuild
- New service: app/services/update_service.py
get_current_version() — reads /app/version.json
check_for_updates() — queries Gitea API for latest commit on branch
backup_database() — timestamped SQLite copy to /app/backups/
trigger_update() — git pull + fire-and-forget compose rebuild
- New script: update.sh — SSH-based manual update with health check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract shared SlowAPI limiter to app/limiter.py to break circular
import between app.main and app.routers.auth
- Seed default SystemConfig row (id=1) on first DB init so settings
page works out of the box
- Make all docker_service.compose_* functions async (run_in_executor)
so long docker pulls/stops no longer block the async event loop
- Propagate async to netbird_service stop/start/restart and await
callers in deployments router
- Move customer delete to BackgroundTasks so the HTTP response returns
immediately and avoids frontend "Network error" on slow machines
- docker-compose: add :z SELinux labels, mount docker.sock directly,
add security_opt label:disable for socket access, extra_hosts for
host.docker.internal, enable DELETE/VOLUMES on socket proxy
- npm_service: auto-detect outbound host IP via UDP socket when
HOST_IP env var is not set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix#1 - SECRET_KEY startup validation (config.py, .env):
- App refuses to start if SECRET_KEY is missing, shorter than 32 chars,
or matches a known insecure default value
- .env: replaced hardcoded test key with placeholder + generation hint
Fix#2 - Docker socket proxy (docker-compose.yml):
- Add tecnativa/docker-socket-proxy sidecar
- Only expose required Docker API endpoints (CONTAINERS, IMAGES,
NETWORKS, POST, EXEC); dangerous endpoints explicitly blocked
- Remove direct /var/run/docker.sock mount from main container
- Route Docker API via DOCKER_HOST=tcp://docker-socket-proxy:2375
Fix#3 - Azure AD group whitelist (auth.py, models.py, validators.py):
- New azure_allowed_group_id field in SystemConfig
- After token exchange, verify group membership via Graph API /me/memberOf
- Deny login with HTTP 403 if user is not in the required group
- New Azure AD users now get role 'viewer' instead of 'admin'
Fix#4 - Rate limiting on login (main.py, auth.py, requirements.txt):
- Add slowapi==0.1.9 dependency
- Initialize SlowAPI limiter in main.py with 429 exception handler
- Apply 10 requests/minute limit per IP on /login and /mfa/verify
Socket detection inside Docker returns the container IP (172.18.0.x),
not the host IP. Now:
- install.sh detects host IP via hostname -I and stores in .env
- docker-compose.yml passes HOST_IP to the container
- npm_service.py reads HOST_IP from environment
- Increased SSL cert timeout to 120s (LE validation is slow)
- Added better logging for SSL cert creation/assignment
- README updated with HOST_IP in .env example
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>