openvpn-ubuntu-install.sh 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. #!/bin/bash
  2. #
  3. # https://github.com/Nyr/openvpn-install
  4. #
  5. # Copyright (c) 2013 Nyr. Released under the MIT License.
  6. # Detect Debian users running the script with "sh" instead of bash
  7. if readlink /proc/$$/exe | grep -q "dash"; then
  8. echo 'This installer needs to be run with "bash", not "sh".'
  9. exit
  10. fi
  11. # Discard stdin. Needed when running from a one-liner which includes a newline
  12. read -N 999999 -t 0.001
  13. # Detect OS
  14. # $os_version variables aren't always in use, but are kept here for convenience
  15. if grep -qs "ubuntu" /etc/os-release; then
  16. os="ubuntu"
  17. os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')
  18. group_name="nogroup"
  19. elif [[ -e /etc/debian_version ]]; then
  20. os="debian"
  21. os_version=$(grep -oE '[0-9]+' /etc/debian_version | head -1)
  22. group_name="nogroup"
  23. elif [[ -e /etc/almalinux-release || -e /etc/rocky-release || -e /etc/centos-release ]]; then
  24. os="centos"
  25. os_version=$(grep -shoE '[0-9]+' /etc/almalinux-release /etc/rocky-release /etc/centos-release | head -1)
  26. group_name="nobody"
  27. elif [[ -e /etc/fedora-release ]]; then
  28. os="fedora"
  29. os_version=$(grep -oE '[0-9]+' /etc/fedora-release | head -1)
  30. group_name="nobody"
  31. else
  32. echo "This installer seems to be running on an unsupported distribution.
  33. Supported distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora."
  34. exit
  35. fi
  36. if [[ "$os" == "ubuntu" && "$os_version" -lt 2204 ]]; then
  37. echo "Ubuntu 22.04 or higher is required to use this installer.
  38. This version of Ubuntu is too old and unsupported."
  39. exit
  40. fi
  41. if [[ "$os" == "debian" ]]; then
  42. if grep -q '/sid' /etc/debian_version; then
  43. echo "Debian Testing and Debian Unstable are unsupported by this installer."
  44. exit
  45. fi
  46. if [[ "$os_version" -lt 11 ]]; then
  47. echo "Debian 11 or higher is required to use this installer.
  48. This version of Debian is too old and unsupported."
  49. exit
  50. fi
  51. fi
  52. if [[ "$os" == "centos" && "$os_version" -lt 9 ]]; then
  53. os_name=$(sed 's/ release.*//' /etc/almalinux-release /etc/rocky-release /etc/centos-release 2>/dev/null | head -1)
  54. echo "$os_name 9 or higher is required to use this installer.
  55. This version of $os_name is too old and unsupported."
  56. exit
  57. fi
  58. # Detect environments where $PATH does not include the sbin directories
  59. if ! grep -q sbin <<< "$PATH"; then
  60. echo '$PATH does not include sbin. Try using "su -" instead of "su".'
  61. exit
  62. fi
  63. if [[ "$EUID" -ne 0 ]]; then
  64. echo "This installer needs to be run with superuser privileges."
  65. exit
  66. fi
  67. if [[ ! -e /dev/net/tun ]] || ! ( exec 7<>/dev/net/tun ) 2>/dev/null; then
  68. echo "The system does not have the TUN device available.
  69. TUN needs to be enabled before running this installer."
  70. exit
  71. fi
  72. # Store the absolute path of the directory where the script is located
  73. script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  74. if [[ ! -e /etc/openvpn/server/server.conf ]]; then
  75. # Detect some Debian minimal setups where neither wget nor curl are installed
  76. if ! hash wget 2>/dev/null && ! hash curl 2>/dev/null; then
  77. echo "Wget is required to use this installer."
  78. read -n1 -r -p "Press any key to install Wget and continue..."
  79. apt-get update
  80. apt-get install -y wget
  81. fi
  82. clear
  83. echo 'Welcome to this OpenVPN road warrior installer!'
  84. # If system has a single IPv4, it is selected automatically. Else, ask the user
  85. if [[ $(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}') -eq 1 ]]; then
  86. ip=$(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}')
  87. else
  88. number_of_ip=$(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}')
  89. echo
  90. echo "Which IPv4 address should be used?"
  91. 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}' | nl -s ') '
  92. read -p "IPv4 address [1]: " ip_number
  93. until [[ -z "$ip_number" || "$ip_number" =~ ^[0-9]+$ && "$ip_number" -le "$number_of_ip" ]]; do
  94. echo "$ip_number: invalid selection."
  95. read -p "IPv4 address [1]: " ip_number
  96. done
  97. [[ -z "$ip_number" ]] && ip_number="1"
  98. ip=$(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)
  99. fi
  100. # If $ip is a private IP address, the server must be behind NAT
  101. if echo "$ip" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then
  102. echo
  103. echo "This server is behind NAT. What is the public IPv4 address or hostname?"
  104. # Get public IP and sanitize with grep
  105. get_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/")")
  106. read -p "Public IPv4 address / hostname [$get_public_ip]: " public_ip
  107. # If the checkip service is unavailable and user didn't provide input, ask again
  108. until [[ -n "$get_public_ip" || -n "$public_ip" ]]; do
  109. echo "Invalid input."
  110. read -p "Public IPv4 address / hostname: " public_ip
  111. done
  112. [[ -z "$public_ip" ]] && public_ip="$get_public_ip"
  113. fi
  114. # If system has a single IPv6, it is selected automatically
  115. if [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then
  116. ip6=$(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}')
  117. fi
  118. # If system has multiple IPv6, ask the user to select one
  119. if [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then
  120. number_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]')
  121. echo
  122. echo "Which IPv6 address should be used?"
  123. 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}' | nl -s ') '
  124. read -p "IPv6 address [1]: " ip6_number
  125. until [[ -z "$ip6_number" || "$ip6_number" =~ ^[0-9]+$ && "$ip6_number" -le "$number_of_ip6" ]]; do
  126. echo "$ip6_number: invalid selection."
  127. read -p "IPv6 address [1]: " ip6_number
  128. done
  129. [[ -z "$ip6_number" ]] && ip6_number="1"
  130. ip6=$(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)
  131. fi
  132. echo
  133. echo "Which protocol should OpenVPN use?"
  134. echo " 1) UDP (recommended)"
  135. echo " 2) TCP"
  136. read -p "Protocol [1]: " protocol
  137. until [[ -z "$protocol" || "$protocol" =~ ^[12]$ ]]; do
  138. echo "$protocol: invalid selection."
  139. read -p "Protocol [1]: " protocol
  140. done
  141. case "$protocol" in
  142. 1|"")
  143. protocol=udp
  144. ;;
  145. 2)
  146. protocol=tcp
  147. ;;
  148. esac
  149. echo
  150. echo "What port should OpenVPN listen on?"
  151. read -p "Port [1194]: " port
  152. until [[ -z "$port" || "$port" =~ ^[0-9]+$ && "$port" -le 65535 ]]; do
  153. echo "$port: invalid port."
  154. read -p "Port [1194]: " port
  155. done
  156. [[ -z "$port" ]] && port="1194"
  157. echo
  158. echo "Select a DNS server for the clients:"
  159. echo " 1) Default system resolvers"
  160. echo " 2) Google"
  161. echo " 3) 1.1.1.1"
  162. echo " 4) OpenDNS"
  163. echo " 5) Quad9"
  164. echo " 6) Gcore"
  165. echo " 7) AdGuard"
  166. echo " 8) Specify custom resolvers"
  167. read -p "DNS server [1]: " dns
  168. until [[ -z "$dns" || "$dns" =~ ^[1-8]$ ]]; do
  169. echo "$dns: invalid selection."
  170. read -p "DNS server [1]: " dns
  171. done
  172. # If the user selected custom resolvers, we deal with that here
  173. if [[ "$dns" = "8" ]]; then
  174. echo
  175. until [[ -n "$custom_dns" ]]; do
  176. echo "Enter DNS servers (one or more IPv4 addresses, separated by commas or spaces):"
  177. read -p "DNS servers: " dns_input
  178. # Convert comma delimited to space delimited
  179. dns_input=$(echo "$dns_input" | tr ',' ' ')
  180. # Validate and build custom DNS IP list
  181. for dns_ip in $dns_input; do
  182. if [[ "$dns_ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
  183. if [[ -z "$custom_dns" ]]; then
  184. custom_dns="$dns_ip"
  185. else
  186. custom_dns="$custom_dns $dns_ip"
  187. fi
  188. fi
  189. done
  190. if [ -z "$custom_dns" ]; then
  191. echo "Invalid input."
  192. fi
  193. done
  194. fi
  195. echo
  196. echo "Enter a name for the first client:"
  197. read -p "Name [client]: " unsanitized_client
  198. # Allow a limited set of characters to avoid conflicts
  199. client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
  200. [[ -z "$client" ]] && client="client"
  201. echo
  202. echo "OpenVPN installation is ready to begin."
  203. # Install a firewall if firewalld or iptables are not already available
  204. if ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>/dev/null; then
  205. if [[ "$os" == "centos" || "$os" == "fedora" ]]; then
  206. firewall="firewalld"
  207. # We don't want to silently enable firewalld, so we give a subtle warning
  208. # If the user continues, firewalld will be installed and enabled during setup
  209. echo "firewalld, which is required to manage routing tables, will also be installed."
  210. elif [[ "$os" == "debian" || "$os" == "ubuntu" ]]; then
  211. # iptables is way less invasive than firewalld so no warning is given
  212. firewall="iptables"
  213. fi
  214. fi
  215. read -n1 -r -p "Press any key to continue..."
  216. # If running inside a container, disable LimitNPROC to prevent conflicts
  217. if systemd-detect-virt -cq; then
  218. mkdir /etc/systemd/system/openvpn-server@server.service.d/ 2>/dev/null
  219. echo "[Service]
  220. LimitNPROC=infinity" > /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf
  221. fi
  222. if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then
  223. apt-get update
  224. apt-get install -y --no-install-recommends openvpn openssl ca-certificates $firewall
  225. elif [[ "$os" = "centos" ]]; then
  226. dnf install -y epel-release
  227. dnf install -y openvpn openssl ca-certificates tar $firewall
  228. else
  229. # Else, OS must be Fedora
  230. dnf install -y openvpn openssl ca-certificates tar $firewall
  231. fi
  232. # If firewalld was just installed, enable it
  233. if [[ "$firewall" == "firewalld" ]]; then
  234. systemctl enable --now firewalld.service
  235. fi
  236. # Get easy-rsa
  237. easy_rsa_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.2.5/EasyRSA-3.2.5.tgz'
  238. mkdir -p /etc/openvpn/server/easy-rsa/
  239. { wget -qO- "$easy_rsa_url" 2>/dev/null || curl -sL "$easy_rsa_url" ; } | tar xz -C /etc/openvpn/server/easy-rsa/ --strip-components 1
  240. chown -R root:root /etc/openvpn/server/easy-rsa/
  241. cd /etc/openvpn/server/easy-rsa/
  242. # Create the PKI, set up the CA and create TLS key
  243. ./easyrsa --batch init-pki
  244. ./easyrsa --batch build-ca nopass
  245. ./easyrsa gen-tls-crypt-key
  246. # Create the DH parameters file using the predefined ffdhe2048 group
  247. echo '-----BEGIN DH PARAMETERS-----
  248. MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
  249. +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
  250. 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
  251. YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
  252. 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
  253. ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
  254. -----END DH PARAMETERS-----' > /etc/openvpn/server/dh.pem
  255. # Make easy-rsa aware of our external DH file (prevents a warning)
  256. ln -s /etc/openvpn/server/dh.pem pki/dh.pem
  257. # Create certificates and CRL
  258. ./easyrsa --batch --days=3650 build-server-full server nopass
  259. ./easyrsa --batch --days=3650 build-client-full "$client" nopass
  260. ./easyrsa --batch --days=3650 gen-crl
  261. # Move the stuff we need
  262. cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server
  263. cp pki/private/easyrsa-tls.key /etc/openvpn/server/tc.key
  264. # CRL is read with each client connection, while OpenVPN is dropped to nobody
  265. chown nobody:"$group_name" /etc/openvpn/server/crl.pem
  266. # Without +x in the directory, OpenVPN can't run a stat() on the CRL file
  267. chmod o+x /etc/openvpn/server/
  268. # Generate server.conf
  269. echo "local $ip
  270. port $port
  271. proto $protocol
  272. dev tun
  273. ca ca.crt
  274. cert server.crt
  275. key server.key
  276. dh dh.pem
  277. auth SHA512
  278. tls-crypt tc.key
  279. topology subnet
  280. server 10.8.0.0 255.255.255.0" > /etc/openvpn/server/server.conf
  281. # IPv6
  282. if [[ -z "$ip6" ]]; then
  283. echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server/server.conf
  284. else
  285. echo 'server-ipv6 fddd:1194:1194:1194::/64' >> /etc/openvpn/server/server.conf
  286. echo 'push "redirect-gateway def1 ipv6 bypass-dhcp"' >> /etc/openvpn/server/server.conf
  287. fi
  288. echo 'ifconfig-pool-persist ipp.txt' >> /etc/openvpn/server/server.conf
  289. # DNS
  290. case "$dns" in
  291. 1|"")
  292. # Locate the proper resolv.conf
  293. # Needed for systems running systemd-resolved
  294. if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then
  295. resolv_conf="/etc/resolv.conf"
  296. else
  297. resolv_conf="/run/systemd/resolve/resolv.conf"
  298. fi
  299. # Obtain the resolvers from resolv.conf and use them for OpenVPN
  300. grep -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
  301. echo "push \"dhcp-option DNS $line\"" >> /etc/openvpn/server/server.conf
  302. done
  303. ;;
  304. 2)
  305. echo 'push "dhcp-option DNS 8.8.8.8"' >> /etc/openvpn/server/server.conf
  306. echo 'push "dhcp-option DNS 8.8.4.4"' >> /etc/openvpn/server/server.conf
  307. ;;
  308. 3)
  309. echo 'push "dhcp-option DNS 1.1.1.1"' >> /etc/openvpn/server/server.conf
  310. echo 'push "dhcp-option DNS 1.0.0.1"' >> /etc/openvpn/server/server.conf
  311. ;;
  312. 4)
  313. echo 'push "dhcp-option DNS 208.67.222.222"' >> /etc/openvpn/server/server.conf
  314. echo 'push "dhcp-option DNS 208.67.220.220"' >> /etc/openvpn/server/server.conf
  315. ;;
  316. 5)
  317. echo 'push "dhcp-option DNS 9.9.9.9"' >> /etc/openvpn/server/server.conf
  318. echo 'push "dhcp-option DNS 149.112.112.112"' >> /etc/openvpn/server/server.conf
  319. ;;
  320. 6)
  321. echo 'push "dhcp-option DNS 95.85.95.85"' >> /etc/openvpn/server/server.conf
  322. echo 'push "dhcp-option DNS 2.56.220.2"' >> /etc/openvpn/server/server.conf
  323. ;;
  324. 7)
  325. echo 'push "dhcp-option DNS 94.140.14.14"' >> /etc/openvpn/server/server.conf
  326. echo 'push "dhcp-option DNS 94.140.15.15"' >> /etc/openvpn/server/server.conf
  327. ;;
  328. 8)
  329. for dns_ip in $custom_dns; do
  330. echo "push \"dhcp-option DNS $dns_ip\"" >> /etc/openvpn/server/server.conf
  331. done
  332. ;;
  333. esac
  334. echo 'push "block-outside-dns"' >> /etc/openvpn/server/server.conf
  335. echo "keepalive 10 120
  336. user nobody
  337. group $group_name
  338. persist-key
  339. persist-tun
  340. verb 3
  341. crl-verify crl.pem" >> /etc/openvpn/server/server.conf
  342. if [[ "$protocol" = "udp" ]]; then
  343. echo "explicit-exit-notify" >> /etc/openvpn/server/server.conf
  344. fi
  345. # Enable net.ipv4.ip_forward for the system
  346. echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-openvpn-forward.conf
  347. # Enable without waiting for a reboot or service restart
  348. echo 1 > /proc/sys/net/ipv4/ip_forward
  349. if [[ -n "$ip6" ]]; then
  350. # Enable net.ipv6.conf.all.forwarding for the system
  351. echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.d/99-openvpn-forward.conf
  352. # Enable without waiting for a reboot or service restart
  353. echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
  354. fi
  355. if systemctl is-active --quiet firewalld.service; then
  356. # Using both permanent and not permanent rules to avoid a firewalld
  357. # reload.
  358. # We don't use --add-service=openvpn because that would only work with
  359. # the default port and protocol.
  360. firewall-cmd --add-port="$port"/"$protocol"
  361. firewall-cmd --zone=trusted --add-source=10.8.0.0/24
  362. firewall-cmd --permanent --add-port="$port"/"$protocol"
  363. firewall-cmd --permanent --zone=trusted --add-source=10.8.0.0/24
  364. # Set NAT for the VPN subnet
  365. firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
  366. firewall-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"
  367. if [[ -n "$ip6" ]]; then
  368. firewall-cmd --zone=trusted --add-source=fddd:1194:1194:1194::/64
  369. firewall-cmd --permanent --zone=trusted --add-source=fddd:1194:1194:1194::/64
  370. firewall-cmd --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
  371. firewall-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"
  372. fi
  373. else
  374. # Create a service to set up persistent iptables rules
  375. iptables_path=$(command -v iptables)
  376. ip6tables_path=$(command -v ip6tables)
  377. # nf_tables is not available as standard in OVZ kernels. So use iptables-legacy
  378. # if we are in OVZ, with a nf_tables backend and iptables-legacy is available.
  379. if [[ $(systemd-detect-virt) == "openvz" ]] && readlink -f "$(command -v iptables)" | grep -q "nft" && hash iptables-legacy 2>/dev/null; then
  380. iptables_path=$(command -v iptables-legacy)
  381. ip6tables_path=$(command -v ip6tables-legacy)
  382. fi
  383. echo "[Unit]
  384. After=network-online.target
  385. Wants=network-online.target
  386. [Service]
  387. Type=oneshot
  388. ExecStart=$iptables_path -w 5 -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip
  389. ExecStart=$iptables_path -w 5 -I INPUT -p $protocol --dport $port -j ACCEPT
  390. ExecStart=$iptables_path -w 5 -I FORWARD -s 10.8.0.0/24 -j ACCEPT
  391. ExecStart=$iptables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
  392. ExecStop=$iptables_path -w 5 -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip
  393. ExecStop=$iptables_path -w 5 -D INPUT -p $protocol --dport $port -j ACCEPT
  394. ExecStop=$iptables_path -w 5 -D FORWARD -s 10.8.0.0/24 -j ACCEPT
  395. ExecStop=$iptables_path -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" > /etc/systemd/system/openvpn-iptables.service
  396. if [[ -n "$ip6" ]]; then
  397. echo "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
  398. ExecStart=$ip6tables_path -w 5 -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
  399. ExecStart=$ip6tables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
  400. ExecStop=$ip6tables_path -w 5 -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6
  401. ExecStop=$ip6tables_path -w 5 -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
  402. ExecStop=$ip6tables_path -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" >> /etc/systemd/system/openvpn-iptables.service
  403. fi
  404. echo "RemainAfterExit=yes
  405. [Install]
  406. WantedBy=multi-user.target" >> /etc/systemd/system/openvpn-iptables.service
  407. systemctl enable --now openvpn-iptables.service
  408. fi
  409. # If SELinux is enabled and a custom port was selected, we need this
  410. if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then
  411. # Install semanage if not already present
  412. if ! hash semanage 2>/dev/null; then
  413. dnf install -y policycoreutils-python-utils
  414. fi
  415. semanage port -a -t openvpn_port_t -p "$protocol" "$port"
  416. fi
  417. # If the server is behind NAT, use the correct IP address
  418. [[ -n "$public_ip" ]] && ip="$public_ip"
  419. # client-common.txt is created so we have a template to add further users later
  420. echo "client
  421. dev tun
  422. proto $protocol
  423. remote $ip $port
  424. resolv-retry infinite
  425. nobind
  426. persist-key
  427. persist-tun
  428. remote-cert-tls server
  429. auth SHA512
  430. ignore-unknown-option block-outside-dns
  431. verb 3" > /etc/openvpn/server/client-common.txt
  432. # Enable and start the OpenVPN service
  433. systemctl enable --now openvpn-server@server.service
  434. # Build the $client.ovpn file, stripping comments from easy-rsa in the process
  435. grep -vh '^#' /etc/openvpn/server/client-common.txt /etc/openvpn/server/easy-rsa/pki/inline/private/"$client".inline > "$script_dir"/"$client".ovpn
  436. echo
  437. echo "Finished!"
  438. echo
  439. echo "The client configuration is available in:" "$script_dir"/"$client.ovpn"
  440. echo "New clients can be added by running this script again."
  441. else
  442. clear
  443. echo "OpenVPN is already installed."
  444. echo
  445. echo "Select an option:"
  446. echo " 1) Add a new client"
  447. echo " 2) Revoke an existing client"
  448. echo " 3) Remove OpenVPN"
  449. echo " 4) Exit"
  450. read -p "Option: " option
  451. until [[ "$option" =~ ^[1-4]$ ]]; do
  452. echo "$option: invalid selection."
  453. read -p "Option: " option
  454. done
  455. case "$option" in
  456. 1)
  457. echo
  458. echo "Provide a name for the client:"
  459. read -p "Name: " unsanitized_client
  460. client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
  461. while [[ -z "$client" || -e /etc/openvpn/server/easy-rsa/pki/issued/"$client".crt ]]; do
  462. echo "$client: invalid name."
  463. read -p "Name: " unsanitized_client
  464. client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
  465. done
  466. cd /etc/openvpn/server/easy-rsa/
  467. ./easyrsa --batch --days=3650 build-client-full "$client" nopass
  468. # Build the $client.ovpn file, stripping comments from easy-rsa in the process
  469. grep -vh '^#' /etc/openvpn/server/client-common.txt /etc/openvpn/server/easy-rsa/pki/inline/private/"$client".inline > "$script_dir"/"$client".ovpn
  470. echo
  471. echo "$client added. Configuration available in:" "$script_dir"/"$client.ovpn"
  472. exit
  473. ;;
  474. 2)
  475. # This option could be documented a bit better and maybe even be simplified
  476. # ...but what can I say, I want some sleep too
  477. number_of_clients=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V")
  478. if [[ "$number_of_clients" = 0 ]]; then
  479. echo
  480. echo "There are no existing clients!"
  481. exit
  482. fi
  483. echo
  484. echo "Select the client to revoke:"
  485. tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
  486. read -p "Client: " client_number
  487. until [[ "$client_number" =~ ^[0-9]+$ && "$client_number" -le "$number_of_clients" ]]; do
  488. echo "$client_number: invalid selection."
  489. read -p "Client: " client_number
  490. done
  491. client=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$client_number"p)
  492. echo
  493. read -p "Confirm $client revocation? [y/N]: " revoke
  494. until [[ "$revoke" =~ ^[yYnN]*$ ]]; do
  495. echo "$revoke: invalid selection."
  496. read -p "Confirm $client revocation? [y/N]: " revoke
  497. done
  498. if [[ "$revoke" =~ ^[yY]$ ]]; then
  499. cd /etc/openvpn/server/easy-rsa/
  500. ./easyrsa --batch revoke "$client"
  501. ./easyrsa --batch --days=3650 gen-crl
  502. rm -f /etc/openvpn/server/crl.pem
  503. rm -f /etc/openvpn/server/easy-rsa/pki/reqs/"$client".req
  504. rm -f /etc/openvpn/server/easy-rsa/pki/private/"$client".key
  505. cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem
  506. # CRL is read with each client connection, when OpenVPN is dropped to nobody
  507. chown nobody:"$group_name" /etc/openvpn/server/crl.pem
  508. echo
  509. echo "$client revoked!"
  510. else
  511. echo
  512. echo "$client revocation aborted!"
  513. fi
  514. exit
  515. ;;
  516. 3)
  517. echo
  518. read -p "Confirm OpenVPN removal? [y/N]: " remove
  519. until [[ "$remove" =~ ^[yYnN]*$ ]]; do
  520. echo "$remove: invalid selection."
  521. read -p "Confirm OpenVPN removal? [y/N]: " remove
  522. done
  523. if [[ "$remove" =~ ^[yY]$ ]]; then
  524. port=$(grep '^port ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
  525. protocol=$(grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
  526. if systemctl is-active --quiet firewalld.service; then
  527. ip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\-s 10.8.0.0/24 '"'"'!'"'"' -d 10.8.0.0/24' | grep -oE '[^ ]+$')
  528. # Using both permanent and not permanent rules to avoid a firewalld reload.
  529. firewall-cmd --remove-port="$port"/"$protocol"
  530. firewall-cmd --zone=trusted --remove-source=10.8.0.0/24
  531. firewall-cmd --permanent --remove-port="$port"/"$protocol"
  532. firewall-cmd --permanent --zone=trusted --remove-source=10.8.0.0/24
  533. firewall-cmd --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
  534. firewall-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"
  535. if grep -qs "server-ipv6" /etc/openvpn/server/server.conf; then
  536. ip6=$(firewall-cmd --direct --get-rules ipv6 nat POSTROUTING | grep '\-s fddd:1194:1194:1194::/64 '"'"'!'"'"' -d fddd:1194:1194:1194::/64' | grep -oE '[^ ]+$')
  537. firewall-cmd --zone=trusted --remove-source=fddd:1194:1194:1194::/64
  538. firewall-cmd --permanent --zone=trusted --remove-source=fddd:1194:1194:1194::/64
  539. firewall-cmd --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
  540. firewall-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"
  541. fi
  542. else
  543. systemctl disable --now openvpn-iptables.service
  544. rm -f /etc/systemd/system/openvpn-iptables.service
  545. fi
  546. if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then
  547. semanage port -d -t openvpn_port_t -p "$protocol" "$port"
  548. fi
  549. systemctl disable --now openvpn-server@server.service
  550. rm -f /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf
  551. rm -f /etc/sysctl.d/99-openvpn-forward.conf
  552. if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then
  553. rm -rf /etc/openvpn/server
  554. apt-get remove --purge -y openvpn
  555. else
  556. # Else, OS must be CentOS or Fedora
  557. dnf remove -y openvpn
  558. rm -rf /etc/openvpn/server
  559. fi
  560. echo
  561. echo "OpenVPN removed!"
  562. else
  563. echo
  564. echo "OpenVPN removal aborted!"
  565. fi
  566. exit
  567. ;;
  568. 4)
  569. exit
  570. ;;
  571. esac
  572. fi