[
  {
    "path": "LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013 Nyr\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "**New: [wireguard-install](https://github.com/Nyr/wireguard-install) is also available.**\n\n## openvpn-install\nOpenVPN [road warrior](http://en.wikipedia.org/wiki/Road_warrior_%28computing%29) installer for Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora.\n\nThis script will let you set up your own VPN server in no more than a minute, even if you haven't used OpenVPN before. It has been designed to be as unobtrusive and universal as possible.\n\n### Installation\nRun the script and follow the assistant:\n\n```plain text\nwget https://git.io/vpn -O openvpn-install.sh && bash openvpn-install.sh\n```\n\nOnce it ends, you can run it again to add more users, remove some of them or even completely uninstall OpenVPN.\n\n### I want to run my own VPN but don't have a server for that\nYou can get a VPS from just [2 EUR](https://alphavps.com/clients/aff.php?aff=474&pid=457&currency=1) or [2 USD](https://alphavps.com/clients/aff.php?aff=474&pid=457&currency=6) per month at [AlphaVPS](https://alphavps.com/clients/aff.php?aff=474&pid=457&currency=1).\n\n### Donations\nIf you want to show your appreciation, you can donate via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VBAYDL34Z7J6L) or [cryptocurrency](https://pastebin.com/raw/M2JJpQpC). Thanks!\n\n### Sponsors\n[Clever SaaS](https://www.clever-vpn.net/en?wg-referral=01LOULuQoi) – Launch your professional VPN service in 5 minutes. No tech team needed. Just $1."
  },
  {
    "path": "openvpn-install.sh",
    "content": "#!/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 a one-liner which includes a newline\nread -N 999999 -t 0.001\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/almalinux-release || -e /etc/rocky-release || -e /etc/centos-release ]]; then\n\tos=\"centos\"\n\tos_version=$(grep -shoE '[0-9]+' /etc/almalinux-release /etc/rocky-release /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 distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora.\"\n\texit\nfi\n\nif [[ \"$os\" == \"ubuntu\" && \"$os_version\" -lt 2204 ]]; then\n\techo \"Ubuntu 22.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\" ]]; then\n\tif grep -q '/sid' /etc/debian_version; then\n\t\techo \"Debian Testing and Debian Unstable are unsupported by this installer.\"\n\t\texit\n\tfi\n\tif [[ \"$os_version\" -lt 11 ]]; then\n\t\techo \"Debian 11 or higher is required to use this installer.\nThis version of Debian is too old and unsupported.\"\n\t\texit\n\tfi\nfi\n\nif [[ \"$os\" == \"centos\" && \"$os_version\" -lt 9 ]]; then\n\tos_name=$(sed 's/ release.*//' /etc/almalinux-release /etc/rocky-release /etc/centos-release 2>/dev/null | head -1)\n\techo \"$os_name 9 or higher is required to use this installer.\nThis version of $os_name 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\n# Store the absolute path of the directory where the script is located\nscript_dir=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\"\n\nif [[ ! -e /etc/openvpn/server/server.conf ]]; then\n\t# Detect some Debian minimal setups where neither wget nor curl are installed\n\tif ! hash wget 2>/dev/null && ! hash curl 2>/dev/null; then\n\t\techo \"Wget is required to use this installer.\"\n\t\tread -n1 -r -p \"Press any key to install Wget and continue...\"\n\t\tapt-get update\n\t\tapt-get install -y wget\n\tfi\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 on?\"\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) Default system resolvers\"\n\techo \"   2) Google\"\n\techo \"   3) 1.1.1.1\"\n\techo \"   4) OpenDNS\"\n\techo \"   5) Quad9\"\n\techo \"   6) Gcore\"\n\techo \"   7) AdGuard\"\n\techo \"   8) Specify custom resolvers\"\n\tread -p \"DNS server [1]: \" dns\n\tuntil [[ -z \"$dns\" || \"$dns\" =~ ^[1-8]$ ]]; do\n\t\techo \"$dns: invalid selection.\"\n\t\tread -p \"DNS server [1]: \" dns\n\tdone\n\t# If the user selected custom resolvers, we deal with that here\n\tif [[ \"$dns\" = \"8\" ]]; then\n\t\techo\n\t\tuntil [[ -n \"$custom_dns\" ]]; do\n\t\t\techo \"Enter DNS servers (one or more IPv4 addresses, separated by commas or spaces):\"\n\t\t\tread -p \"DNS servers: \" dns_input\n\t\t\t# Convert comma delimited to space delimited\n\t\t\tdns_input=$(echo \"$dns_input\" | tr ',' ' ')\n\t\t\t# Validate and build custom DNS IP list\n\t\t\tfor dns_ip in $dns_input; do\n\t\t\t\tif [[ \"$dns_ip\" =~ ^[0-9]{1,3}(\\.[0-9]{1,3}){3}$ ]]; then\n\t\t\t\t\tif [[ -z \"$custom_dns\" ]]; then\n\t\t\t\t\t\tcustom_dns=\"$dns_ip\"\n\t\t\t\t\telse\n\t\t\t\t\t\tcustom_dns=\"$custom_dns $dns_ip\"\n\t\t\t\t\tfi\n\t\t\t\tfi\n\t\t\tdone\n\t\t\tif [ -z \"$custom_dns\" ]; then\n\t\t\t\techo \"Invalid input.\"\n\t\t\tfi\n\t\tdone\n\tfi\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 if firewalld or iptables are 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 --no-install-recommends openvpn openssl ca-certificates $firewall\n\telif [[ \"$os\" = \"centos\" ]]; then\n\t\tdnf install -y epel-release\n\t\tdnf 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.2.6/EasyRSA-3.2.6.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 create TLS key\n\t./easyrsa --batch init-pki\n\t./easyrsa --batch build-ca nopass\n\t./easyrsa gen-tls-crypt-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# Make easy-rsa aware of our external DH file (prevents a warning)\n\tln -s /etc/openvpn/server/dh.pem pki/dh.pem\n\t# Create certificates and CRL\n\t./easyrsa --batch --days=3650 build-server-full server nopass\n\t./easyrsa --batch --days=3650 build-client-full \"$client\" nopass\n\t./easyrsa --batch --days=3650 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\tcp pki/private/easyrsa-tls.key /etc/openvpn/server/tc.key\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 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 '^nameserver' \"/etc/resolv.conf\" | grep -qv '127.0.0.53' ; then\n\t\t\t\tresolv_conf=\"/etc/resolv.conf\"\n\t\t\telse\n\t\t\t\tresolv_conf=\"/run/systemd/resolve/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 -v '127.0.0.53' | 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 95.85.95.85\"' >> /etc/openvpn/server/server.conf\n\t\t\techo 'push \"dhcp-option DNS 2.56.220.2\"' >> /etc/openvpn/server/server.conf\n\t\t;;\n\t\t7)\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\t\t8)\n\t\tfor dns_ip in $custom_dns; do\n\t\t\techo \"push \\\"dhcp-option DNS $dns_ip\\\"\" >> /etc/openvpn/server/server.conf\n\t\tdone\n\t\t;;\n\tesac\n\techo 'push \"block-outside-dns\"' >> /etc/openvpn/server/server.conf\n\techo \"keepalive 10 120\nuser nobody\ngroup $group_name\npersist-key\npersist-tun\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/99-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/99-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]\nAfter=network-online.target\nWants=network-online.target\n[Service]\nType=oneshot\nExecStart=$iptables_path -w 5 -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip\nExecStart=$iptables_path -w 5 -I INPUT -p $protocol --dport $port -j ACCEPT\nExecStart=$iptables_path -w 5 -I FORWARD -s 10.8.0.0/24 -j ACCEPT\nExecStart=$iptables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\nExecStop=$iptables_path -w 5 -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip\nExecStop=$iptables_path -w 5 -D INPUT -p $protocol --dport $port -j ACCEPT\nExecStop=$iptables_path -w 5 -D FORWARD -s 10.8.0.0/24 -j ACCEPT\nExecStop=$iptables_path -w 5 -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 -w 5 -t nat -A POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6\nExecStart=$ip6tables_path -w 5 -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT\nExecStart=$ip6tables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\nExecStop=$ip6tables_path -w 5 -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6\nExecStop=$ip6tables_path -w 5 -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT\nExecStop=$ip6tables_path -w 5 -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\t\tdnf install -y policycoreutils-python-utils\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\nignore-unknown-option block-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# Build the $client.ovpn file, stripping comments from easy-rsa in the process\n\tgrep -vh '^#' /etc/openvpn/server/client-common.txt /etc/openvpn/server/easy-rsa/pki/inline/private/\"$client\".inline > \"$script_dir\"/\"$client\".ovpn\n\techo\n\techo \"Finished!\"\n\techo\n\techo \"The client configuration is available in:\" \"$script_dir\"/\"$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\t./easyrsa --batch --days=3650 build-client-full \"$client\" nopass\n\t\t\t# Build the $client.ovpn file, stripping comments from easy-rsa in the process\n\t\t\tgrep -vh '^#' /etc/openvpn/server/client-common.txt /etc/openvpn/server/easy-rsa/pki/inline/private/\"$client\".inline > \"$script_dir\"/\"$client\".ovpn\n\t\t\techo\n\t\t\techo \"$client added. Configuration available in:\" \"$script_dir\"/\"$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\t./easyrsa --batch --days=3650 gen-crl\n\t\t\t\trm -f /etc/openvpn/server/crl.pem\n\t\t\t\trm -f /etc/openvpn/server/easy-rsa/pki/reqs/\"$client\".req\n\t\t\t\trm -f /etc/openvpn/server/easy-rsa/pki/private/\"$client\".key\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 -f /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf\n\t\t\t\trm -f /etc/sysctl.d/99-openvpn-forward.conf\n\t\t\t\tif [[ \"$os\" = \"debian\" || \"$os\" = \"ubuntu\" ]]; then\n\t\t\t\t\trm -rf /etc/openvpn/server\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\tdnf remove -y openvpn\n\t\t\t\t\trm -rf /etc/openvpn/server\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\n"
  }
]