{"id":39691,"date":"2020-12-08T22:43:59","date_gmt":"2020-12-08T15:43:59","guid":{"rendered":"https:\/\/harry.sufehmi.com\/?p=39691"},"modified":"2020-12-08T22:51:04","modified_gmt":"2020-12-08T15:51:04","slug":"setup-openvpn-server-on-proxmox-lxc","status":"publish","type":"post","link":"https:\/\/harry.sufehmi.com\/archives\/2020-12-08-setup-openvpn-server-on-proxmox-lxc\/","title":{"rendered":"Setup OpenVPN Server on Proxmox LXC"},"content":{"rendered":"\n

I needed to do this, but all the tutorials that I could find are incomplete, or already outdated, such as this<\/a>.

After hacking around for a while, here’s how to correctly setup OpenVPN server in a container on Proxmox:

(btw if you just need to setup an OpenVPN Server in a normal server \/ non-container, then just do the “in container” part below<\/em>)<\/p>\n\n\n\n

IN HOST<\/h2>\n\n\n\n
# create special device \"tun\" for OpenVPN\nmkdir -p \/devcontainer\/net\nmknod \/devcontainer\/net\/tun c 10 200\nchown 100000:100000 \/devcontainer\/net\/tun\n\n# enable your container to use that tun device\n# change 124 into your container's number : pct list\necho \"lxc.mount.entry: \/devcontainer\/net dev\/net none bind,create=dir\" >> \/etc\/pve\/lxc\/124.conf\n\n# forward OpenVPN traffic to your container's IP address\n# change 10.10.60.6 to your container's IP address\niptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport 1194 -j DNAT --to-destination 10.10.60.6:1194\n\niptables -t nat -A PREROUTING -i vmbr0 -p udp -m udp --dport 1194 -j DNAT --to-destination 10.10.60.6:1194\n\niptables -t nat -A PREROUTING -i vmbr1 -p tcp -m tcp --dport 53 -j DNAT --to-destination 10.10.60.6:53\n\n# save iptables's rule\niptables-save > \/etc\/iptables.rules\n<\/code><\/pre>\n\n\n\n

IN CONTAINER<\/h2>\n\n\n\n
# execute the automated OpenVPN installation script \nmkdir \/root\/scripts\ncd \/root\/scripts\n\nwget git.io\/vpn --no-check-certificate -O openvpn-install.sh ; chmod +x openvpn-install.sh ; .\/openvpn-install.sh\n \n# if you'd like to change the default 10.8.0.xxx IP address, do this :\n# vi openvpn-install.sh\n# :%s\/10.8.0\/10.88.0\/g\n\n# setup NAT, so the OpenVPN clients can connect to the internet \n# while connected to this OpenVPN server\niptables -I POSTROUTING -t nat -s 10.88.0.0\/24 -j MASQUERADE\n\n# save iptables's rule\niptables-save > \/etc\/iptables.rules\n<\/code><\/pre>\n\n\n\n

After executing the \/root\/scripts\/openvpn-install.sh script , it will result in a file with ovpn<\/strong> extension

Download that to your computer \/ client,
install OpenVPN client,
and use that ovpn file as the configuration

Enjoy !<\/p>\n\n\n\n


\n\n\n\n

<\/p>\n\n\n\n

In case that very helpful OpenVPN Server install script suddenly disappear, here it is :<\/em><\/p>\n\n\n\n

#!\/bin\/bash\n#\n# https:\/\/github.com\/Nyr\/openvpn-install\n#\n# Copyright (c) 2013 Nyr. Released under the MIT License.\n\n\n# Detect Debian users running the script with \"sh\" instead of bash\nif readlink \/proc\/$$\/exe | grep -q \"dash\"; then\n\techo 'This installer needs to be run with \"bash\", not \"sh\".'\n\texit\nfi\n\n# Discard stdin. Needed when running from an one-liner which includes a newline\nread -N 999999 -t 0.001\n\n# Detect OpenVZ 6\nif [[ $(uname -r | cut -d \".\" -f 1) -eq 2 ]]; then\n\techo \"The system is running an old kernel, which is incompatible with this installer.\"\n\texit\nfi\n\n# Detect OS\n# $os_version variables aren't always in use, but are kept here for convenience\nif grep -qs \"ubuntu\" \/etc\/os-release; then\n\tos=\"ubuntu\"\n\tos_version=$(grep 'VERSION_ID' \/etc\/os-release | cut -d '\"' -f 2 | tr -d '.')\n\tgroup_name=\"nogroup\"\nelif [[ -e \/etc\/debian_version ]]; then\n\tos=\"debian\"\n\tos_version=$(grep -oE '[0-9]+' \/etc\/debian_version | head -1)\n\tgroup_name=\"nogroup\"\nelif [[ -e \/etc\/centos-release ]]; then\n\tos=\"centos\"\n\tos_version=$(grep -oE '[0-9]+' \/etc\/centos-release | head -1)\n\tgroup_name=\"nobody\"\nelif [[ -e \/etc\/fedora-release ]]; then\n\tos=\"fedora\"\n\tos_version=$(grep -oE '[0-9]+' \/etc\/fedora-release | head -1)\n\tgroup_name=\"nobody\"\nelse\n\techo \"This installer seems to be running on an unsupported distribution.\nSupported distributions are Ubuntu, Debian, CentOS, and Fedora.\"\n\texit\nfi\n\nif [[ \"$os\" == \"ubuntu\" && \"$os_version\" -lt 1804 ]]; then\n\techo \"Ubuntu 18.04 or higher is required to use this installer.\nThis version of Ubuntu is too old and unsupported.\"\n\texit\nfi\n\nif [[ \"$os\" == \"debian\" && \"$os_version\" -lt 9 ]]; then\n\techo \"Debian 9 or higher is required to use this installer.\nThis version of Debian is too old and unsupported.\"\n\texit\nfi\n\nif [[ \"$os\" == \"centos\" && \"$os_version\" -lt 7 ]]; then\n\techo \"CentOS 7 or higher is required to use this installer.\nThis version of CentOS is too old and unsupported.\"\n\texit\nfi\n\n# Detect environments where $PATH does not include the sbin directories\nif ! grep -q sbin <<< \"$PATH\"; then\n\techo '$PATH does not include sbin. Try using \"su -\" instead of \"su\".'\n\texit\nfi\n\nif [[ \"$EUID\" -ne 0 ]]; then\n\techo \"This installer needs to be run with superuser privileges.\"\n\texit\nfi\n\nif [[ ! -e \/dev\/net\/tun ]] || ! ( exec 7<>\/dev\/net\/tun ) 2>\/dev\/null; then\n\techo \"The system does not have the TUN device available.\nTUN needs to be enabled before running this installer.\"\n\texit\nfi\n\nnew_client () {\n\t# Generates the custom client.ovpn\n\t{\n\tcat \/etc\/openvpn\/server\/client-common.txt\n\techo \"<ca>\"\n\tcat \/etc\/openvpn\/server\/easy-rsa\/pki\/ca.crt\n\techo \"<\/ca>\"\n\techo \"<cert>\"\n\tsed -ne '\/BEGIN CERTIFICATE\/,$ p' \/etc\/openvpn\/server\/easy-rsa\/pki\/issued\/\"$client\".crt\n\techo \"<\/cert>\"\n\techo \"<key>\"\n\tcat \/etc\/openvpn\/server\/easy-rsa\/pki\/private\/\"$client\".key\n\techo \"<\/key>\"\n\techo \"<tls-crypt>\"\n\tsed -ne '\/BEGIN OpenVPN Static key\/,$ p' \/etc\/openvpn\/server\/tc.key\n\techo \"<\/tls-crypt>\"\n\t} > ~\/\"$client\".ovpn\n}\n\nif [[ ! -e \/etc\/openvpn\/server\/server.conf ]]; then\n\tclear\n\techo 'Welcome to this OpenVPN road warrior installer!'\n\t# If system has a single IPv4, it is selected automatically. Else, ask the user\n\tif [[ $(ip -4 addr | grep inet | grep -vEc '127(\\.[0-9]{1,3}){3}') -eq 1 ]]; then\n\t\tip=$(ip -4 addr | grep inet | grep -vE '127(\\.[0-9]{1,3}){3}' | cut -d '\/' -f 1 | grep -oE '[0-9]{1,3}(\\.[0-9]{1,3}){3}')\n\telse\n\t\tnumber_of_ip=$(ip -4 addr | grep inet | grep -vEc '127(\\.[0-9]{1,3}){3}')\n\t\techo\n\t\techo \"Which IPv4 address should be used?\"\n\t\tip -4 addr | grep inet | grep -vE '127(\\.[0-9]{1,3}){3}' | cut -d '\/' -f 1 | grep -oE '[0-9]{1,3}(\\.[0-9]{1,3}){3}' | nl -s ') '\n\t\tread -p \"IPv4 address [1]: \" ip_number\n\t\tuntil [[ -z \"$ip_number\" || \"$ip_number\" =~ ^[0-9]+$ && \"$ip_number\" -le \"$number_of_ip\" ]]; do\n\t\t\techo \"$ip_number: invalid selection.\"\n\t\t\tread -p \"IPv4 address [1]: \" ip_number\n\t\tdone\n\t\t[[ -z \"$ip_number\" ]] && ip_number=\"1\"\n\t\tip=$(ip -4 addr | grep inet | grep -vE '127(\\.[0-9]{1,3}){3}' | cut -d '\/' -f 1 | grep -oE '[0-9]{1,3}(\\.[0-9]{1,3}){3}' | sed -n \"$ip_number\"p)\n\tfi\n\t# If $ip is a private IP address, the server must be behind NAT\n\tif echo \"$ip\" | grep -qE '^(10\\.|172\\.1[6789]\\.|172\\.2[0-9]\\.|172\\.3[01]\\.|192\\.168)'; then\n\t\techo\n\t\techo \"This server is behind NAT. What is the public IPv4 address or hostname?\"\n\t\t# Get public IP and sanitize with grep\n\t\tget_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\\.[0-9]{1,3}){3}$' <<< \"$(wget -T 10 -t 1 -4qO- \"http:\/\/ip1.dynupdate.no-ip.com\/\" || curl -m 10 -4Ls \"http:\/\/ip1.dynupdate.no-ip.com\/\")\")\n\t\tread -p \"Public IPv4 address \/ hostname [$get_public_ip]: \" public_ip\n\t\t# If the checkip service is unavailable and user didn't provide input, ask again\n\t\tuntil [[ -n \"$get_public_ip\" || -n \"$public_ip\" ]]; do\n\t\t\techo \"Invalid input.\"\n\t\t\tread -p \"Public IPv4 address \/ hostname: \" public_ip\n\t\tdone\n\t\t[[ -z \"$public_ip\" ]] && public_ip=\"$get_public_ip\"\n\tfi\n\t# If system has a single IPv6, it is selected automatically\n\tif [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then\n\t\tip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '\/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}')\n\tfi\n\t# If system has multiple IPv6, ask the user to select one\n\tif [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then\n\t\tnumber_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]')\n\t\techo\n\t\techo \"Which IPv6 address should be used?\"\n\t\tip -6 addr | grep 'inet6 [23]' | cut -d '\/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | nl -s ') '\n\t\tread -p \"IPv6 address [1]: \" ip6_number\n\t\tuntil [[ -z \"$ip6_number\" || \"$ip6_number\" =~ ^[0-9]+$ && \"$ip6_number\" -le \"$number_of_ip6\" ]]; do\n\t\t\techo \"$ip6_number: invalid selection.\"\n\t\t\tread -p \"IPv6 address [1]: \" ip6_number\n\t\tdone\n\t\t[[ -z \"$ip6_number\" ]] && ip6_number=\"1\"\n\t\tip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '\/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | sed -n \"$ip6_number\"p)\n\tfi\n\techo\n\techo \"Which protocol should OpenVPN use?\"\n\techo \"   1) UDP (recommended)\"\n\techo \"   2) TCP\"\n\tread -p \"Protocol [1]: \" protocol\n\tuntil [[ -z \"$protocol\" || \"$protocol\" =~ ^[12]$ ]]; do\n\t\techo \"$protocol: invalid selection.\"\n\t\tread -p \"Protocol [1]: \" protocol\n\tdone\n\tcase \"$protocol\" in\n\t\t1|\"\") \n\t\tprotocol=udp\n\t\t;;\n\t\t2) \n\t\tprotocol=tcp\n\t\t;;\n\tesac\n\techo\n\techo \"What port should OpenVPN listen to?\"\n\tread -p \"Port [1194]: \" port\n\tuntil [[ -z \"$port\" || \"$port\" =~ ^[0-9]+$ && \"$port\" -le 65535 ]]; do\n\t\techo \"$port: invalid port.\"\n\t\tread -p \"Port [1194]: \" port\n\tdone\n\t[[ -z \"$port\" ]] && port=\"1194\"\n\techo\n\techo \"Select a DNS server for the clients:\"\n\techo \"   1) Current system resolvers\"\n\techo \"   2) Google\"\n\techo \"   3) 1.1.1.1\"\n\techo \"   4) OpenDNS\"\n\techo \"   5) Quad9\"\n\techo \"   6) AdGuard\"\n\tread -p \"DNS server [1]: \" dns\n\tuntil [[ -z \"$dns\" || \"$dns\" =~ ^[1-6]$ ]]; do\n\t\techo \"$dns: invalid selection.\"\n\t\tread -p \"DNS server [1]: \" dns\n\tdone\n\techo\n\techo \"Enter a name for the first client:\"\n\tread -p \"Name [client]: \" unsanitized_client\n\t# Allow a limited set of characters to avoid conflicts\n\tclient=$(sed 's\/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]\/_\/g' <<< \"$unsanitized_client\")\n\t[[ -z \"$client\" ]] && client=\"client\"\n\techo\n\techo \"OpenVPN installation is ready to begin.\"\n\t# Install a firewall in the rare case where one is not already available\n\tif ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>\/dev\/null; then\n\t\tif [[ \"$os\" == \"centos\" || \"$os\" == \"fedora\" ]]; then\n\t\t\tfirewall=\"firewalld\"\n\t\t\t# We don't want to silently enable firewalld, so we give a subtle warning\n\t\t\t# If the user continues, firewalld will be installed and enabled during setup\n\t\t\techo \"firewalld, which is required to manage routing tables, will also be installed.\"\n\t\telif [[ \"$os\" == \"debian\" || \"$os\" == \"ubuntu\" ]]; then\n\t\t\t# iptables is way less invasive than firewalld so no warning is given\n\t\t\tfirewall=\"iptables\"\n\t\tfi\n\tfi\n\tread -n1 -r -p \"Press any key to continue...\"\n\t# If running inside a container, disable LimitNPROC to prevent conflicts\n\tif systemd-detect-virt -cq; then\n\t\tmkdir \/etc\/systemd\/system\/openvpn-server@server.service.d\/ 2>\/dev\/null\n\t\techo \"[Service]\nLimitNPROC=infinity\" > \/etc\/systemd\/system\/openvpn-server@server.service.d\/disable-limitnproc.conf\n\tfi\n\tif [[ \"$os\" = \"debian\" || \"$os\" = \"ubuntu\" ]]; then\n\t\tapt-get update\n\t\tapt-get install -y openvpn openssl ca-certificates $firewall\n\telif [[ \"$os\" = \"centos\" ]]; then\n\t\tyum install -y epel-release\n\t\tyum install -y openvpn openssl ca-certificates tar $firewall\n\telse\n\t\t# Else, OS must be Fedora\n\t\tdnf install -y openvpn openssl ca-certificates tar $firewall\n\tfi\n\t# If firewalld was just installed, enable it\n\tif [[ \"$firewall\" == \"firewalld\" ]]; then\n\t\tsystemctl enable --now firewalld.service\n\tfi\n\t# Get easy-rsa\n\teasy_rsa_url='https:\/\/github.com\/OpenVPN\/easy-rsa\/releases\/download\/v3.0.8\/EasyRSA-3.0.8.tgz'\n\tmkdir -p \/etc\/openvpn\/server\/easy-rsa\/\n\t{ wget -qO- \"$easy_rsa_url\" 2>\/dev\/null || curl -sL \"$easy_rsa_url\" ; } | tar xz -C \/etc\/openvpn\/server\/easy-rsa\/ --strip-components 1\n\tchown -R root:root \/etc\/openvpn\/server\/easy-rsa\/\n\tcd \/etc\/openvpn\/server\/easy-rsa\/\n\t# Create the PKI, set up the CA and the server and client certificates\n\t.\/easyrsa init-pki\n\t.\/easyrsa --batch build-ca nopass\n\tEASYRSA_CERT_EXPIRE=3650 .\/easyrsa build-server-full server nopass\n\tEASYRSA_CERT_EXPIRE=3650 .\/easyrsa build-client-full \"$client\" nopass\n\tEASYRSA_CRL_DAYS=3650 .\/easyrsa gen-crl\n\t# Move the stuff we need\n\tcp pki\/ca.crt pki\/private\/ca.key pki\/issued\/server.crt pki\/private\/server.key pki\/crl.pem \/etc\/openvpn\/server\n\t# CRL is read with each client connection, while OpenVPN is dropped to nobody\n\tchown nobody:\"$group_name\" \/etc\/openvpn\/server\/crl.pem\n\t# Without +x in the directory, OpenVPN can't run a stat() on the CRL file\n\tchmod o+x \/etc\/openvpn\/server\/\n\t# Generate key for tls-crypt\n\topenvpn --genkey --secret \/etc\/openvpn\/server\/tc.key\n\t# Create the DH parameters file using the predefined ffdhe2048 group\n\techo '-----BEGIN DH PARAMETERS-----\nMIIBCAKCAQEA\/\/\/\/\/\/\/\/\/\/+t+FRYortKmq\/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n+8yTnc4kmz75fS\/jY2MMddj2gbICrsRhetPfHtXV\/WVhJDP1H18GbtCFY2VVPe0a\n87VXE15\/V8k1mE8McODmi3fipona8+\/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\nYdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n7MA0BM0oNC9hkXL+nOmFg\/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\nssbzSibBsu\/6iGtCOGEoXJf\/\/\/\/\/\/\/\/\/\/wIBAg==\n-----END DH PARAMETERS-----' > \/etc\/openvpn\/server\/dh.pem\n\t# Generate server.conf\n\techo \"local $ip\nport $port\nproto $protocol\ndev tun\nca ca.crt\ncert server.crt\nkey server.key\ndh dh.pem\nauth SHA512\ntls-crypt tc.key\ntopology subnet\nserver 10.8.0.0 255.255.255.0\" > \/etc\/openvpn\/server\/server.conf\n\t# IPv6\n\tif [[ -z \"$ip6\" ]]; then\n\t\techo 'push \"redirect-gateway def1 bypass-dhcp\"' >> \/etc\/openvpn\/server\/server.conf\n\telse\n\t\techo 'server-ipv6 fddd:1194:1194:1194::\/64' >> \/etc\/openvpn\/server\/server.conf\n\t\techo 'push \"redirect-gateway def1 ipv6 bypass-dhcp\"' >> \/etc\/openvpn\/server\/server.conf\n\tfi\n\techo 'ifconfig-pool-persist ipp.txt' >> \/etc\/openvpn\/server\/server.conf\n\t# DNS\n\tcase \"$dns\" in\n\t\t1|\"\")\n\t\t\t# Locate the proper resolv.conf\n\t\t\t# Needed for systems running systemd-resolved\n\t\t\tif grep -q '^nameserver 127.0.0.53' \"\/etc\/resolv.conf\"; then\n\t\t\t\tresolv_conf=\"\/run\/systemd\/resolve\/resolv.conf\"\n\t\t\telse\n\t\t\t\tresolv_conf=\"\/etc\/resolv.conf\"\n\t\t\tfi\n\t\t\t# Obtain the resolvers from resolv.conf and use them for OpenVPN\n\t\t\tgrep -v '^#\\|^;' \"$resolv_conf\" | grep '^nameserver' | grep -oE '[0-9]{1,3}(\\.[0-9]{1,3}){3}' | while read line; do\n\t\t\t\techo \"push \\\"dhcp-option DNS $line\\\"\" >> \/etc\/openvpn\/server\/server.conf\n\t\t\tdone\n\t\t;;\n\t\t2)\n\t\t\techo 'push \"dhcp-option DNS 8.8.8.8\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t\techo 'push \"dhcp-option DNS 8.8.4.4\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t;;\n\t\t3)\n\t\t\techo 'push \"dhcp-option DNS 1.1.1.1\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t\techo 'push \"dhcp-option DNS 1.0.0.1\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t;;\n\t\t4)\n\t\t\techo 'push \"dhcp-option DNS 208.67.222.222\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t\techo 'push \"dhcp-option DNS 208.67.220.220\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t;;\n\t\t5)\n\t\t\techo 'push \"dhcp-option DNS 9.9.9.9\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t\techo 'push \"dhcp-option DNS 149.112.112.112\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t;;\n\t\t6)\n\t\t\techo 'push \"dhcp-option DNS 94.140.14.14\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t\techo 'push \"dhcp-option DNS 94.140.15.15\"' >> \/etc\/openvpn\/server\/server.conf\n\t\t;;\n\tesac\n\techo \"keepalive 10 120\ncipher AES-256-CBC\nuser nobody\ngroup $group_name\npersist-key\npersist-tun\nstatus openvpn-status.log\nverb 3\ncrl-verify crl.pem\" >> \/etc\/openvpn\/server\/server.conf\n\tif [[ \"$protocol\" = \"udp\" ]]; then\n\t\techo \"explicit-exit-notify\" >> \/etc\/openvpn\/server\/server.conf\n\tfi\n\t# Enable net.ipv4.ip_forward for the system\n\techo 'net.ipv4.ip_forward=1' > \/etc\/sysctl.d\/30-openvpn-forward.conf\n\t# Enable without waiting for a reboot or service restart\n\techo 1 > \/proc\/sys\/net\/ipv4\/ip_forward\n\tif [[ -n \"$ip6\" ]]; then\n\t\t# Enable net.ipv6.conf.all.forwarding for the system\n\t\techo \"net.ipv6.conf.all.forwarding=1\" >> \/etc\/sysctl.d\/30-openvpn-forward.conf\n\t\t# Enable without waiting for a reboot or service restart\n\t\techo 1 > \/proc\/sys\/net\/ipv6\/conf\/all\/forwarding\n\tfi\n\tif systemctl is-active --quiet firewalld.service; then\n\t\t# Using both permanent and not permanent rules to avoid a firewalld\n\t\t# reload.\n\t\t# We don't use --add-service=openvpn because that would only work with\n\t\t# the default port and protocol.\n\t\tfirewall-cmd --add-port=\"$port\"\/\"$protocol\"\n\t\tfirewall-cmd --zone=trusted --add-source=10.8.0.0\/24\n\t\tfirewall-cmd --permanent --add-port=\"$port\"\/\"$protocol\"\n\t\tfirewall-cmd --permanent --zone=trusted --add-source=10.8.0.0\/24\n\t\t# Set NAT for the VPN subnet\n\t\tfirewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to \"$ip\"\n\t\tfirewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to \"$ip\"\n\t\tif [[ -n \"$ip6\" ]]; then\n\t\t\tfirewall-cmd --zone=trusted --add-source=fddd:1194:1194:1194::\/64\n\t\t\tfirewall-cmd --permanent --zone=trusted --add-source=fddd:1194:1194:1194::\/64\n\t\t\tfirewall-cmd --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to \"$ip6\"\n\t\t\tfirewall-cmd --permanent --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to \"$ip6\"\n\t\tfi\n\telse\n\t\t# Create a service to set up persistent iptables rules\n\t\tiptables_path=$(command -v iptables)\n\t\tip6tables_path=$(command -v ip6tables)\n\t\t# nf_tables is not available as standard in OVZ kernels. So use iptables-legacy\n\t\t# if we are in OVZ, with a nf_tables backend and iptables-legacy is available.\n\t\tif [[ $(systemd-detect-virt) == \"openvz\" ]] && readlink -f \"$(command -v iptables)\" | grep -q \"nft\" && hash iptables-legacy 2>\/dev\/null; then\n\t\t\tiptables_path=$(command -v iptables-legacy)\n\t\t\tip6tables_path=$(command -v ip6tables-legacy)\n\t\tfi\n\t\techo \"[Unit]\nBefore=network.target\n[Service]\nType=oneshot\nExecStart=$iptables_path -t nat -A POSTROUTING -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to $ip\nExecStart=$iptables_path -I INPUT -p $protocol --dport $port -j ACCEPT\nExecStart=$iptables_path -I FORWARD -s 10.8.0.0\/24 -j ACCEPT\nExecStart=$iptables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\nExecStop=$iptables_path -t nat -D POSTROUTING -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to $ip\nExecStop=$iptables_path -D INPUT -p $protocol --dport $port -j ACCEPT\nExecStop=$iptables_path -D FORWARD -s 10.8.0.0\/24 -j ACCEPT\nExecStop=$iptables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\" > \/etc\/systemd\/system\/openvpn-iptables.service\n\t\tif [[ -n \"$ip6\" ]]; then\n\t\t\techo \"ExecStart=$ip6tables_path -t nat -A POSTROUTING -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to $ip6\nExecStart=$ip6tables_path -I FORWARD -s fddd:1194:1194:1194::\/64 -j ACCEPT\nExecStart=$ip6tables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\nExecStop=$ip6tables_path -t nat -D POSTROUTING -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to $ip6\nExecStop=$ip6tables_path -D FORWARD -s fddd:1194:1194:1194::\/64 -j ACCEPT\nExecStop=$ip6tables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\" >> \/etc\/systemd\/system\/openvpn-iptables.service\n\t\tfi\n\t\techo \"RemainAfterExit=yes\n[Install]\nWantedBy=multi-user.target\" >> \/etc\/systemd\/system\/openvpn-iptables.service\n\t\tsystemctl enable --now openvpn-iptables.service\n\tfi\n\t# If SELinux is enabled and a custom port was selected, we need this\n\tif sestatus 2>\/dev\/null | grep \"Current mode\" | grep -q \"enforcing\" && [[ \"$port\" != 1194 ]]; then\n\t\t# Install semanage if not already present\n\t\tif ! hash semanage 2>\/dev\/null; then\n\t\t\tif [[ \"$os_version\" -eq 7 ]]; then\n\t\t\t\t# Centos 7\n\t\t\t\tyum install -y policycoreutils-python\n\t\t\telse\n\t\t\t\t# CentOS 8 or Fedora\n\t\t\t\tdnf install -y policycoreutils-python-utils\n\t\t\tfi\n\t\tfi\n\t\tsemanage port -a -t openvpn_port_t -p \"$protocol\" \"$port\"\n\tfi\n\t# If the server is behind NAT, use the correct IP address\n\t[[ -n \"$public_ip\" ]] && ip=\"$public_ip\"\n\t# client-common.txt is created so we have a template to add further users later\n\techo \"client\ndev tun\nproto $protocol\nremote $ip $port\nresolv-retry infinite\nnobind\npersist-key\npersist-tun\nremote-cert-tls server\nauth SHA512\ncipher AES-256-CBC\nignore-unknown-option block-outside-dns\nblock-outside-dns\nverb 3\" > \/etc\/openvpn\/server\/client-common.txt\n\t# Enable and start the OpenVPN service\n\tsystemctl enable --now openvpn-server@server.service\n\t# Generates the custom client.ovpn\n\tnew_client\n\techo\n\techo \"Finished!\"\n\techo\n\techo \"The client configuration is available in:\" ~\/\"$client.ovpn\"\n\techo \"New clients can be added by running this script again.\"\nelse\n\tclear\n\techo \"OpenVPN is already installed.\"\n\techo\n\techo \"Select an option:\"\n\techo \"   1) Add a new client\"\n\techo \"   2) Revoke an existing client\"\n\techo \"   3) Remove OpenVPN\"\n\techo \"   4) Exit\"\n\tread -p \"Option: \" option\n\tuntil [[ \"$option\" =~ ^[1-4]$ ]]; do\n\t\techo \"$option: invalid selection.\"\n\t\tread -p \"Option: \" option\n\tdone\n\tcase \"$option\" in\n\t\t1)\n\t\t\techo\n\t\t\techo \"Provide a name for the client:\"\n\t\t\tread -p \"Name: \" unsanitized_client\n\t\t\tclient=$(sed 's\/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]\/_\/g' <<< \"$unsanitized_client\")\n\t\t\twhile [[ -z \"$client\" || -e \/etc\/openvpn\/server\/easy-rsa\/pki\/issued\/\"$client\".crt ]]; do\n\t\t\t\techo \"$client: invalid name.\"\n\t\t\t\tread -p \"Name: \" unsanitized_client\n\t\t\t\tclient=$(sed 's\/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]\/_\/g' <<< \"$unsanitized_client\")\n\t\t\tdone\n\t\t\tcd \/etc\/openvpn\/server\/easy-rsa\/\n\t\t\tEASYRSA_CERT_EXPIRE=3650 .\/easyrsa build-client-full \"$client\" nopass\n\t\t\t# Generates the custom client.ovpn\n\t\t\tnew_client\n\t\t\techo\n\t\t\techo \"$client added. Configuration available in:\" ~\/\"$client.ovpn\"\n\t\t\texit\n\t\t;;\n\t\t2)\n\t\t\t# This option could be documented a bit better and maybe even be simplified\n\t\t\t# ...but what can I say, I want some sleep too\n\t\t\tnumber_of_clients=$(tail -n +2 \/etc\/openvpn\/server\/easy-rsa\/pki\/index.txt | grep -c \"^V\")\n\t\t\tif [[ \"$number_of_clients\" = 0 ]]; then\n\t\t\t\techo\n\t\t\t\techo \"There are no existing clients!\"\n\t\t\t\texit\n\t\t\tfi\n\t\t\techo\n\t\t\techo \"Select the client to revoke:\"\n\t\t\ttail -n +2 \/etc\/openvpn\/server\/easy-rsa\/pki\/index.txt | grep \"^V\" | cut -d '=' -f 2 | nl -s ') '\n\t\t\tread -p \"Client: \" client_number\n\t\t\tuntil [[ \"$client_number\" =~ ^[0-9]+$ && \"$client_number\" -le \"$number_of_clients\" ]]; do\n\t\t\t\techo \"$client_number: invalid selection.\"\n\t\t\t\tread -p \"Client: \" client_number\n\t\t\tdone\n\t\t\tclient=$(tail -n +2 \/etc\/openvpn\/server\/easy-rsa\/pki\/index.txt | grep \"^V\" | cut -d '=' -f 2 | sed -n \"$client_number\"p)\n\t\t\techo\n\t\t\tread -p \"Confirm $client revocation? [y\/N]: \" revoke\n\t\t\tuntil [[ \"$revoke\" =~ ^[yYnN]*$ ]]; do\n\t\t\t\techo \"$revoke: invalid selection.\"\n\t\t\t\tread -p \"Confirm $client revocation? [y\/N]: \" revoke\n\t\t\tdone\n\t\t\tif [[ \"$revoke\" =~ ^[yY]$ ]]; then\n\t\t\t\tcd \/etc\/openvpn\/server\/easy-rsa\/\n\t\t\t\t.\/easyrsa --batch revoke \"$client\"\n\t\t\t\tEASYRSA_CRL_DAYS=3650 .\/easyrsa gen-crl\n\t\t\t\trm -f \/etc\/openvpn\/server\/crl.pem\n\t\t\t\tcp \/etc\/openvpn\/server\/easy-rsa\/pki\/crl.pem \/etc\/openvpn\/server\/crl.pem\n\t\t\t\t# CRL is read with each client connection, when OpenVPN is dropped to nobody\n\t\t\t\tchown nobody:\"$group_name\" \/etc\/openvpn\/server\/crl.pem\n\t\t\t\techo\n\t\t\t\techo \"$client revoked!\"\n\t\t\telse\n\t\t\t\techo\n\t\t\t\techo \"$client revocation aborted!\"\n\t\t\tfi\n\t\t\texit\n\t\t;;\n\t\t3)\n\t\t\techo\n\t\t\tread -p \"Confirm OpenVPN removal? [y\/N]: \" remove\n\t\t\tuntil [[ \"$remove\" =~ ^[yYnN]*$ ]]; do\n\t\t\t\techo \"$remove: invalid selection.\"\n\t\t\t\tread -p \"Confirm OpenVPN removal? [y\/N]: \" remove\n\t\t\tdone\n\t\t\tif [[ \"$remove\" =~ ^[yY]$ ]]; then\n\t\t\t\tport=$(grep '^port ' \/etc\/openvpn\/server\/server.conf | cut -d \" \" -f 2)\n\t\t\t\tprotocol=$(grep '^proto ' \/etc\/openvpn\/server\/server.conf | cut -d \" \" -f 2)\n\t\t\t\tif systemctl is-active --quiet firewalld.service; then\n\t\t\t\t\tip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\\-s 10.8.0.0\/24 '\"'\"'!'\"'\"' -d 10.8.0.0\/24' | grep -oE '[^ ]+$')\n\t\t\t\t\t# Using both permanent and not permanent rules to avoid a firewalld reload.\n\t\t\t\t\tfirewall-cmd --remove-port=\"$port\"\/\"$protocol\"\n\t\t\t\t\tfirewall-cmd --zone=trusted --remove-source=10.8.0.0\/24\n\t\t\t\t\tfirewall-cmd --permanent --remove-port=\"$port\"\/\"$protocol\"\n\t\t\t\t\tfirewall-cmd --permanent --zone=trusted --remove-source=10.8.0.0\/24\n\t\t\t\t\tfirewall-cmd --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to \"$ip\"\n\t\t\t\t\tfirewall-cmd --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0\/24 ! -d 10.8.0.0\/24 -j SNAT --to \"$ip\"\n\t\t\t\t\tif grep -qs \"server-ipv6\" \/etc\/openvpn\/server\/server.conf; then\n\t\t\t\t\t\tip6=$(firewall-cmd --direct --get-rules ipv6 nat POSTROUTING | grep '\\-s fddd:1194:1194:1194::\/64 '\"'\"'!'\"'\"' -d fddd:1194:1194:1194::\/64' | grep -oE '[^ ]+$')\n\t\t\t\t\t\tfirewall-cmd --zone=trusted --remove-source=fddd:1194:1194:1194::\/64\n\t\t\t\t\t\tfirewall-cmd --permanent --zone=trusted --remove-source=fddd:1194:1194:1194::\/64\n\t\t\t\t\t\tfirewall-cmd --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to \"$ip6\"\n\t\t\t\t\t\tfirewall-cmd --permanent --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::\/64 ! -d fddd:1194:1194:1194::\/64 -j SNAT --to \"$ip6\"\n\t\t\t\t\tfi\n\t\t\t\telse\n\t\t\t\t\tsystemctl disable --now openvpn-iptables.service\n\t\t\t\t\trm -f \/etc\/systemd\/system\/openvpn-iptables.service\n\t\t\t\tfi\n\t\t\t\tif sestatus 2>\/dev\/null | grep \"Current mode\" | grep -q \"enforcing\" && [[ \"$port\" != 1194 ]]; then\n\t\t\t\t\tsemanage port -d -t openvpn_port_t -p \"$protocol\" \"$port\"\n\t\t\t\tfi\n\t\t\t\tsystemctl disable --now openvpn-server@server.service\n\t\t\t\trm -rf \/etc\/openvpn\/server\n\t\t\t\trm -f \/etc\/systemd\/system\/openvpn-server@server.service.d\/disable-limitnproc.conf\n\t\t\t\trm -f \/etc\/sysctl.d\/30-openvpn-forward.conf\n\t\t\t\tif [[ \"$os\" = \"debian\" || \"$os\" = \"ubuntu\" ]]; then\n\t\t\t\t\tapt-get remove --purge -y openvpn\n\t\t\t\telse\n\t\t\t\t\t# Else, OS must be CentOS or Fedora\n\t\t\t\t\tyum remove -y openvpn\n\t\t\t\tfi\n\t\t\t\techo\n\t\t\t\techo \"OpenVPN removed!\"\n\t\t\telse\n\t\t\t\techo\n\t\t\t\techo \"OpenVPN removal aborted!\"\n\t\t\tfi\n\t\t\texit\n\t\t;;\n\t\t4)\n\t\t\texit\n\t\t;;\n\tesac\nfi<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"

I needed to do this, but all the tutorials that I could find are incomplete, or already outdated, such as this. After hacking around for a while, here’s how to correctly setup OpenVPN server in a container on Proxmox: (btw if you just need to setup an OpenVPN Server in a normal server \/ non-container, … Continue reading Setup OpenVPN Server on Proxmox LXC<\/span> →<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,2],"tags":[],"class_list":["post-39691","post","type-post","status-publish","format-standard","hentry","category-open-source","category-teknoblogia"],"_links":{"self":[{"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/posts\/39691"}],"collection":[{"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/comments?post=39691"}],"version-history":[{"count":4,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/posts\/39691\/revisions"}],"predecessor-version":[{"id":39695,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/posts\/39691\/revisions\/39695"}],"wp:attachment":[{"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/media?parent=39691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/categories?post=39691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/harry.sufehmi.com\/wp-json\/wp\/v2\/tags?post=39691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}