A self-hosted VPN server implementation with TUN interface support, built in Go. This project demonstrates VPN concepts including packet encapsulation, encryption, routing tables, and network interface management.
- TUN Interface: Full TUN/TAP support for creating virtual network interfaces
- Encryption: AES-256-GCM encryption for all VPN traffic
- Routing Management: Automatic routing table configuration (full tunnel or split tunnel)
- Docker Support: Ready-to-use Docker containers for easy deployment
- Cloud Ready: Automated deployment scripts for AWS and Azure
- Cross-Platform: Works on Linux and macOS
- Educational: Learn about VPN protocols, routing, and network programming
┌─────────────┐ UDP (Encrypted) ┌─────────────┐
│ Client │◄───────────────────────────────►│ Server │
│ │ │ │
│ ┌────────┐│ │ ┌────────┐│
│ │ TUN ││ │ │ TUN ││
│ │Interface│ │ │Interface│
│ └────────┘│ │ └────────┘│
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ Routing │ │ Routing │
│ Tables │ │ Tables │
└─────────────┘ └─────────────┘
- TUN Interface: Virtual network interface that captures IP packets
- Protocol Layer: Packet encapsulation/decapsulation with session management
- Crypto Layer: AES-256-GCM encryption for secure communication
- Routing Manager: Handles routing table operations
- Server: Listens on UDP, manages client sessions, forwards packets
- Client: Connects to server, routes traffic through VPN
- Go 1.21 or later
- Linux or macOS (for TUN support)
- Root/Administrator privileges (for TUN interface creation)
- Docker and Docker Compose (for containerized deployment)
Deploy your VPN server in the cloud in minutes:
AWS:
# Upload project to EC2
scp -r omail/ ubuntu@YOUR_EC2_IP:~/
# SSH and deploy
ssh ubuntu@YOUR_EC2_IP
cd omail && sudo bash deploy/aws-deploy.shAzure:
# Upload project to VM
scp -r omail/ azureuser@YOUR_AZURE_IP:~/
# SSH and deploy
ssh azureuser@YOUR_AZURE_IP
cd omail && sudo bash deploy/azure-deploy.shSee CLOUD-SETUP.md for detailed instructions.
- Install dependencies:
go mod download- Build the binaries:
make build
# or
go build -o bin/omail-server ./cmd/server
go build -o bin/omail-client ./cmd/client- Run the server (requires root):
sudo ./bin/omail-server -address :51820 -password your-secure-password- Run the client (requires root):
sudo ./bin/omail-client -server localhost:51820 -password your-secure-password- Set environment variables:
export VPN_PASSWORD=your-secure-password- Start the server:
docker-compose up -d vpn-server- View logs:
docker-compose logs -f vpn-server-address string
Server listen address (default ":51820")
-password string
Encryption password (required)
-tun string
TUN interface name (default "omail0")
-tun-ip string
TUN interface IP address (default "10.0.0.1")
-tun-netmask string
TUN interface netmask (default "255.255.255.0")
-mtu int
MTU size (default 1500)
-server string
Server address (e.g., server.com:51820) (required)
-password string
Encryption password (required)
-tun string
TUN interface name (default "omail0")
-tun-ip string
TUN interface IP address (default "10.0.0.2")
-tun-netmask string
TUN interface netmask (default "255.255.255.0")
-mtu int
MTU size (default 1500)
-split-tunnel string
Comma-separated list of CIDR networks for split tunneling
(empty for full tunnel)
Only route specific networks through VPN:
sudo ./bin/omail-client \
-server vpn.example.com:51820 \
-password mypassword \
-split-tunnel "10.0.0.0/8,192.168.1.0/24"Routing tables tell the operating system how to route network packets. When you send a packet, the OS checks the routing table to determine:
- Which network interface to use
- What gateway (next hop) to send the packet to
- What route has the highest priority (metric)
Linux:
ip route show
# or
route -nmacOS:
netstat -rn
# or
route -n get default-
Full Tunnel: All traffic goes through VPN
0.0.0.0/0 → dev omail0This routes ALL internet traffic through the VPN.
-
Split Tunnel: Only specific networks go through VPN
10.0.0.0/8 → dev omail0 192.168.1.0/24 → dev omail0Only traffic to these networks goes through VPN.
-
Default Route: Normal internet traffic
0.0.0.0/0 → dev eth0 via 192.168.1.1This is your normal internet connection.
Our VPN client automatically manages routing:
- Add Route: Adds routes through the TUN interface
- Delete Route: Removes routes when disconnecting
- List Routes: Shows current routing configuration
The TUN (Tunnel) interface is a virtual network interface that operates at Layer 3 (IP layer). When created:
tun, err := tun.New("omail0", 1500)
tun.SetIP(net.ParseIP("10.0.0.1"), net.CIDRMask(24, 32))
tun.Up()This creates a virtual network interface that can capture and inject IP packets.
Client → Server:
- Application sends IP packet
- OS routes packet to TUN interface (based on routing table)
- VPN client reads packet from TUN
- Packet is encrypted and encapsulated
- Encrypted packet sent via UDP to server
- Server decrypts and writes to server's TUN interface
- Server's OS routes packet to destination
Server → Client:
- Server receives IP packet on its TUN interface
- Packet is encrypted and sent to client
- Client decrypts and writes to client's TUN interface
- Client's OS routes packet to application
All packets are encrypted using AES-256-GCM:
- Key Derivation: PBKDF2 with SHA-256 (4096 iterations)
- Encryption: AES-256-GCM with random nonce
- Authentication: GCM provides authentication
Each packet has a header:
+--------+--------+--------+--------+
| Type | Reserved| Length | SessionID |
+--------+--------+--------+--------+
| Data |
+-----------------------------------+
- Type: Data packet or keep-alive
- Length: Payload length
- SessionID: Client session identifier
- Stronger Key Exchange: Implement ECDH or similar for key exchange
- Certificate-based Auth: Use TLS certificates instead of passwords
- Perfect Forward Secrecy: Rotate keys periodically
- Rate Limiting: Prevent DoS attacks
- Connection Authentication: Verify client identity
- Audit Logging: Log security events
Error: operation not permitted
Solution: Run with root privileges:
sudo ./bin/omail-server ...Error: SIOCSIFADDR: operation not permitted
Solution: Ensure you have NET_ADMIN capability (Docker) or root access.
Check routing table:
ip route showManually add route (if needed):
sudo ip route add 10.0.0.0/8 dev omail0Error: open /dev/net/tun: no such file or directory
Solution: Ensure Docker has TUN device access:
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMINomail/
├── cmd/
│ ├── server/ # Server entry point
│ └── client/ # Client entry point
├── internal/
│ ├── crypto/ # Encryption layer
│ ├── protocol/ # Packet protocol
│ ├── routing/ # Routing management
│ ├── server/ # Server implementation
│ ├── client/ # Client implementation
│ └── tun/ # TUN interface wrapper
├── docker/
│ ├── Dockerfile.server
│ └── Dockerfile.client
├── docker-compose.yml
├── Makefile
└── README.md
go test ./...make fmt
# or
go fmt ./...- TUN/TAP: Virtual network interfaces
- Packet Encapsulation: Wrapping IP packets in another protocol
- Routing Tables: How OS routes network traffic
- NAT Traversal: Getting through firewalls
- UDP Socket Programming: Connectionless communication
- Raw Sockets: Low-level packet access
- Network Namespaces: Isolated network environments
- Symmetric Encryption: AES-GCM
- Key Derivation: PBKDF2
- Nonce Management: Random nonces for GCM
MIT License - feel free to use this for learning and experimentation!
Contributions welcome! This is an educational project, so focus on:
- Code clarity and documentation
- Educational examples
- Security improvements
- Cross-platform support
Inspired by WireGuard, OpenVPN, and other VPN implementations. Built for educational purposes to understand VPN internals.