Skip to content

Instantly share code, notes, and snippets.

@AndersonIncorp
Last active March 5, 2022 21:27
Show Gist options
  • Save AndersonIncorp/b17877787f33c851feb2dd1ddaea2aeb to your computer and use it in GitHub Desktop.
Save AndersonIncorp/b17877787f33c851feb2dd1ddaea2aeb to your computer and use it in GitHub Desktop.
MIT License; Short guide for setting up openvpn-server service (tcp+udp), full network (redirect-gateway def1); Optional - Second VPS with traffic redirect to first via iptables; Optional - SSH tunneling for DPI prevention (pubkey)
$ pacman -S openvpn easy-rsa
$ cp -a /etc/easy-rsa/. /etc/easy-rsa-clientnet/
$ cd /etc/easy-rsa-clientnet/
$ export EASYRSA=$(pwd)
$ easyrsa init-pki
$ easyrsa build-ca nopass
$ easyrsa gen-req server nopass
$ easyrsa sign-req server server
$ cat pki/index.txt
$ mkdir -p /etc/openvpn/server/{tcp,udp}
$ cp /etc/easy-rsa-clientnet/pki/ca.crt /etc/openvpn/server/tcp/
$ cp /etc/easy-rsa-clientnet/pki/private/server.key /etc/openvpn/server/tcp/
$ cp /etc/easy-rsa-clientnet/pki/issued/server.crt /etc/openvpn/server/tcp/
$ openssl dhparam -out /etc/openvpn/server/tcp/dh.pem 2048
$ openvpn --genkey secret /etc/openvpn/server/tcp/ta.key
$ cp -a /etc/openvpn/server/tcp/. /etc/openvpn/server/udp/
/etc/sysctl.d/30-ipforward.conf
-
net.ipv4.ip_forward=1
/etc/iptables/iptables.rules
-
...
*filter
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -i tun+ -j ACCEPT
-A TCP -p tcp --dport 5060 -j ACCEPT
-A UDP -p udp --dport 5060 -j ACCEPT
COMMIT
*nat
-A POSTROUTING -o tun1 -j MASQUERADE
-A POSTROUTING -o tun2 -j MASQUERADE
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j SNAT --to-source 203.0.113.2
-A POSTROUTING -s 10.9.0.0/24 -o eth0 -j SNAT --to-source 203.0.113.2
COMMIT
/etc/openvpn/server/clientnet-udp.conf
-
sndbuf 0
rcvbuf 0
reneg-sec 0
mute-replay-warnings
management 127.0.0.1 5061
port 5060
proto udp
dev tun1
topology subnet
ca /etc/openvpn/server/udp/ca.crt
cert /etc/openvpn/server/udp/server.crt
key /etc/openvpn/server/udp/server.key
dh /etc/openvpn/server/udp/dh.pem
tls-auth /etc/openvpn/server/udp/ta.key 0
tls-exit
server 10.8.0.0 255.255.0.0 # 65K IP's
keepalive 10 120
persist-key
persist-tun
cipher AES-256-GCM
data-ciphers "AES-256-GCM"
replay-persist /etc/openvpn/server/udp/replay-persist.txt
verb 2
explicit-exit-notify 5
config /etc/openvpn/server/routes.conf
/etc/openvpn/server/clientnet-tcp.conf
-
sndbuf 0
rcvbuf 0
reneg-sec 0
mute-replay-warnings
management 127.0.0.1 5062
port 5060
proto tcp
dev tun2
topology subnet
ca /etc/openvpn/server/tcp/ca.crt
cert /etc/openvpn/server/tcp/server.crt
key /etc/openvpn/server/tcp/server.key
dh /etc/openvpn/server/tcp/dh.pem
tls-auth /etc/openvpn/server/tcp/ta.key 0
tls-exit
server 10.9.0.0 255.255.0.0 # 65K IP's
keepalive 10 120
persist-key
persist-tun
cipher AES-256-GCM
data-ciphers "AES-256-GCM"
replay-persist /etc/openvpn/server/tcp/replay-persist.txt
verb 2
config /etc/openvpn/server/routes.conf
$ chown -R openvpn:network /etc/openvpn/server
/etc/openvpn/server/routes.conf
-
push "block-outside-dns"
push "redirect-gateway def1 bypass-dhcp"
push "route 8.8.8.8 255.255.255.255 vpn_gateway"
push "route 8.8.4.4 255.255.255.255 vpn_gateway"
push "route 208.67.222.222 255.255.255.255 vpn_gateway"
push "route 208.67.220.220 255.255.255.255 vpn_gateway"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
$ systemctl enable openvpn-server@clientnet-udp \
    && systemctl start openvpn-server@clientnet-udp \
    && systemctl status openvpn-server@clientnet-udp
$ systemctl enable openvpn-server@clientnet-tcp \
    && systemctl start openvpn-server@clientnet-tcp \
    && systemctl status openvpn-server@clientnet-tcp
$ mkdir /etc/openvpn/client
/etc/openvpn/client/client-udp.conf
-
client
dev tun
remote fqdn1.example 5060 udp
remote 203.0.113.2 5060 udp
remote fqdn2.example 5060 udp
remote 203.0.113.3 5060 udp
resolv-retry 10
nobind
#user nobody
#group nobody
persist-key
persist-tun
key-direction 1
remote-cert-tls server
verb 2
cipher AES-256-GCM
explicit-exit-notify 5
connect-retry-max 2
tls-exit
push-peer-info
#socks-proxy localhost 9150 socks-proxy-retry
/etc/openvpn/client/client-tcp.conf
-
client
dev tun
remote fqdn1.example 5060 tcp
remote 203.0.113.2 5060 tcp
remote fqdn2.example 5060 tcp
remote 203.0.113.3 5060 tcp
resolv-retry 10
nobind
#user nobody
#group nobody
persist-key
persist-tun
key-direction 1
remote-cert-tls server
verb 2
cipher AES-256-GCM
connect-retry-max 2
tls-exit
push-peer-info
#socks-proxy localhost 9150 socks-proxy-retry
/etc/openvpn/client/generate.php
-
(see generate.php file)
$ cd /etc/openvpn/client
$ php generate.php client1 tcp
$ php generate.php client1 udp

Management

$ telnet localhost 5061 # clientnet-udp
> status
$ telnet localhost 5062 # clientnet-tcp
> status

Optional, setting up forawrd of traffic form second vps (203.0.113.3) to first (203.0.113.2)

/etc/iptables/iptables.rules
...
*filter
:TCP - [0:0]
-A TCP -p tcp --dport 5060 -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p udp -m udp --dport 5060 -j DNAT --to-destination 203.0.113.2:5060
-A PREROUTING -p tcp -m tcp --dport 5060 -j DNAT --to-destination 203.0.113.2:5060
-A POSTROUTING -j MASQUERADE
COMMIT

Optional, setting up SSH tunnel

# on local machine
$ cd somedirtostorekeys
$ ssh-keygen -t ed25519 -f ./id_ed25519 -C "forward"
# on remote
$ useradd -m forward
$ su forward
$ mkdir ~/.ssh && chmod 700 ~/.ssh
$ touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
~/.ssh/authorized_keys
-
restrict,port-forwarding,command="echo 'This account can only be used for forwarding, e.g. run: ssh -D 5060 -N [email protected]'" ssh-ed25519 ...# id_ed25519.pub content

tcp ovpn profile changes

client
...
socks-proxy localhost 5060
socks-proxy-retry
route 203.0.113.2 255.255.255.255 net_gateway
route 203.0.113.3 255.255.255.255 net_gateway

run tunnel

$ cd somedirtostorekeys
$ ssh -i id_ed25519 -D 5060 -N [email protected]
#!/usr/bin/php
<?php
// Generates client certificates and conf for clientnet
// USAGE: generate.php cert_id proto
class Certificate {
const EASYRSA_DIR = '/etc/easy-rsa-clientnet';
const OPENVPN_UDP = '/etc/openvpn/client/client-udp.conf';
const OPENVPN_TCP = '/etc/openvpn/client/client-tcp.conf';
const OPENVPN_SRV_UDP = '/etc/openvpn/server/udp';
const OPENVPN_SRV_TCP = '/etc/openvpn/server/tcp';
private function exec_rsa($cmd) {
exec('EASYRSA='.self::EASYRSA_DIR.' '.$cmd.' &> /dev/null',$output,$return_var);
if($return_var!=0) {
throw new Exception('Certificate::exec_rsa() failed');
}
}
public function generate_cert_file(string $cert_id) : bool {
$ok = true;
try {
if(!file_exists(self::EASYRSA_DIR.'/pki/issued/'.$cert_id.'.crt')) {
$this->exec_rsa('easyrsa --batch --dn-mode=org --req-c= --req-st= --req-city= --req-email= --req-ou= --req-org= --req-cn='.$cert_id.' gen-req '.$cert_id.' nopass');
$this->exec_rsa('easyrsa --batch sign-req client '.$cert_id);
}
} catch (Exception $ex) {
$ok = false;
}
return $ok;
}
public function generate_ovpn_file(string $cert_id, string $proto) : void {
if(!( $proto === 'udp' || $proto === 'tcp' )) {
log_message('error','Certificate::generate_ovpn_file('."$cert_id,$proto".') failed: Unknown proto');
return;
}
$ovpn_fname = __DIR__.'/auth-'.$cert_id.'-'.$proto.'.ovpn';
// Content
$c_srv = $proto === 'udp' ? self::OPENVPN_SRV_UDP : self::OPENVPN_SRV_TCP;
$c_base = $this->generate_ovpn_fread($proto === 'udp' ? self::OPENVPN_UDP : self::OPENVPN_TCP);
$c_ca = $this->generate_ovpn_fread($c_srv.'/ca.crt');
$c_crt = $this->generate_ovpn_fread(self::EASYRSA_DIR.'/pki/issued/'.$cert_id.'.crt');
$c_key = $this->generate_ovpn_fread(self::EASYRSA_DIR.'/pki/private/'.$cert_id.'.key');
$c_ta = $this->generate_ovpn_fread($c_srv.'/ta.key');
if($c_base === null || $c_ca === null || $c_crt === null || $c_key === null || $c_ta === null) {
throw new Exception('Certificate::generate_ovpn_file('.$cert_id.') failed: One or more fread() failed');
return;
}
$c_crt_trim = strpos($c_crt,'-----BEGIN CERTIFICATE-----');
if($c_crt_trim!==0) {
$c_crt = substr($c_crt,$c_crt_trim);
}
$ovpn_str = $c_base;
$ovpn_str.= '<ca>'."\n";
$ovpn_str.= $c_ca;
$ovpn_str.= '</ca>'."\n";
$ovpn_str.= '<cert>'."\n";
$ovpn_str.= $c_crt;
$ovpn_str.= '</cert>'."\n";
$ovpn_str.= '<key>'."\n";
$ovpn_str.= $c_key;
$ovpn_str.= '</key>'."\n";
$ovpn_str.= '<tls-auth>'."\n";
$ovpn_str.= $c_ta;
$ovpn_str.= '</tls-auth>'."\n";
// Write
file_put_contents($ovpn_fname,$ovpn_str);
}
public function generate_ovpn_fread(string $filename) : ?string {
$f = @file_get_contents($filename);
if(!$f) {
throw new Exception('Certificate::generate_ovpn_fread() File not exists '.$filename);
return null;
}
return $f;
}
public function main($argc,$argv) {
if($argc !== 3) {
echo "USAGE: generate.php cert_id proto\n";
return;
}
$cert_id = $argv[1];
$proto = $argv[2];
$this->generate_cert_file($cert_id);
$this->generate_ovpn_file($cert_id, $proto);
}
}
$c = new Certificate();
$c->main($argc,$argv);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment