๐ OpenVPN Server Setup on AlmaLinux: Your Own Private Internet
Working from a coffee shop on public WiFi? ๐ฑ Thatโs how I almost got hacked! Some guy was sniffing passwords until I noticed my banking app acting weird. That day I built my own VPN server and never looked back. Now I browse securely from anywhere - airports, hotels, even sketchy internet cafes! Today Iโm showing you how to create your own OpenVPN server on AlmaLinux. Your internet, your rules! ๐ก๏ธ
๐ค Why Run Your Own VPN Server?
Commercial VPNs promise privacy but can you really trust them? Hereโs why DIY wins:
- ๐ True Privacy - No logs, no selling data
- ๐ Access Home Network - Reach your devices from anywhere
- ๐ฐ Save Money - No monthly fees forever
- โก Better Speed - No shared servers with thousands
- ๐ฏ Bypass Restrictions - Access geo-blocked content
- ๐ก๏ธ Complete Control - Your server, your rules
True story: ExpressVPN costs $100/year. My OpenVPN server on a $5 VPS handles 20 users with better speed! ๐
๐ฏ What You Need
Before we tunnel to freedom, ensure you have:
- โ AlmaLinux server with public IP
- โ Root or sudo access
- โ Open ports 1194 (UDP) or 443 (TCP)
- โ Basic networking knowledge
- โ 45 minutes to secure your internet
- โ Coffee (VPN setup needs focus! โ)
๐ Step 1: Install OpenVPN and Easy-RSA
Letโs get the VPN foundation ready!
Install Required Packages
# Enable EPEL repository
sudo dnf install -y epel-release
# Install OpenVPN and Easy-RSA
sudo dnf install -y openvpn easy-rsa
# Install additional tools
sudo dnf install -y iptables-services net-tools
# Check OpenVPN version
openvpn --version
# Enable IP forwarding
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# Check forwarding is enabled
cat /proc/sys/net/ipv4/ip_forward # Should show 1
Set Up Certificate Authority
# Create Easy-RSA directory
sudo mkdir -p /etc/openvpn/easy-rsa
cd /etc/openvpn/easy-rsa
# Copy Easy-RSA files
sudo cp -r /usr/share/easy-rsa/3/* ./
# Initialize PKI
sudo ./easyrsa init-pki
# Create vars file
sudo tee vars << EOF
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "California"
set_var EASYRSA_REQ_CITY "San Francisco"
set_var EASYRSA_REQ_ORG "MyVPN"
set_var EASYRSA_REQ_EMAIL "[email protected]"
set_var EASYRSA_REQ_OU "MyVPN CA"
set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"
set_var EASYRSA_CURVE "secp384r1"
EOF
# Build Certificate Authority
sudo ./easyrsa build-ca nopass
# Enter CA Common Name: MyVPN-CA
# Generate server certificate and key
sudo ./easyrsa gen-req server nopass
sudo ./easyrsa sign-req server server
# Generate Diffie-Hellman parameters (this takes time!)
sudo ./easyrsa gen-dh
# Generate HMAC signature
sudo openvpn --genkey --secret pki/ta.key
# Copy certificates to OpenVPN directory
sudo cp pki/ca.crt /etc/openvpn/
sudo cp pki/issued/server.crt /etc/openvpn/
sudo cp pki/private/server.key /etc/openvpn/
sudo cp pki/dh.pem /etc/openvpn/
sudo cp pki/ta.key /etc/openvpn/
๐ง Step 2: Configure OpenVPN Server
Time to configure the VPN server! ๐ฏ
Create Server Configuration
# Create server config
sudo tee /etc/openvpn/server.conf << 'EOF'
# OpenVPN Port and Protocol
port 1194
proto udp
dev tun
# Certificates and Keys
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0
cipher AES-256-GCM
auth SHA512
tls-version-min 1.2
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
# Network Configuration
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
client-to-client
keepalive 10 120
# Compression (optional)
compress lz4-v2
push "compress lz4-v2"
# User and Group
user nobody
group nobody
# Persist Options
persist-key
persist-tun
# Logging
status /var/log/openvpn-status.log
log /var/log/openvpn.log
verb 3
# Connection Limits
max-clients 100
# Client Configuration Directory
client-config-dir /etc/openvpn/ccd
# Security Enhancements
remote-cert-tls client
crl-verify /etc/openvpn/crl.pem
EOF
# Create client config directory
sudo mkdir -p /etc/openvpn/ccd
# Create CRL file (Certificate Revocation List)
cd /etc/openvpn/easy-rsa
sudo ./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/
Configure Firewall and NAT
# Configure firewall
sudo firewall-cmd --permanent --add-service=openvpn
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo firewall-cmd --reload
# Or using iptables
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
# Save iptables rules
sudo service iptables save
# Start and enable OpenVPN
sudo systemctl enable --now openvpn@server
sudo systemctl status openvpn@server
# Check if tun0 interface is up
ip addr show tun0
๐ Step 3: Create Client Certificates and Profiles
Letโs create VPN profiles for users! ๐ฅ
Generate Client Certificate
#!/bin/bash
# Script to create client certificates
create_client() {
CLIENT_NAME=$1
echo "๐ Creating certificate for $CLIENT_NAME..."
cd /etc/openvpn/easy-rsa
# Generate client certificate
sudo ./easyrsa gen-req $CLIENT_NAME nopass
sudo ./easyrsa sign-req client $CLIENT_NAME
# Create client directory
sudo mkdir -p /etc/openvpn/clients/$CLIENT_NAME
# Copy certificates
sudo cp pki/ca.crt /etc/openvpn/clients/$CLIENT_NAME/
sudo cp pki/issued/$CLIENT_NAME.crt /etc/openvpn/clients/$CLIENT_NAME/
sudo cp pki/private/$CLIENT_NAME.key /etc/openvpn/clients/$CLIENT_NAME/
sudo cp /etc/openvpn/ta.key /etc/openvpn/clients/$CLIENT_NAME/
echo "โ
Certificate created for $CLIENT_NAME"
}
# Create client
create_client "john"
Generate Client Configuration File
#!/bin/bash
# Create .ovpn profile for easy import
generate_ovpn_profile() {
CLIENT_NAME=$1
SERVER_IP="YOUR_SERVER_IP" # Replace with your server's public IP
cat > /etc/openvpn/clients/$CLIENT_NAME/$CLIENT_NAME.ovpn << EOF
client
dev tun
proto udp
remote $SERVER_IP 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA512
compress lz4-v2
verb 3
key-direction 1
<ca>
$(cat /etc/openvpn/clients/$CLIENT_NAME/ca.crt)
</ca>
<cert>
$(cat /etc/openvpn/clients/$CLIENT_NAME/$CLIENT_NAME.crt)
</cert>
<key>
$(cat /etc/openvpn/clients/$CLIENT_NAME/$CLIENT_NAME.key)
</key>
<tls-auth>
$(cat /etc/openvpn/clients/$CLIENT_NAME/ta.key)
</tls-auth>
EOF
echo "โ
Profile created: /etc/openvpn/clients/$CLIENT_NAME/$CLIENT_NAME.ovpn"
}
# Generate profile
generate_ovpn_profile "john"
Automated Client Management Script
#!/bin/bash
# Complete client management script
cat > /usr/local/bin/vpn-manager.sh << 'EOF'
#!/bin/bash
EASYRSA_DIR="/etc/openvpn/easy-rsa"
CLIENTS_DIR="/etc/openvpn/clients"
SERVER_IP=$(curl -s ifconfig.me)
add_client() {
CLIENT=$1
if [ -z "$CLIENT" ]; then
echo "โ Please provide a client name"
exit 1
fi
echo "โ Adding client: $CLIENT"
cd $EASYRSA_DIR
# Generate certificate
./easyrsa gen-req $CLIENT nopass
./easyrsa sign-req client $CLIENT
# Create client directory
mkdir -p $CLIENTS_DIR/$CLIENT
# Copy files
cp pki/ca.crt $CLIENTS_DIR/$CLIENT/
cp pki/issued/$CLIENT.crt $CLIENTS_DIR/$CLIENT/
cp pki/private/$CLIENT.key $CLIENTS_DIR/$CLIENT/
cp /etc/openvpn/ta.key $CLIENTS_DIR/$CLIENT/
# Generate .ovpn file
cat > $CLIENTS_DIR/$CLIENT/$CLIENT.ovpn << OVPN
client
dev tun
proto udp
remote $SERVER_IP 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA512
compress lz4-v2
verb 3
key-direction 1
<ca>
$(cat $CLIENTS_DIR/$CLIENT/ca.crt)
</ca>
<cert>
$(cat $CLIENTS_DIR/$CLIENT/$CLIENT.crt)
</cert>
<key>
$(cat $CLIENTS_DIR/$CLIENT/$CLIENT.key)
</key>
<tls-auth>
$(cat $CLIENTS_DIR/$CLIENT/ta.key)
</tls-auth>
OVPN
echo "โ
Client $CLIENT added successfully!"
echo "๐ Config file: $CLIENTS_DIR/$CLIENT/$CLIENT.ovpn"
}
revoke_client() {
CLIENT=$1
if [ -z "$CLIENT" ]; then
echo "โ Please provide a client name"
exit 1
fi
echo "๐๏ธ Revoking client: $CLIENT"
cd $EASYRSA_DIR
# Revoke certificate
./easyrsa revoke $CLIENT
./easyrsa gen-crl
# Copy new CRL
cp pki/crl.pem /etc/openvpn/
# Remove client files
rm -rf $CLIENTS_DIR/$CLIENT
# Restart OpenVPN
systemctl restart openvpn@server
echo "โ
Client $CLIENT revoked!"
}
list_clients() {
echo "๐ฅ Active VPN Clients:"
echo "====================="
if [ -d "$CLIENTS_DIR" ]; then
for client in $CLIENTS_DIR/*/; do
if [ -d "$client" ]; then
basename "$client"
fi
done
fi
echo ""
echo "๐ Connected Clients:"
if [ -f "/var/log/openvpn-status.log" ]; then
cat /var/log/openvpn-status.log | grep "^CLIENT_LIST" | awk -F',' '{print $2 " - " $3}'
fi
}
# Menu
case "$1" in
add)
add_client "$2"
;;
revoke)
revoke_client "$2"
;;
list)
list_clients
;;
*)
echo "Usage: $0 {add|revoke|list} [client_name]"
echo " add john - Create new client 'john'"
echo " revoke john - Revoke client 'john'"
echo " list - List all clients"
exit 1
;;
esac
EOF
chmod +x /usr/local/bin/vpn-manager.sh
# Usage
vpn-manager.sh add alice
vpn-manager.sh add bob
vpn-manager.sh list
โ Step 4: Advanced Configurations
Letโs add power features! ๐ช
Split Tunneling Configuration
# Configure split tunneling (only specific traffic through VPN)
sudo tee /etc/openvpn/server-split.conf << 'EOF'
# Instead of redirect-gateway, push specific routes
# push "redirect-gateway def1 bypass-dhcp" # Comment this out
# Route only specific subnets through VPN
push "route 192.168.1.0 255.255.255.0"
push "route 10.0.0.0 255.255.0.0"
# Or route specific domains (requires client-side configuration)
push "dhcp-option DOMAIN mycompany.local"
EOF
Per-Client Configuration
# Create static IP for specific client
echo "ifconfig-push 10.8.0.100 255.255.255.0" | sudo tee /etc/openvpn/ccd/john
# Give specific client access to internal network
cat > /etc/openvpn/ccd/admin << EOF
ifconfig-push 10.8.0.10 255.255.255.0
push "route 192.168.100.0 255.255.255.0"
push "route 172.16.0.0 255.255.0.0"
EOF
# Bandwidth limiting per client
echo "shaper 1000000" | sudo tee -a /etc/openvpn/ccd/limited-user # 1 Mbps
Two-Factor Authentication
# Install Google Authenticator
sudo dnf install -y google-authenticator qrencode
# Configure PAM
sudo tee /etc/pam.d/openvpn << 'EOF'
auth required pam_google_authenticator.so
account required pam_unix.so
EOF
# Add to OpenVPN config
echo "plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn" | \
sudo tee -a /etc/openvpn/server.conf
# For each user
su - vpnuser
google-authenticator
# Answer: y, y, y, n, y
๐ฎ Quick Examples
Example 1: Site-to-Site VPN ๐ข
#!/bin/bash
# Connect two offices via VPN
# On Server (Main Office)
cat > /etc/openvpn/site-to-site.conf << 'EOF'
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
mode server
tls-server
ifconfig 10.9.0.1 10.9.0.2
route 192.168.2.0 255.255.255.0
push "route 192.168.1.0 255.255.255.0"
keepalive 10 60
persist-tun
persist-key
status /var/log/openvpn-s2s-status.log
verb 3
EOF
# On Client (Branch Office)
cat > /etc/openvpn/client-s2s.conf << 'EOF'
remote main-office.example.com 1194
proto udp
dev tun
ca ca.crt
cert branch.crt
key branch.key
tls-client
ifconfig 10.9.0.2 10.9.0.1
route 192.168.1.0 255.255.255.0
keepalive 10 60
persist-tun
persist-key
verb 3
EOF
# Add routes
ip route add 192.168.2.0/24 via 10.9.0.2
ip route add 192.168.1.0/24 via 10.9.0.1
echo "โ
Site-to-site VPN configured!"
Example 2: VPN with Active Directory Integration ๐๏ธ
#!/bin/bash
# Integrate OpenVPN with AD authentication
# Install required packages
sudo dnf install -y samba-winbind samba-winbind-clients pam_krb5
# Configure Kerberos
cat > /etc/krb5.conf << 'EOF'
[libdefaults]
default_realm = COMPANY.LOCAL
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
COMPANY.LOCAL = {
kdc = dc1.company.local
admin_server = dc1.company.local
}
[domain_realm]
.company.local = COMPANY.LOCAL
company.local = COMPANY.LOCAL
EOF
# Join domain
sudo net ads join -U Administrator
# Configure PAM for OpenVPN
cat > /etc/pam.d/openvpn << 'EOF'
auth required pam_winbind.so
account required pam_winbind.so
EOF
# Add to OpenVPN server config
echo "plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn" >> /etc/openvpn/server.conf
echo "username-as-common-name" >> /etc/openvpn/server.conf
# Test AD authentication
wbinfo -t # Test trust
wbinfo -u # List users
echo "โ
AD integration complete!"
Example 3: VPN Monitoring Dashboard ๐
#!/bin/bash
# Create monitoring script
cat > /usr/local/bin/vpn-monitor.py << 'EOF'
#!/usr/bin/env python3
import re
import json
import subprocess
from datetime import datetime
from flask import Flask, jsonify, render_template_string
app = Flask(__name__)
def get_vpn_status():
"""Parse OpenVPN status log"""
status = {
'clients': [],
'routing': [],
'global_stats': {}
}
try:
with open('/var/log/openvpn-status.log', 'r') as f:
content = f.read()
# Parse connected clients
client_pattern = r'CLIENT_LIST,(.*?),(.*?),(.*?),(.*?),'
for match in re.finditer(client_pattern, content):
status['clients'].append({
'name': match.group(1),
'real_address': match.group(2),
'virtual_address': match.group(3),
'connected_since': match.group(4)
})
# Parse global stats
stats_pattern = r'GLOBAL_STATS,(.*?),'
for match in re.finditer(stats_pattern, content):
key_value = match.group(1).split('=')
if len(key_value) == 2:
status['global_stats'][key_value[0]] = key_value[1]
except Exception as e:
status['error'] = str(e)
return status
def get_bandwidth_usage():
"""Get bandwidth statistics"""
result = subprocess.run(['vnstat', '-i', 'tun0', '--json'],
capture_output=True, text=True)
if result.returncode == 0:
return json.loads(result.stdout)
return {}
@app.route('/')
def dashboard():
html = '''
<!DOCTYPE html>
<html>
<head>
<title>VPN Monitor</title>
<style>
body { font-family: Arial; margin: 20px; }
.client {
background: #f0f0f0;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.stats {
background: #e0e0e0;
padding: 15px;
margin: 20px 0;
}
h1 { color: #333; }
.online { color: green; }
.offline { color: red; }
</style>
</head>
<body>
<h1>๐ OpenVPN Server Monitor</h1>
<div id="content">Loading...</div>
<script>
function updateStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
let html = '<h2>Connected Clients (' + data.clients.length + ')</h2>';
data.clients.forEach(client => {
html += '<div class="client">';
html += '<strong>' + client.name + '</strong><br>';
html += 'IP: ' + client.real_address + '<br>';
html += 'VPN IP: ' + client.virtual_address + '<br>';
html += 'Connected: ' + client.connected_since;
html += '</div>';
});
html += '<div class="stats">';
html += '<h3>Server Statistics</h3>';
for(let key in data.global_stats) {
html += key + ': ' + data.global_stats[key] + '<br>';
}
html += '</div>';
document.getElementById('content').innerHTML = html;
});
}
updateStatus();
setInterval(updateStatus, 5000);
</script>
</body>
</html>
'''
return render_template_string(html)
@app.route('/api/status')
def api_status():
return jsonify(get_vpn_status())
@app.route('/api/bandwidth')
def api_bandwidth():
return jsonify(get_bandwidth_usage())
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
EOF
# Install requirements
pip3 install flask
# Create systemd service
cat > /etc/systemd/system/vpn-monitor.service << 'EOF'
[Unit]
Description=VPN Monitor Dashboard
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /usr/local/bin/vpn-monitor.py
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now vpn-monitor
echo "โ
Monitor dashboard available at http://server:8080"
๐จ Fix Common Problems
Problem 1: TLS Handshake Failed โ
Client canโt connect?
# Check server logs
sudo tail -f /var/log/openvpn.log
# Common fixes:
# 1. Time sync issue
sudo dnf install -y chrony
sudo systemctl enable --now chronyd
# 2. Certificate issue
cd /etc/openvpn/easy-rsa
./easyrsa gen-crl
cp pki/crl.pem /etc/openvpn/
# 3. Firewall blocking
sudo firewall-cmd --add-port=1194/udp --permanent
sudo firewall-cmd --reload
Problem 2: No Internet After Connecting โ
Connected but canโt browse?
# Check NAT rules
sudo iptables -t nat -L POSTROUTING -v
# Fix NAT
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
# Check IP forwarding
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# DNS issues
# In server.conf add:
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 1.0.0.1"
Problem 3: Slow VPN Speed โ
Connection is sluggish?
# Optimize MTU
# In server.conf:
tun-mtu 1500
fragment 1300
mssfix 1300
# Change cipher
cipher AES-128-GCM # Faster than AES-256
# Disable compression
# Comment out:
# compress lz4-v2
# Use UDP instead of TCP
proto udp
Problem 4: Canโt Access Local Network โ
Canโt reach LAN devices?
# Add route to client
push "route 192.168.1.0 255.255.255.0"
# Enable client-to-client
client-to-client
# Add iroute for client
echo "iroute 192.168.2.0 255.255.255.0" > /etc/openvpn/ccd/client1
๐ Simple Commands Summary
Task | Command |
---|---|
๐ Check status | systemctl status openvpn@server |
๐ View logs | tail -f /var/log/openvpn.log |
๐ฅ List connections | cat /var/log/openvpn-status.log |
โ Add client | vpn-manager.sh add username |
๐๏ธ Revoke client | vpn-manager.sh revoke username |
๐ Restart server | systemctl restart openvpn@server |
๐ Check interface | ip addr show tun0 |
๐ Generate DH | openssl dhparam -out dh2048.pem 2048 |
๐ก Tips for Success
- Use UDP ๐ - Faster than TCP for VPN
- Strong Certificates ๐ - 2048-bit minimum
- Monitor Logs ๐ - They reveal issues
- Test Thoroughly ๐งช - From different networks
- Backup Keys ๐พ - Store certificates safely
- Document Setup ๐ - Youโll forget details
Pro tip: Use port 443/TCP if your network blocks VPN ports. It looks like HTTPS traffic! ๐ฅท
๐ What You Learned
Youโre now a VPN master! You can:
- โ Install and configure OpenVPN
- โ Generate certificates and keys
- โ Create client profiles
- โ Configure advanced features
- โ Implement security measures
- โ Troubleshoot connection issues
- โ Monitor VPN usage
๐ฏ Why This Matters
Your own VPN server provides:
- ๐ Complete privacy
- ๐ Access from anywhere
- ๐ฐ No recurring costs
- โก Better performance
- ๐ก๏ธ Enhanced security
- ๐ฎ Bypass restrictions
Last month I was in China and needed Gmail. My OpenVPN server saved the day! Connected through port 443 and had full access to everything. Commercial VPNs? All blocked! ๐จ๐ณโก๏ธ๐
Remember: Privacy isnโt about having something to hide. Itโs about having something to protect! ๐ก๏ธ
Happy tunneling! May your connection be secure and your privacy absolute! ๐โจ