A secure, restricted SSH shell for managing WireGuard VPN clients remotely via PiVPN. Designed to allow safe client management from mobile devices while away from home.
- Restricted SSH Access: Limited shell with only whitelisted WireGuard management commands
- QR Code Generation: Generate QR codes for easy mobile client setup
- Web-Accessible QR Codes: HTTPS access to QR codes via reverse proxy
- Automatic Cleanup: QR codes auto-delete after 10 minutes for security
- Group-Based Permissions: Secure file access without world-readable permissions
- Interactive Mode: Works with mobile SSH clients like ConnectBot
[Mobile Device] --> SSH --> [WG Server] --> [Restricted Shell]
|
+--> [QR HTTP Server] <-- [Reverse Proxy]
(port 8888) (HTTPS)
- Linux server with PiVPN already installed and configured
- WireGuard VPN server running
- Root/sudo access for setup
- Optional: Reverse proxy server (e.g., Caddy) for external HTTPS access
The restricted shell automatically detects PiVPN configuration by reading /etc/pivpn/wireguard/setupVars.conf:
- PiVPN User: Reads
install_user(which user installed PiVPN) - Config Path: Reads
install_home(where configs are stored) - Hostname: Reads
pivpnHOST(WireGuard endpoint hostname)
This means no manual editing of paths or usernames is required. The script adapts to your PiVPN installation automatically.
The QR code domain can be set via environment variable:
- During installation: Prompted by
install.sh - After installation: Set
QR_DOMAINin/home/wgadmin/.config/environment
Run the automated installation script on your PiVPN server:
sudo ./install.shThe script will:
- Auto-detect PiVPN user, home directory, and hostname
- Create wgadmin user and configure permissions
- Install and configure all services
- Set up automatic cleanup
You'll be prompted for:
- QR code hostname (defaults to server hostname for tunnel/local access)
Add your SSH public key with command restriction:
sudo tee /home/wgadmin/.ssh/authorized_keys << EOF
command="/home/wgadmin/restricted-shell.sh" ssh-ed25519 YOUR_PUBLIC_KEY user@device
EOF
sudo chmod 600 /home/wgadmin/.ssh/authorized_keys
sudo chown -R wgadmin:wgadmin /home/wgadmin/.sshAdd a DNS record for the QR hostname pointing to the WireGuard server IP (for tunnel/local access only):
# Example: Add to /etc/hosts or your local DNS server
192.168.1.10 wg-serverQR Code Access Restrictions:
The HTTP server only accepts connections from:
- WireGuard tunnel subnet (auto-detected from PiVPN config)
- Local network (auto-detected from server IP)
Why this matters:
- QR codes are NOT exposed to the internet
- You must be connected via VPN or on local network to access them
- Even if someone discovers the URL, they cannot access it externally
- No need for authentication - network isolation provides security
This means when you're away:
- Connect to your WireGuard VPN from your phone
- SSH to wgadmin@server (via tunnel)
- Generate QR code with
qrpng <name> - Access via browser while still on VPN:
http://server-hostname:8888/wg-<name>-qr.png
From your mobile device (e.g., using ConnectBot):
ssh wgadmin@your-wg-serverOnce connected, you'll see an interactive shell:
=== WireGuard Admin Shell ===
Available commands:
add <name> - Add a new WireGuard client
remove - Remove a WireGuard client (interactive)
list - List all WireGuard clients
qr <name> - Show QR code for a client (terminal)
qrpng <name> - Generate QR code PNG file for a client
qrlist - List available QR code PNG files
qrclean <name> - Delete a QR code PNG file
share <name> - Display config for copy/paste (for Bitwarden Send)
clients - Show connected clients
status - Show WireGuard server status
config <name> - View client config file
configs - List available config files
help - Show this help message
exit - Exit the shell
- Connect via SSH to wgadmin@server
- Add client:
add laptop - Generate QR:
qrpng laptop - Access QR: Open browser to
https://codes.example.com/wg-laptop-qr.png - Scan QR with WireGuard mobile app
- Cleanup: QR auto-deletes after 10 minutes (or use
qrclean laptop)
- Only whitelisted commands allowed
- No shell access or system commands
- Prevents command injection
- Path traversal protection
- Group-based access (not world-readable)
- Restricted user cannot access other system files
- Configs only accessible via sudo with specific paths
- Stored in dedicated directory (
/var/lib/wg-qrcodes) - Auto-deleted after 10 minutes
- Can be manually cleaned with
qrclean - No mixing with system temp files
- Key-based authentication only
- Command forced via
authorized_keys - No direct shell access
- Interactive loop within restricted environment
# Check SSH service
sudo systemctl status sshd
# Test SSH key
ssh -i /path/to/key wgadmin@server
# Check authorized_keys
sudo cat /home/wgadmin/.ssh/authorized_keys# Validate sudoers
sudo visudo -c
# Check sudoers file
sudo cat /etc/sudoers.d/wgadmin
# Test sudo as wgadmin
sudo -u wgadmin sudo /usr/local/bin/pivpn list# Check service status
sudo systemctl status wg-qr-server.service
# Check port
sudo netstat -tlnp | grep 8888
# Test locally
curl -I http://localhost:8888/# Check timer status
systemctl list-timers wg-qr-cleanup.timer
# Check last run
sudo journalctl -u wg-qr-cleanup.service -n 20
# Manually trigger
sudo systemctl start wg-qr-cleanup.service# Check group membership
id wgadmin
# Check directory permissions
ls -ld /home/PIVPN_USER/configs
ls -l /home/PIVPN_USER/configs/
# Verify wgadmin in correct group
sudo usermod -a -G PIVPN_USER wgadminEdit /etc/systemd/system/wg-qr-cleanup.timer:
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min # Change this valueEdit /etc/systemd/system/wg-qr-cleanup.service:
ExecStart=/usr/bin/find /var/lib/wg-qrcodes -name "wg-*-qr.png" -type f -mmin +10 -delete
# Change +10 to desired minutesThen reload systemd:
sudo systemctl daemon-reload
sudo systemctl restart wg-qr-cleanup.timerEdit /etc/systemd/system/wg-qr-server.service:
ExecStart=/usr/bin/python3 -m http.server 8888 --directory /var/lib/wg-qrcodes
# Change 8888 to desired portThen:
sudo systemctl daemon-reload
sudo systemctl restart wg-qr-server.serviceDon't forget to update:
restricted-shell.sh(help message and qrpng output)- Reverse proxy configuration
- Firewall rules if applicable
wg-admin-shell/
├── README.md # This file
├── scripts/
│ └── restricted-shell.sh # Main restricted shell script
├── systemd/
│ ├── wg-qr-server.service # QR code HTTP server
│ ├── wg-qr-cleanup.service # Cleanup service
│ └── wg-qr-cleanup.timer # Cleanup timer
├── caddy/
│ └── Caddyfile.example # Example reverse proxy config
└── sudoers-wgadmin # Sudo permissions file
MIT License - Feel free to use and modify as needed.
Contributions welcome! Please ensure:
- Security best practices are maintained
- Code is well-documented
- Changes are tested on a clean PiVPN installation
- Built for PiVPN (https://pivpn.io/)
- Designed for use with WireGuard VPN
- Tested with ConnectBot SSH client on Android