WireGuard VPN Portal
PersonalA self-hosted web portal for managing WireGuard VPN configurations. Features automated CI/CD, rootless Podman deployment, and secure native key generation.
Project Context
I developed this self-hosted web portal to simplify WireGuard VPN management. The goal was to eliminate command-line configuration for end-users by providing a clean UI where they can view, download, and scan their VPN profiles via QR codes, while giving administrators a centralized dashboard to manage access.
Technical Architecture & Challenges
1. Secure Key Generation
Instead of executing wg shell commands from the web server (which poses command-injection risks), the application generates X25519 keypairs natively server-side using Python’s cryptography library.
- Security: The backend includes brute-force protection via
Flask-Limiterand secures sessions withFlask-LoginandFlask-Bcrypt.
2. Container-to-Host Boundary
WireGuard runs in the host kernel, but the web app runs isolated inside a Podman container. To bridge this securely:
- The container writes peer configurations to a shared volume (
/var/lib/vpn-portal/peers.conf). - A lightweight
systemdpath unit on the Rocky Linux host watches this file and automatically triggerswg addconfto apply changes to the livewg0interface without exposing host privileges to the container.
3. Zero-Downtime CI/CD Pipeline
I built a fully automated deployment pipeline using GitHub Actions:
- Build & Push: On every push to
main, GitHub Actions builds the image and pushes it to the GitHub Container Registry (GHCR). - Automated Deploy: The runner connects to the VPS via SSH, pulls the new image, and executes
podman-compose up -d. - Database Migrations: An entrypoint script handles SQLite database migrations (
Flask-Migrate) automatically on startup.