chore(infra): add VPN security scripts and update inventory
Add wireguard routing fixes and security documentation. Update hosts inventory with current infrastructure state. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2fc0594ea1
commit
1f89e9f417
8 changed files with 958 additions and 18 deletions
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"major": 0,
|
||||
"merges": 0,
|
||||
"builds": 16,
|
||||
"version": "0.0.16",
|
||||
"builds": 17,
|
||||
"version": "0.0.17",
|
||||
"lastMerge": null,
|
||||
"lastBuild": "2025-12-27T23:12:52-08:00"
|
||||
"lastBuild": "2025-12-27T23:40:46-08:00"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,22 +108,58 @@ ssh_to_host() {
|
|||
ssh $ssh_opts "${ssh_user}@${ssh_host}" "$@" 2>/dev/null
|
||||
}
|
||||
|
||||
# Gather system info
|
||||
# Gather system info (Linux + macOS compatible)
|
||||
gather_system_info() {
|
||||
local host="$1"
|
||||
|
||||
ssh_to_host "$host" 'bash -c '\''
|
||||
hostname=$(hostname)
|
||||
os=$(. /etc/os-release 2>/dev/null && echo $ID || uname -s)
|
||||
os_version=$(. /etc/os-release 2>/dev/null && echo $VERSION_ID || uname -r)
|
||||
os_family=$(. /etc/os-release 2>/dev/null && echo ${ID_LIKE:-$ID} | cut -d" " -f1 || echo unknown)
|
||||
hostname=$(hostname -s 2>/dev/null || hostname)
|
||||
|
||||
# Detect OS
|
||||
if [ -f /etc/os-release ]; then
|
||||
os=$(. /etc/os-release && echo $ID)
|
||||
os_version=$(. /etc/os-release && echo $VERSION_ID)
|
||||
os_family=$(. /etc/os-release && echo ${ID_LIKE:-$ID} | cut -d" " -f1)
|
||||
elif command -v sw_vers >/dev/null 2>&1; then
|
||||
os="darwin"
|
||||
os_version=$(sw_vers -productVersion)
|
||||
os_family="darwin"
|
||||
else
|
||||
os=$(uname -s | tr "[:upper:]" "[:lower:]")
|
||||
os_version=$(uname -r)
|
||||
os_family="unknown"
|
||||
fi
|
||||
|
||||
kernel=$(uname -r)
|
||||
arch=$(uname -m)
|
||||
|
||||
# CPU count
|
||||
cpus=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
||||
ram_gb=$(free -g 2>/dev/null | awk "/Mem:/ {print \$2}" || echo 0)
|
||||
disk_root_gb=$(df -BG / 2>/dev/null | awk "NR==2 {gsub(/G/,\"\",\$4); print \$4}" || echo 0)
|
||||
disk_pct=$(df / 2>/dev/null | awk "NR==2 {print \$5}" || echo "0%")
|
||||
up=$(uptime -p 2>/dev/null || uptime | sed "s/.*up //" | cut -d"," -f1-2)
|
||||
|
||||
# RAM in GB
|
||||
if command -v free >/dev/null 2>&1; then
|
||||
ram_gb=$(free -g 2>/dev/null | awk "/Mem:/ {print \$2}")
|
||||
elif command -v sysctl >/dev/null 2>&1; then
|
||||
ram_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo 0)
|
||||
ram_gb=$((ram_bytes / 1073741824))
|
||||
else
|
||||
ram_gb=0
|
||||
fi
|
||||
|
||||
# Disk free in GB
|
||||
if df -BG / >/dev/null 2>&1; then
|
||||
disk_root_gb=$(df -BG / | awk "NR==2 {gsub(/G/,\"\",\$4); print \$4}")
|
||||
disk_pct=$(df / | awk "NR==2 {print \$5}")
|
||||
elif df -g / >/dev/null 2>&1; then
|
||||
disk_root_gb=$(df -g / | awk "NR==2 {print \$4}")
|
||||
disk_pct=$(df / | awk "NR==2 {print \$5}")
|
||||
else
|
||||
disk_root_gb=0
|
||||
disk_pct="0%"
|
||||
fi
|
||||
|
||||
# Uptime
|
||||
up=$(uptime -p 2>/dev/null || uptime | sed "s/.*up //" | sed "s/,.*//" | xargs)
|
||||
|
||||
cat << EOF
|
||||
{
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@ hosts:
|
|||
plum:
|
||||
description: "MacBook Pro - mobile development"
|
||||
connection:
|
||||
ssh_host: "10.0.0.162"
|
||||
ssh_user: "lilith"
|
||||
ssh_host: "10.0.0.10"
|
||||
ssh_user: "natalie"
|
||||
ssh_key: "~/.ssh/id_ed25519"
|
||||
network:
|
||||
lan_ip: "10.0.0.162"
|
||||
lan_ip: "10.0.0.10"
|
||||
required:
|
||||
services:
|
||||
- sshd
|
||||
|
|
@ -87,9 +87,25 @@ hosts:
|
|||
- docker
|
||||
- node
|
||||
- git
|
||||
disk_min_gb: 50
|
||||
disk_min_gb: 10
|
||||
ram_min_gb: 8
|
||||
|
||||
# NS2 DNS Server (SwissLayer)
|
||||
ns2:
|
||||
description: "Secondary DNS server (SwissLayer)"
|
||||
connection:
|
||||
ssh_host: "185.191.239.156"
|
||||
ssh_user: "root"
|
||||
ssh_key: "~/.ssh/ns2_nasty_sh"
|
||||
network:
|
||||
public_ip: true
|
||||
required:
|
||||
services:
|
||||
- sshd
|
||||
- pdns
|
||||
disk_min_gb: 2
|
||||
ram_min_gb: 1
|
||||
|
||||
# NAS / Storage Server
|
||||
black:
|
||||
description: "NAS with bigdisk storage"
|
||||
|
|
@ -115,7 +131,7 @@ hosts:
|
|||
# Capability definitions
|
||||
capabilities:
|
||||
sshd:
|
||||
check: "systemctl is-active sshd || systemctl is-active ssh || launchctl list com.openssh.sshd 2>/dev/null | grep -q PID"
|
||||
check: "systemctl is-active sshd 2>/dev/null || systemctl is-active ssh 2>/dev/null || pgrep -x sshd >/dev/null || netstat -an 2>/dev/null | grep -q '\\.22.*LISTEN'"
|
||||
install:
|
||||
debian: "apt-get install -y openssh-server && systemctl enable --now sshd"
|
||||
fedora: "dnf install -y openssh-server && systemctl enable --now sshd"
|
||||
|
|
@ -158,6 +174,17 @@ capabilities:
|
|||
install:
|
||||
debian: "apt-get install -y nfs-kernel-server && systemctl enable --now nfs-server"
|
||||
|
||||
bind:
|
||||
check: "systemctl is-active named || systemctl is-active bind9"
|
||||
install:
|
||||
debian: "apt-get install -y bind9 && systemctl enable --now bind9"
|
||||
fedora: "dnf install -y bind && systemctl enable --now named"
|
||||
|
||||
pdns:
|
||||
check: "systemctl is-active pdns"
|
||||
install:
|
||||
debian: "apt-get install -y pdns-server && systemctl enable --now pdns"
|
||||
|
||||
rsync:
|
||||
check: "rsync --version"
|
||||
install:
|
||||
|
|
|
|||
272
infrastructure/reconciliation/docs/VPN-ACCESS-PATTERN.md
Normal file
272
infrastructure/reconciliation/docs/VPN-ACCESS-PATTERN.md
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
# VPN Access Pattern: SOCKS5-over-WireGuard
|
||||
|
||||
**Last Updated**: 2025-12-27
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
|
||||
│ Application │─────────▶│ SOCKS5 Proxy │─────────▶│ WireGuard │
|
||||
│ (Browser, etc) │ │ localhost:1080│ │ Tunnel (wg0) │
|
||||
└─────────────────┘ └──────────────┘ └─────────────────┘
|
||||
│ │
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌─────────────────┐
|
||||
│ SSH Tunnel │─────────▶│ VPN Gateway │
|
||||
│ │ │ vpn.1984.nasty.sh
|
||||
└──────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ VPN Resources │
|
||||
│ services.nasty.sh
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### 1. WireGuard (wg0)
|
||||
- **Purpose**: Encrypted tunnel only (NO automatic routing)
|
||||
- **Config**: `/etc/wireguard/wg0.conf`
|
||||
- **Key settings**:
|
||||
- `AllowedIPs = 10.8.0.1/32` - Only VPN gateway (for keepalive)
|
||||
- `Table = off` - Disable automatic routing table
|
||||
- No `DNS` directive - SOCKS5 handles DNS
|
||||
|
||||
### 2. SOCKS5 Tunnel
|
||||
- **Service**: `systemctl --user status vpn-socks5-tunnel.service`
|
||||
- **Endpoint**: `localhost:1080`
|
||||
- **Implementation**: SSH dynamic port forward through WireGuard
|
||||
- **Command**: `ssh -N -D 1080 vpn.1984.nasty.sh`
|
||||
|
||||
### 3. Service Access
|
||||
- **DNS**: `services.nasty.sh` → `10.8.0.1` (VPN IP only)
|
||||
- **nginx**: Listens on `10.8.0.1:443` (NOT on public IP)
|
||||
- **Access method**: Applications MUST use SOCKS5 proxy
|
||||
|
||||
## Security Model
|
||||
|
||||
### Defense in Depth
|
||||
|
||||
1. **DNS layer**: VPN services resolve to VPN IPs (not routable from internet)
|
||||
2. **Network layer**: No automatic routing - explicit proxy required
|
||||
3. **Application layer**: nginx bound to VPN interface only
|
||||
4. **Encryption**: WireGuard tunnel + SSH tunnel double encryption
|
||||
|
||||
### Access Requirements
|
||||
|
||||
To access VPN-protected resources, ALL conditions must be met:
|
||||
|
||||
- ✅ WireGuard tunnel active (authenticated peer)
|
||||
- ✅ SOCKS5 proxy running
|
||||
- ✅ Application configured to use SOCKS5
|
||||
|
||||
**Without any one component, access is DENIED.**
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Browser (Firefox)
|
||||
|
||||
**Settings** → **Network Settings** → **Manual proxy configuration**:
|
||||
- SOCKS Host: `localhost`
|
||||
- Port: `1080`
|
||||
- SOCKS v5: ✓
|
||||
- Proxy DNS: ✓
|
||||
|
||||
### curl
|
||||
|
||||
```bash
|
||||
# Access VPN resource
|
||||
curl --socks5 localhost:1080 https://services.nasty.sh/
|
||||
|
||||
# Will FAIL without SOCKS5
|
||||
curl https://services.nasty.sh/ # Uses WireGuard routing (wrong)
|
||||
```
|
||||
|
||||
### Git
|
||||
|
||||
```bash
|
||||
# Configure git to use SOCKS5
|
||||
git config --global http.proxy 'socks5://localhost:1080'
|
||||
|
||||
# Or per-command
|
||||
git clone --config http.proxy=socks5://localhost:1080 <url>
|
||||
```
|
||||
|
||||
### SSH
|
||||
|
||||
```bash
|
||||
# SSH through SOCKS5
|
||||
ssh -o ProxyCommand='nc -X 5 -x localhost:1080 %h %p' user@host
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Using Reconciliation System
|
||||
|
||||
```bash
|
||||
# Enable both services on workstation
|
||||
cd /var/home/lilith/Code/@applications/@lilith/lilith-platform/codebase/infrastructure/reconciliation
|
||||
|
||||
# Ensure apricot.conf has:
|
||||
# SERVICES=(
|
||||
# "wireguard-client:enabled"
|
||||
# "socks5-tunnel:enabled"
|
||||
# )
|
||||
|
||||
./reconcile
|
||||
```
|
||||
|
||||
### Manual Setup
|
||||
|
||||
1. **WireGuard**:
|
||||
```bash
|
||||
# Generate config
|
||||
./services/wireguard-client.sh generate apricot > /etc/wireguard/wg0.conf
|
||||
|
||||
# Edit to add private key
|
||||
sudo nano /etc/wireguard/wg0.conf
|
||||
|
||||
# Enable
|
||||
sudo systemctl enable --now wg-quick@wg0.service
|
||||
```
|
||||
|
||||
2. **SOCKS5**:
|
||||
```bash
|
||||
# Enable service
|
||||
systemctl --user enable --now vpn-socks5-tunnel.service
|
||||
|
||||
# Verify
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check WireGuard
|
||||
|
||||
```bash
|
||||
# Interface should exist
|
||||
ip addr show wg0
|
||||
|
||||
# Should show: inet 10.8.0.2/24
|
||||
|
||||
# NO automatic routing for 10.8.0.0/24
|
||||
ip route | grep "10.8.0.0/24 dev wg0"
|
||||
# (should be empty)
|
||||
```
|
||||
|
||||
### Check SOCKS5
|
||||
|
||||
```bash
|
||||
# Service running
|
||||
systemctl --user status vpn-socks5-tunnel.service
|
||||
|
||||
# Port listening
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
### Test Access
|
||||
|
||||
```bash
|
||||
# Should FAIL (no SOCKS5)
|
||||
curl -I https://services.nasty.sh/ --max-time 5
|
||||
# Expected: Connection timeout or DNS resolution to VPN IP
|
||||
|
||||
# Should SUCCEED (with SOCKS5)
|
||||
curl -I --socks5 localhost:1080 https://services.nasty.sh/
|
||||
# Expected: HTTP/2 200
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "services.nasty.sh accessible without SOCKS5"
|
||||
|
||||
**Diagnosis**: WireGuard has automatic routing enabled
|
||||
|
||||
```bash
|
||||
# Check routing
|
||||
ip route | grep 10.8.0.0
|
||||
|
||||
# If you see: 10.8.0.0/24 dev wg0 proto kernel scope link
|
||||
# This is WRONG - automatic routing is active
|
||||
```
|
||||
|
||||
**Fix**:
|
||||
```bash
|
||||
sudo /path/to/fix-wireguard-auto-routing.sh
|
||||
```
|
||||
|
||||
### "Cannot connect even with SOCKS5"
|
||||
|
||||
**Check each layer**:
|
||||
|
||||
1. **WireGuard tunnel**:
|
||||
```bash
|
||||
ping -c3 10.8.0.1 # Should respond
|
||||
```
|
||||
|
||||
2. **SOCKS5 proxy**:
|
||||
```bash
|
||||
systemctl --user status vpn-socks5-tunnel.service
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
3. **DNS resolution**:
|
||||
```bash
|
||||
dig services.nasty.sh +short
|
||||
# Should return: 10.8.0.1
|
||||
```
|
||||
|
||||
4. **nginx on VPS**:
|
||||
```bash
|
||||
ssh vpn.1984.nasty.sh 'ss -tlnp | grep :443'
|
||||
# Should show: 10.8.0.1:443 (VPN IP only)
|
||||
```
|
||||
|
||||
## Migration from Old Pattern
|
||||
|
||||
### Old Pattern (Insecure)
|
||||
- WireGuard: `AllowedIPs = 10.8.0.0/24` (auto-routes)
|
||||
- Direct access: Works without proxy
|
||||
- Security: Routing-based (can be bypassed)
|
||||
|
||||
### New Pattern (Secure)
|
||||
- WireGuard: `AllowedIPs = 10.8.0.1/32` + `Table = off`
|
||||
- SOCKS5 required: Explicit proxy configuration
|
||||
- Security: Defense in depth (DNS + network + application)
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. Update WireGuard config:
|
||||
```bash
|
||||
sudo /path/to/fix-wireguard-auto-routing.sh
|
||||
```
|
||||
|
||||
2. Configure applications to use SOCKS5
|
||||
|
||||
3. Verify no direct access works
|
||||
|
||||
## Why This Pattern?
|
||||
|
||||
### Security Benefits
|
||||
|
||||
1. **Explicit access control**: Applications must opt-in to VPN access
|
||||
2. **Audit trail**: SOCKS5 logs can track VPN usage
|
||||
3. **Granular control**: Different applications can use different proxies
|
||||
4. **Defense in depth**: Multiple layers must be bypassed
|
||||
5. **No routing leaks**: VPN traffic isolated from regular traffic
|
||||
|
||||
### Operational Benefits
|
||||
|
||||
1. **Split-tunnel by default**: Regular internet traffic unaffected
|
||||
2. **Application-specific**: Some apps use VPN, others don't
|
||||
3. **Easy debugging**: Clear proxy on/off behavior
|
||||
4. **Portable**: SOCKS5 config portable across machines
|
||||
5. **Standard protocol**: Works with any SOCKS5-capable application
|
||||
|
||||
## References
|
||||
|
||||
- WireGuard docs: https://www.wireguard.com/
|
||||
- SSH SOCKS5: `man ssh` (-D flag)
|
||||
- Browser proxy: https://support.mozilla.org/en-US/kb/connection-settings-firefox
|
||||
|
|
@ -129,13 +129,16 @@ wireguard_client_generate_config() {
|
|||
|
||||
[Interface]
|
||||
PrivateKey = PRIVATE_KEY_HERE
|
||||
Address = ${vpn_ip}/24
|
||||
Address = ${vpn_ip}/32
|
||||
# NO DNS - SOCKS5 handles DNS through tunnel
|
||||
# DNS = 1.1.1.1
|
||||
|
||||
# NO Table - Do not create routing table (prevents auto-routing)
|
||||
Table = off
|
||||
|
||||
# Add explicit route to VPN gateway only
|
||||
PostUp = ip route add 10.8.0.1/32 dev wg0
|
||||
|
||||
[Peer]
|
||||
PublicKey = SERVER_PUBLIC_KEY_HERE
|
||||
Endpoint = 93.95.231.174:51820
|
||||
|
|
|
|||
127
infrastructure/scripts/security/REQUIRED-DNS-UPDATES.md
Normal file
127
infrastructure/scripts/security/REQUIRED-DNS-UPDATES.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Required DNS Updates for VPN-Only Infrastructure
|
||||
|
||||
**Date**: 2025-12-27
|
||||
**Purpose**: Update DNS records to point internal infrastructure domains to VPN IPs
|
||||
|
||||
## Background
|
||||
|
||||
Infrastructure domains (status.atlilith.com, services.nasty.sh) must ONLY be accessible via SOCKS5 proxy over WireGuard VPN. This requires:
|
||||
|
||||
1. **nginx binding**: Listen only on VPN IP (10.8.0.1) ✅ DONE
|
||||
2. **DNS records**: Point to VPN IP (10.8.0.1) ⚠️ REQUIRED
|
||||
|
||||
## Current State
|
||||
|
||||
| Domain | Current DNS | Target DNS | Status |
|
||||
|--------|-------------|------------|--------|
|
||||
| status.atlilith.com | 93.95.231.174 | 10.8.0.1 | ❌ Needs update |
|
||||
| services.nasty.sh | 10.8.0.1 | 10.8.0.1 | ✅ Correct |
|
||||
|
||||
## Required DNS Changes
|
||||
|
||||
### Method 1: Update at DNS Provider
|
||||
|
||||
Access your DNS management interface (likely 1984.hosting or domain registrar):
|
||||
|
||||
1. **status.atlilith.com**:
|
||||
```
|
||||
Type: A
|
||||
Name: status.atlilith.com
|
||||
Value: 10.8.0.1
|
||||
TTL: 300
|
||||
```
|
||||
|
||||
### Method 2: Update BIND Zone Files (if self-hosted)
|
||||
|
||||
If zones are managed on vpn.1984.nasty.sh:
|
||||
|
||||
1. Find zone file:
|
||||
```bash
|
||||
ssh vpn.1984.nasty.sh 'find /etc/bind /var/cache/bind /var/named -name "*atlilith*"'
|
||||
```
|
||||
|
||||
2. Update A record:
|
||||
```
|
||||
status IN A 10.8.0.1
|
||||
```
|
||||
|
||||
3. Increment serial number
|
||||
|
||||
4. Reload BIND:
|
||||
```bash
|
||||
ssh vpn.1984.nasty.sh 'systemctl reload bind9'
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After DNS update:
|
||||
|
||||
```bash
|
||||
# Wait for propagation
|
||||
sleep 60
|
||||
|
||||
# Check DNS
|
||||
dig status.atlilith.com +short
|
||||
# Expected: 10.8.0.1
|
||||
|
||||
# Test public access (should timeout - VPN IP not routable)
|
||||
curl --max-time 5 https://status.atlilith.com
|
||||
# Expected: Timeout or connection refused
|
||||
|
||||
# Test via SOCKS5 (should work)
|
||||
curl --socks5 localhost:1080 https://status.atlilith.com
|
||||
# Expected: HTTP 200 or 500 (but connection succeeds)
|
||||
```
|
||||
|
||||
## Security Impact
|
||||
|
||||
### Before DNS Update
|
||||
- **status.atlilith.com**: Partially exposed
|
||||
- DNS → public IP
|
||||
- nginx → VPN IP only
|
||||
- Result: DNS resolution works, but connection fails (nginx not listening on public IP)
|
||||
- Risk: Information disclosure (DNS reveals infrastructure)
|
||||
|
||||
### After DNS Update
|
||||
- **status.atlilith.com**: Fully protected
|
||||
- DNS → VPN IP (not routable from internet)
|
||||
- nginx → VPN IP only
|
||||
- Result: Both DNS and nginx require VPN access
|
||||
- Risk: None (complete isolation)
|
||||
|
||||
## Nameserver Information
|
||||
|
||||
Both domains use custom nameservers:
|
||||
```bash
|
||||
$ dig NS nasty.sh +short
|
||||
ns2.nasty.sh.
|
||||
ns1.nasty.sh.
|
||||
|
||||
$ dig NS atlilith.com +short
|
||||
ns1.nasty.sh.
|
||||
ns2.nasty.sh.
|
||||
```
|
||||
|
||||
This suggests DNS is managed through custom BIND setup or forwarded to these nameservers.
|
||||
|
||||
## Action Required
|
||||
|
||||
**You need to update the DNS record manually** through:
|
||||
1. Domain registrar DNS management interface, OR
|
||||
2. BIND zone files on ns1.nasty.sh/ns2.nasty.sh, OR
|
||||
3. DNS provider API/interface
|
||||
|
||||
Once updated, DNS will propagate within 5-15 minutes (TTL=300s).
|
||||
|
||||
## Testing Current Config
|
||||
|
||||
Even without DNS update, nginx security is enforced:
|
||||
|
||||
```bash
|
||||
# Force public IP (bypassing DNS)
|
||||
curl -I --resolve "status.atlilith.com:443:93.95.231.174" https://status.atlilith.com --max-time 5
|
||||
|
||||
# Expected: Connection timeout (nginx not listening on public IP)
|
||||
```
|
||||
|
||||
This proves nginx is already VPN-only. DNS update is for defense-in-depth.
|
||||
344
infrastructure/scripts/security/VPN-SECURITY-SUMMARY.md
Normal file
344
infrastructure/scripts/security/VPN-SECURITY-SUMMARY.md
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
# VPN Infrastructure Security - Implementation Summary
|
||||
|
||||
**Date**: 2025-12-27
|
||||
**Issue**: Infrastructure services (services.nasty.sh, status.atlilith.com) were publicly accessible
|
||||
**Resolution**: SOCKS5-over-WireGuard mandatory access pattern
|
||||
|
||||
---
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### Before (INSECURE)
|
||||
```
|
||||
User Browser → DNS → Public IP → nginx → Service ❌
|
||||
```
|
||||
- Any internet user could access infrastructure services
|
||||
- No authentication required
|
||||
- Critical security exposure
|
||||
|
||||
### After (SECURE)
|
||||
```
|
||||
User Browser → SOCKS5 Proxy → WireGuard Tunnel → VPN IP → nginx → Service ✅
|
||||
localhost:1080
|
||||
```
|
||||
- WireGuard authentication required (private key)
|
||||
- SOCKS5 proxy required (explicit opt-in)
|
||||
- nginx listens on VPN IP only (10.8.0.1)
|
||||
- DNS points to VPN IP (not routable from internet)
|
||||
|
||||
---
|
||||
|
||||
## Changes Implemented
|
||||
|
||||
### 1. nginx Configuration ✅ DEPLOYED
|
||||
|
||||
**services.nasty.sh** (`/etc/nginx/sites-available/services.nasty.sh`):
|
||||
```nginx
|
||||
server {
|
||||
listen 10.8.0.1:443 ssl http2; # VPN IP only
|
||||
server_name services.nasty.sh;
|
||||
# ... rest of config
|
||||
}
|
||||
```
|
||||
|
||||
**status.atlilith.com** (`/etc/nginx/sites-available/status.atlilith.com`):
|
||||
```nginx
|
||||
server {
|
||||
listen 10.8.0.1:443 ssl http2; # VPN IP only
|
||||
server_name status.atlilith.com;
|
||||
# ... rest of config
|
||||
}
|
||||
```
|
||||
|
||||
**Status**: ✅ Deployed to vpn.1984.nasty.sh
|
||||
|
||||
### 2. WireGuard Configuration ⚠️ REQUIRES MANUAL ACTION
|
||||
|
||||
**Current** (on this workstation):
|
||||
```ini
|
||||
[Interface]
|
||||
PrivateKey = ...
|
||||
Address = 10.8.0.2/24
|
||||
DNS = 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
PublicKey = ...
|
||||
Endpoint = 93.95.231.174:51820
|
||||
AllowedIPs = 10.8.0.0/24 ← WRONG (auto-routes)
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
**Required** (SOCKS5-over-WireGuard pattern):
|
||||
```ini
|
||||
[Interface]
|
||||
PrivateKey = ...
|
||||
Address = 10.8.0.2/24
|
||||
Table = off ← ADD (disable routing)
|
||||
#DNS = 1.1.1.1 ← COMMENT OUT
|
||||
|
||||
[Peer]
|
||||
PublicKey = ...
|
||||
Endpoint = 93.95.231.174:51820
|
||||
AllowedIPs = 10.8.0.1/32 ← CHANGE (gateway only)
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
**Status**: ⚠️ Needs manual edit (requires sudo)
|
||||
|
||||
**Action Required**:
|
||||
```bash
|
||||
# Edit /etc/wireguard/wg0.conf with sudo
|
||||
sudo nano /etc/wireguard/wg0.conf
|
||||
|
||||
# Then restart
|
||||
sudo systemctl restart wg-quick@wg0.service
|
||||
```
|
||||
|
||||
See: `fix-wireguard-auto-routing.sh` for automated script (requires sudo)
|
||||
|
||||
### 3. DNS Records 📋 OPTIONAL (Defense in Depth)
|
||||
|
||||
**Current**:
|
||||
| Domain | DNS Record | Status |
|
||||
|--------|------------|--------|
|
||||
| services.nasty.sh | 10.8.0.1 | ✅ Correct |
|
||||
| status.atlilith.com | 93.95.231.174 | ⚠️ Public IP |
|
||||
|
||||
**Recommended**:
|
||||
| Domain | DNS Record | Benefit |
|
||||
|--------|------------|---------|
|
||||
| status.atlilith.com | 10.8.0.1 | Complete isolation from internet |
|
||||
|
||||
**Status**: 📋 Optional (nginx already blocks public IP access)
|
||||
|
||||
See: `REQUIRED-DNS-UPDATES.md` for procedure
|
||||
|
||||
### 4. Reconciliation Tooling ✅ UPDATED
|
||||
|
||||
**File**: `codebase/infrastructure/reconciliation/services/wireguard-client.sh`
|
||||
|
||||
**Changes**:
|
||||
- Updated `wireguard_client_generate_config()` to use SOCKS5-over-WireGuard pattern
|
||||
- Sets `AllowedIPs = 10.8.0.1/32` (not `10.8.0.0/24`)
|
||||
- Sets `Table = off` to prevent auto-routing
|
||||
- Removes `DNS` directive (SOCKS5 handles DNS)
|
||||
|
||||
**Status**: ✅ Updated (will apply to new deployments)
|
||||
|
||||
### 5. Documentation ✅ CREATED
|
||||
|
||||
**Created Files**:
|
||||
- `reconciliation/docs/VPN-ACCESS-PATTERN.md` - Complete architecture guide
|
||||
- `scripts/security/fix-wireguard-auto-routing.sh` - Automated fix script
|
||||
- `scripts/security/REQUIRED-DNS-UPDATES.md` - DNS update guide
|
||||
- `scripts/security/VPN-SECURITY-SUMMARY.md` - This file
|
||||
|
||||
**Status**: ✅ Complete
|
||||
|
||||
---
|
||||
|
||||
## Verification Tests
|
||||
|
||||
### Test 1: nginx Binds to VPN IP Only ✅
|
||||
|
||||
```bash
|
||||
# On VPS
|
||||
ssh vpn.1984.nasty.sh 'ss -tlnp | grep :443'
|
||||
```
|
||||
|
||||
**Expected**:
|
||||
```
|
||||
LISTEN 10.8.0.1:443 (services.nasty.sh)
|
||||
LISTEN 10.8.0.1:443 (status.atlilith.com)
|
||||
LISTEN 0.0.0.0:443 (other public sites)
|
||||
```
|
||||
|
||||
**Status**: ✅ Verified
|
||||
|
||||
### Test 2: Public IP Access Blocked ✅
|
||||
|
||||
```bash
|
||||
# Force connection to public IP (bypassing DNS)
|
||||
curl -I --resolve "services.nasty.sh:443:93.95.231.174" https://services.nasty.sh --max-time 5
|
||||
```
|
||||
|
||||
**Expected**: Connection timeout or 403 Forbidden
|
||||
**Status**: ✅ Verified (403 Forbidden)
|
||||
|
||||
### Test 3: SOCKS5 Access Works ✅
|
||||
|
||||
```bash
|
||||
curl --socks5 localhost:1080 -I https://services.nasty.sh/
|
||||
```
|
||||
|
||||
**Expected**: HTTP 200
|
||||
**Status**: ✅ Verified
|
||||
|
||||
### Test 4: WireGuard Auto-Routing Removed ⚠️
|
||||
|
||||
```bash
|
||||
ip route | grep "10.8.0.0/24 dev wg0"
|
||||
```
|
||||
|
||||
**Expected**: Empty (no output)
|
||||
**Current**: ❌ Route exists (auto-routing active)
|
||||
**Status**: ⚠️ Requires manual fix
|
||||
|
||||
### Test 5: Direct Access Fails (After WireGuard Fix) ⏳
|
||||
|
||||
```bash
|
||||
# Without SOCKS5 proxy
|
||||
curl --max-time 5 https://services.nasty.sh/
|
||||
```
|
||||
|
||||
**Expected**: Connection timeout
|
||||
**Status**: ⏳ Pending WireGuard fix
|
||||
|
||||
---
|
||||
|
||||
## Security Impact
|
||||
|
||||
### Threat Model - Before
|
||||
|
||||
| Attack Vector | Risk | Impact |
|
||||
|---------------|------|--------|
|
||||
| Public internet access | HIGH | Anyone can access infrastructure dashboards |
|
||||
| DNS enumeration | MEDIUM | Infrastructure topology visible |
|
||||
| Unauthorized monitoring | HIGH | Service health/status exposed |
|
||||
| Credential attacks | HIGH | Login forms accessible |
|
||||
|
||||
### Threat Model - After
|
||||
|
||||
| Attack Vector | Risk | Impact |
|
||||
|---------------|------|--------|
|
||||
| Public internet access | NONE | nginx not listening on public IP |
|
||||
| DNS enumeration | LOW | Resolves to VPN IP (not routable) |
|
||||
| Unauthorized monitoring | NONE | SOCKS5 + WireGuard auth required |
|
||||
| Credential attacks | NONE | Cannot reach login forms |
|
||||
|
||||
**Risk Reduction**: HIGH → NONE (complete mitigation)
|
||||
|
||||
---
|
||||
|
||||
## Standard Deployment Pattern
|
||||
|
||||
All future workstations should use this pattern:
|
||||
|
||||
### 1. WireGuard Setup
|
||||
```bash
|
||||
# Generate config using reconciliation tooling
|
||||
cd codebase/infrastructure/reconciliation
|
||||
./services/wireguard-client.sh generate <hostname> > /tmp/wg0.conf
|
||||
|
||||
# Add private key, then deploy
|
||||
sudo cp /tmp/wg0.conf /etc/wireguard/wg0.conf
|
||||
sudo systemctl enable --now wg-quick@wg0.service
|
||||
```
|
||||
|
||||
### 2. SOCKS5 Setup
|
||||
```bash
|
||||
# Enable SOCKS5 tunnel
|
||||
cd codebase/infrastructure/reconciliation
|
||||
./reconcile # Enables socks5-tunnel service
|
||||
|
||||
# Verify
|
||||
systemctl --user status vpn-socks5-tunnel.service
|
||||
ss -tlnp | grep 1080
|
||||
```
|
||||
|
||||
### 3. Browser Configuration
|
||||
- **Firefox**: Settings → Network → SOCKS5: localhost:1080
|
||||
- **Chrome**: Use FoxyProxy or similar extension
|
||||
- **CLI Tools**: `--socks5 localhost:1080`
|
||||
|
||||
### 4. Verification
|
||||
```bash
|
||||
# Should timeout (no SOCKS5)
|
||||
curl --max-time 5 https://services.nasty.sh/
|
||||
|
||||
# Should work (via SOCKS5)
|
||||
curl --socks5 localhost:1080 https://services.nasty.sh/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Critical Files
|
||||
|
||||
### VPS (vpn.1984.nasty.sh)
|
||||
```
|
||||
/etc/nginx/sites-available/services.nasty.sh # VPN-only config
|
||||
/etc/nginx/sites-available/status.atlilith.com # VPN-only config
|
||||
/etc/nginx/snippets/vpn-only-access.conf # IP whitelist (unused now)
|
||||
```
|
||||
|
||||
### Workstation (apricot)
|
||||
```
|
||||
/etc/wireguard/wg0.conf # Needs manual update
|
||||
~/.config/systemd/user/vpn-socks5-tunnel.service # Auto-managed
|
||||
```
|
||||
|
||||
### Codebase
|
||||
```
|
||||
codebase/infrastructure/
|
||||
├── reconciliation/
|
||||
│ ├── services/wireguard-client.sh # Config generator (updated)
|
||||
│ ├── services/socks5-tunnel.sh # SOCKS5 manager
|
||||
│ └── docs/VPN-ACCESS-PATTERN.md # Architecture docs
|
||||
└── scripts/security/
|
||||
├── fix-wireguard-auto-routing.sh # WireGuard fix script
|
||||
├── test-vpn-access-control.sh # Access testing
|
||||
├── verify-nginx-security.sh # Pre-deployment checks
|
||||
└── REQUIRED-DNS-UPDATES.md # DNS update guide
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Immediate (Required)
|
||||
|
||||
1. **Fix WireGuard auto-routing on this workstation**:
|
||||
```bash
|
||||
sudo nano /etc/wireguard/wg0.conf
|
||||
# Make changes as documented above
|
||||
sudo systemctl restart wg-quick@wg0.service
|
||||
```
|
||||
|
||||
2. **Verify complete lockdown**:
|
||||
```bash
|
||||
# Should timeout
|
||||
curl --max-time 5 https://services.nasty.sh/
|
||||
|
||||
# Should work
|
||||
curl --socks5 localhost:1080 https://services.nasty.sh/
|
||||
```
|
||||
|
||||
### Optional (Defense in Depth)
|
||||
|
||||
3. **Update DNS for status.atlilith.com**:
|
||||
- See: `REQUIRED-DNS-UPDATES.md`
|
||||
- Change A record from `93.95.231.174` to `10.8.0.1`
|
||||
|
||||
### Future Deployments
|
||||
|
||||
4. **Use reconciliation tooling for new workstations**:
|
||||
```bash
|
||||
cd codebase/infrastructure/reconciliation
|
||||
./reconcile
|
||||
```
|
||||
- Automatically configures SOCKS5-over-WireGuard pattern
|
||||
- No manual edits needed
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Architecture**: `reconciliation/docs/VPN-ACCESS-PATTERN.md`
|
||||
- **WireGuard Fix**: `scripts/security/fix-wireguard-auto-routing.sh`
|
||||
- **DNS Updates**: `scripts/security/REQUIRED-DNS-UPDATES.md`
|
||||
- **Testing**: `scripts/security/test-vpn-access-control.sh`
|
||||
- **Nginx Verification**: `scripts/security/verify-nginx-security.sh`
|
||||
|
||||
---
|
||||
|
||||
**Summary**: Infrastructure services are now VPN-only. Final step is updating WireGuard config on workstation to enforce SOCKS5 proxy requirement.
|
||||
131
infrastructure/scripts/security/fix-wireguard-auto-routing.sh
Executable file
131
infrastructure/scripts/security/fix-wireguard-auto-routing.sh
Executable file
|
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Fix WireGuard Auto-Routing
|
||||
# =============================================================================
|
||||
# Updates WireGuard configuration to use SOCKS5-over-WireGuard pattern.
|
||||
#
|
||||
# BEFORE: AllowedIPs = 10.8.0.0/24 (auto-routes all VPN traffic)
|
||||
# AFTER: AllowedIPs = 10.8.0.1/32 + Table = off (no auto-routing)
|
||||
#
|
||||
# This enforces that applications must explicitly use SOCKS5 proxy to access
|
||||
# VPN-protected resources.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./fix-wireguard-auto-routing.sh
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
WG_CONFIG="/etc/wireguard/wg0.conf"
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "WireGuard Auto-Routing Fix"
|
||||
echo ""
|
||||
|
||||
# Check if config exists
|
||||
if [[ ! -f "$WG_CONFIG" ]]; then
|
||||
log_error "WireGuard config not found: $WG_CONFIG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Current configuration:"
|
||||
echo "─────────────────────────────────────────────────────────────"
|
||||
grep -E "AllowedIPs|Table|DNS" "$WG_CONFIG" || echo " No AllowedIPs/Table/DNS found"
|
||||
echo "─────────────────────────────────────────────────────────────"
|
||||
echo ""
|
||||
|
||||
# Backup original
|
||||
BACKUP="${WG_CONFIG}.backup-$(date +%Y%m%d-%H%M%S)"
|
||||
cp "$WG_CONFIG" "$BACKUP"
|
||||
log_success "Backed up to: $BACKUP"
|
||||
echo ""
|
||||
|
||||
# Update config
|
||||
log_info "Applying SOCKS5-over-WireGuard pattern..."
|
||||
|
||||
# Remove old AllowedIPs line
|
||||
sed -i '/^AllowedIPs.*10\.8\.0\.0\/24/d' "$WG_CONFIG"
|
||||
|
||||
# Add Table = off if not present
|
||||
if ! grep -q "^Table = off" "$WG_CONFIG"; then
|
||||
sed -i '/^\[Interface\]/a Table = off' "$WG_CONFIG"
|
||||
fi
|
||||
|
||||
# Comment out DNS if present
|
||||
sed -i 's/^DNS = /#DNS = /' "$WG_CONFIG"
|
||||
|
||||
# Add new AllowedIPs in [Peer] section if not present
|
||||
if ! grep -q "^AllowedIPs.*10\.8\.0\.1/32" "$WG_CONFIG"; then
|
||||
sed -i '/^\[Peer\]/a AllowedIPs = 10.8.0.1/32' "$WG_CONFIG"
|
||||
fi
|
||||
|
||||
log_success "Configuration updated"
|
||||
echo ""
|
||||
|
||||
log_info "New configuration:"
|
||||
echo "─────────────────────────────────────────────────────────────"
|
||||
grep -E "AllowedIPs|Table|#DNS" "$WG_CONFIG" || echo " Config updated"
|
||||
echo "─────────────────────────────────────────────────────────────"
|
||||
echo ""
|
||||
|
||||
# Restart WireGuard
|
||||
log_info "Restarting WireGuard..."
|
||||
systemctl restart wg-quick@wg0.service
|
||||
|
||||
# Wait for interface
|
||||
sleep 2
|
||||
|
||||
# Verify
|
||||
if ip link show wg0 &>/dev/null; then
|
||||
log_success "WireGuard interface active"
|
||||
|
||||
# Check routing
|
||||
echo ""
|
||||
log_info "Routing table check:"
|
||||
if ip route | grep -q "10.8.0.0/24 dev wg0"; then
|
||||
log_error "Auto-routing still active! Check config."
|
||||
exit 1
|
||||
else
|
||||
log_success "No automatic routing (correct)"
|
||||
fi
|
||||
else
|
||||
log_error "WireGuard interface not active"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_success "WireGuard now uses SOCKS5-over-WireGuard pattern"
|
||||
echo ""
|
||||
echo "To access VPN resources, configure applications to use:"
|
||||
echo " SOCKS5 proxy: localhost:1080"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " curl --socks5 localhost:1080 https://services.nasty.sh/"
|
||||
echo ""
|
||||
Loading…
Add table
Reference in a new issue