The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .editorconfig
├── .github
    ├── FUNDING.yml
    ├── dependabot.yml
    ├── issue_template.md
    ├── linters
    │   └── .markdown-lint.yml
    ├── pull_request_template.md
    └── workflows
    │   ├── lint.yml
    │   └── test.yml
├── FAQ.md
├── LICENSE
├── README.md
└── openvpn-install.sh


/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.sh]
2 | indent_style = tab
3 | indent_size = 4
4 | 


--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: stanislas
2 | custom: https://coindrop.to/stanislas
3 | 


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |   - package-ecosystem: "github-actions"
4 |     directory: "/"
5 |     schedule:
6 |       interval: "monthly"
7 | 


--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
 1 | <!---
 2 | ❗️ Please read ❗️
 3 | ➡️ If you need help with OpenVPN itself, please use the community forums (https://forums.openvpn.net/) or Stack Overflow (https://stackoverflow.com/questions/tagged/openvpn)
 4 | ➡️ For the script, prefer opening a discussion thread for help: https://github.com/angristan/openvpn-install/discussions
 5 | 💡 It helps keep the issue tracker clean and focused on bugs and feature requests.
 6 | 
 7 | 🙏 Please include as much information as possible, and make sure you're running the latest version of the script.
 8 | ✍️ Please state the Linux distribution you're using and its version, as well as the OpenVPN version.
 9 | ✋ For feature requests, remember that this script is meant to be simple and easy to use. If you want to add a lot of options, it's better to fork the project.
10 | --->
11 | 


--------------------------------------------------------------------------------
/.github/linters/.markdown-lint.yml:
--------------------------------------------------------------------------------
1 | { "MD013": null, "MD045": null, "MD040": null, "MD036": null }
2 | 


--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | <!---
2 | ❗️ Please read ❗️
3 | ➡️ Please make sure you've followed the guidelines: https://github.com/angristan/openvpn-install#contributing
4 | ✅ Please make sure your changes are tested and working
5 | 🗣️ Please avoid large PRs, and discuss changes in a GitHub issue first
6 | ✋ If the changes are too big and not in line with the project, they will probably be rejected. Remember that this script is meant to be simple and easy to use.
7 | --->
8 | 


--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
 1 | on: [push, pull_request, workflow_dispatch]
 2 | 
 3 | name: Lint
 4 | 
 5 | jobs:
 6 |   super-linter:
 7 |     runs-on: ubuntu-latest
 8 |     steps:
 9 |       - name: Checkout Code
10 |         uses: actions/checkout@v4
11 |       - name: Lint Code Base
12 |         uses: github/super-linter@v4.1.0
13 |         env:
14 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 | 


--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
 1 | on:
 2 |   push:
 3 |     branches:
 4 |       - master
 5 |       - ci
 6 |   workflow_dispatch:
 7 | 
 8 | name: Test
 9 | jobs:
10 |   install:
11 |     runs-on: ubuntu-latest
12 |     if: github.repository == 'angristan/openvpn-install' && github.actor == 'angristan'
13 |     strategy:
14 |       matrix:
15 |         os-image:
16 |           - debian-11-x64
17 |           - debian-12-x64
18 |           - ubuntu-22-04-x64
19 |           - ubuntu-24-04-x64
20 |           - fedora-40-x64
21 |           - fedora-41-x64
22 |           # - centos-stream-9-x64 # yum oomkill
23 |     steps:
24 |       - uses: actions/checkout@v4
25 | 
26 |       - name: Setup doctl
27 |         uses: digitalocean/action-doctl@v2
28 |         with:
29 |           token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
30 | 
31 |       - name: Create server
32 |         run: doctl compute droplet create openvpn-action-$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-${{ matrix.os-image }} --size s-1vcpu-1gb --image ${{ matrix.os-image }} --region lon1 --enable-ipv6 --ssh-keys be:66:76:61:a8:71:93:aa:e3:19:ba:d8:0d:d2:2d:d4 --wait
33 | 
34 |       - name: Get server ID
35 |         run: echo ::set-output name=value::$(doctl compute droplet list -o json | jq -r '.[] | select(.name == "'openvpn-action-$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-${{ matrix.os-image }}'").id')
36 |         id: server_id
37 | 
38 |       - name: Move server to dedicated project
39 |         run: doctl projects resources assign ${{ secrets.DIGITALOCEAN_PROJECT_ID }} --resource=do:droplet:${{ steps.server_id.outputs.value }}
40 | 
41 |       - name: Wait for server to boot
42 |         run: sleep 90
43 | 
44 |       - name: Get server IP
45 |         run: echo ::set-output name=value::$(doctl compute droplet list -o json | jq -r '.[] | select(.name == "'openvpn-action-$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-${{ matrix.os-image }}'").networks.v4 | .[] | select(.type == "'public'").ip_address')
46 |         id: server_ip
47 | 
48 |       - name: Get server OS
49 |         run: echo ::set-output name=value::$(echo ${{ matrix.os-image }} | cut -d '-' -f1)
50 |         id: server_os
51 | 
52 |       - name: Setup remote server (Debian/Ubuntu)
53 |         if: steps.server_os.outputs.value == 'debian' || steps.server_os.outputs.value == 'ubuntu'
54 |         uses: appleboy/ssh-action@v0.1.6
55 |         with:
56 |           host: ${{ steps.server_ip.outputs.value }}
57 |           username: root
58 |           key: ${{ secrets.SSH_KEY }}
59 |           script: set -x && apt-get update && apt-get -o DPkg::Lock::Timeout=120 install -y git
60 | 
61 |       - name: Setup remote server (Fedora)
62 |         if: steps.server_os.outputs.value == 'fedora'
63 |         uses: appleboy/ssh-action@v0.1.6
64 |         with:
65 |           host: ${{ steps.server_ip.outputs.value }}
66 |           username: root
67 |           key: ${{ secrets.SSH_KEY }}
68 |           script: set -x && dnf install -y git
69 | 
70 |       - name: Setup remote server (CentOS)
71 |         if: steps.server_os.outputs.value == 'centos'
72 |         uses: appleboy/ssh-action@v0.1.6
73 |         with:
74 |           host: ${{ steps.server_ip.outputs.value }}
75 |           username: root
76 |           key: ${{ secrets.SSH_KEY }}
77 |           script: set -x && yum install -y git
78 | 
79 |       - name: Download repo and checkout current commit
80 |         uses: appleboy/ssh-action@v0.1.6
81 |         with:
82 |           host: ${{ steps.server_ip.outputs.value }}
83 |           username: root
84 |           key: ${{ secrets.SSH_KEY }}
85 |           script: set -x && git clone https://github.com/angristan/openvpn-install.git && cd openvpn-install && git checkout ${{ github.sha }}
86 | 
87 |       - name: Run openvpn-install.sh in headless mode
88 |         uses: appleboy/ssh-action@v0.1.6
89 |         with:
90 |           host: ${{ steps.server_ip.outputs.value }}
91 |           username: root
92 |           key: ${{ secrets.SSH_KEY }}
93 |           script: 'set -x && AUTO_INSTALL=y bash -x ~/openvpn-install/openvpn-install.sh && ps aux | grep openvpn | grep -v grep > /dev/null 2>&1 && echo "Success: OpenVPN is running" && exit 0 || echo "Failure: OpenVPN is not running" && exit 1'
94 | 
95 |       - name: Delete server
96 |         run: doctl compute droplet delete -f openvpn-action-$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-${{ matrix.os-image }}
97 |         if: always()
98 | 


--------------------------------------------------------------------------------
/FAQ.md:
--------------------------------------------------------------------------------
  1 | # FAQ
  2 | 
  3 | **Q:** The script has been updated since I installed OpenVPN. How do I update?
  4 | 
  5 | **A:** You can't. Managing updates and new features from the script would require way too much work. Your only solution is to uninstall OpenVPN and reinstall with the updated script.
  6 | 
  7 | You can, of course, it's even recommended, update the `openvpn` package with your package manager.
  8 | 
  9 | ---
 10 | 
 11 | **Q:** How do I check for DNS leaks?
 12 | 
 13 | **A:** Go to [browserleaks.com](https://browserleaks.com/dns) or [ipleak.net](https://ipleak.net/) (both perform IPv4 and IPv6 check) with your browser. Your IP should not show up (test without and without the VPN). The DNS servers should be the ones you selected during the setup, not your IP address nor your ISP's DNS servers' addresses.
 14 | 
 15 | ---
 16 | 
 17 | **Q:** How do I fix DNS leaks?
 18 | 
 19 | **A:** On Windows 10 DNS leaks are blocked by default with the `block-outside-dns` option.
 20 | On Linux you need to add these lines to your `.ovpn` file based on your Distribution.
 21 | 
 22 | Debian 9, 10 and Ubuntu 16.04, 18.04
 23 | 
 24 | ```
 25 | script-security 2
 26 | up /etc/openvpn/update-resolv-conf
 27 | down /etc/openvpn/update-resolv-conf
 28 | ```
 29 | 
 30 | Centos 6, 7
 31 | 
 32 | ```
 33 | script-security 2
 34 | up /usr/share/doc/openvpn-2.4.8/contrib/pull-resolv-conf/client.up
 35 | down /usr/share/doc/openvpn-2.4.8/contrib/pull-resolv-conf/client.down
 36 | ```
 37 | 
 38 | Centos 8, Fedora 30, 31
 39 | 
 40 | ```
 41 | script-security 2
 42 | up /usr/share/doc/openvpn/contrib/pull-resolv-conf/client.up
 43 | down /usr/share/doc/openvpn/contrib/pull-resolv-conf/client.down
 44 | ```
 45 | 
 46 | Arch Linux
 47 | 
 48 | ```
 49 | script-security 2
 50 | up /usr/share/openvpn/contrib/pull-resolv-conf/client.up
 51 | down /usr/share/openvpn/contrib/pull-resolv-conf/client.down
 52 | ```
 53 | 
 54 | ---
 55 | 
 56 | **Q:** Can I use an OpenVPN 2.3 client?
 57 | 
 58 | **A:** Yes. I really recommend using an up-to-date client, but if you really need it, choose the following options:
 59 | 
 60 | - No compression or LZ0
 61 | - RSA certificate
 62 | - DH Key
 63 | - AES CBC
 64 | - tls-auth
 65 | 
 66 | If your client is <2.3.3, remove `tls-version-min 1.2` from your `/etc/openvpn/server.conf` and `.ovpn` files.
 67 | 
 68 | ---
 69 | 
 70 | **Q:** IPv6 is not working on my Hetzner VM
 71 | 
 72 | **A:** This an issue on their side. See <https://angristan.xyz/fix-ipv6-hetzner-cloud/>
 73 | 
 74 | ---
 75 | 
 76 | **Q:** DNS is not working on my Linux client
 77 | 
 78 | **A:** See "How do I fix DNS leaks?" question
 79 | 
 80 | ---
 81 | 
 82 | **Q:** What syctl and iptables changes are made by the script?
 83 | 
 84 | **A:** Iptables rules are saved at `/etc/iptables/add-openvpn-rules.sh` and `/etc/iptables/rm-openvpn-rules.sh`. They are managed by the service `/etc/systemd/system/iptables-openvpn.service`
 85 | 
 86 | Sysctl options are at `/etc/sysctl.d/20-openvpn.conf`
 87 | 
 88 | ---
 89 | 
 90 | **Q:** How can I access other clients connected to the same OpenVPN server?
 91 | 
 92 | **A:** Add `client-to-client` to your `server.conf`
 93 | 
 94 | ---
 95 | 
 96 | **Q:** My router can't connect
 97 | 
 98 | **A:**
 99 | 
100 | - `Options error: No closing quotation (") in config.ovpn:46` :
101 | 
102 |   type `yes` when asked to customize encryption settings and choose `tls-auth`
103 | 
104 | - `Options error: Unrecognized option or missing parameter(s) in config.ovpn:36: tls-version-min (2.3.2)` :
105 | 
106 |   see question "Can I use an OpenVPN 2.3 client?"
107 | 
108 | ---
109 | 
110 | **Q:** How can I access computers the OpenVPN server's remote LAN?
111 | 
112 | **A:** Add a route with the subnet of the remote network to `/etc/openvpn/server.conf` and restart openvpn. Example: `push "route 192.168.1.0 255.255.255.0"` if the server's LAN is `192.168.1.0/24`
113 | 
114 | ---
115 | 
116 | **Q:** How can I add multiple users in one go?
117 | 
118 | **A:** Here is a sample bash script to achieve this:
119 | 
120 | ```sh
121 | userlist=(user1 user2 user3)
122 | 
123 | for i in ${userlist[@]};do
124 |    MENU_OPTION=1 CLIENT=$i PASS=1 ./openvpn-install.sh
125 | done
126 | ```
127 | 
128 | From a list in a text file:
129 | 
130 | ```sh
131 | while read USER
132 |     do MENU_OPTION="1" CLIENT="$USER" PASS="1" ./openvpn-install.sh
133 | done < users.txt
134 | ```
135 | 
136 | ---
137 | 
138 | **Q:** How do I change the default `.ovpn` file created for future clients?
139 | 
140 | **A:** You can edit the template out of which `.ovpn` files are created by editing `/etc/openvpn/client-template.txt`
141 | 
142 | ---
143 | 
144 | **Q:** For my clients - I want to set my internal network to pass through the VPN and the rest to go through my internet?
145 | 
146 | **A:** You would need to edit the `.ovpn` file. You can edit the template out of which those files are created by editing `/etc/openvpn/client-template.txt` file and adding
147 | 
148 | ```sh
149 | route-nopull
150 | route 10.0.0.0 255.0.0.0
151 | ```
152 | 
153 | So for example - here it would route all traffic of `10.0.0.0/8` to the vpn. And the rest through the internet.
154 | 
155 | ---
156 | 
157 | **Q:** I have enabled IPv6 and my VPN client gets an IPv6 address. Why do I reach the websites or other dual-stacked destionations via IPv4 only?
158 | 
159 | **A:** This is because inside the tunnel you don't get a publicly routable IPv6 address, instead you get an ULA (Unlique Local Lan) address. Operating systems don't prefer this all the time. You can fix this in your operating system policies as it's unrelated to the VPN itself:
160 | 
161 | Windows (commands needs to run cmd.exe as Administrator):
162 | 
163 | ```
164 | netsh interface ipv6 add prefixpolicy fd00::/8 3 1
165 | ```
166 | 
167 | Linux:
168 | 
169 | edit `/etc/gai.conf` and uncomment the following line and also change its value to `1`:
170 | 
171 | ```
172 | label fc00::/7      1
173 | ```
174 | 
175 | This will not work properly unless you add you your VPN server `server.conf` one or two lines to push at least 1 (one) IPv6 DNS server. Most providers have IPv6 servers as well, add two more lines of `push "dhcp-option DNS <IPv6>"`
176 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2013 Nyr
 4 | Copyright (c) 2016 Stanislas Lange (angristan)
 5 | 
 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
 7 | this software and associated documentation files (the "Software"), to deal in
 8 | the Software without restriction, including without limitation the rights to
 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 | 
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 | 
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # openvpn-install
  2 | 
  3 | ![Test](https://github.com/angristan/openvpn-install/workflows/Test/badge.svg)
  4 | ![Lint](https://github.com/angristan/openvpn-install/workflows/Lint/badge.svg)
  5 | [![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/angristan)
  6 | 
  7 | OpenVPN installer for Debian, Ubuntu, Fedora, CentOS, Arch Linux, Oracle Linux, Rocky Linux and AlmaLinux.
  8 | 
  9 | This script will let you setup your own secure VPN server in just a few seconds.
 10 | 
 11 | You can also check out [wireguard-install](https://github.com/angristan/wireguard-install), a simple installer for a simpler, safer, faster and more modern VPN protocol.
 12 | 
 13 | ## What is this?
 14 | 
 15 | This script is meant to be run on your own server, whether it's a VPS or a dedicated server, or even a computer at home.
 16 | 
 17 | Once set up, you will be able to generate client configuration files for every device you want to connect.
 18 | 
 19 | Each client will be able to route its internet traffic through the server, fully encrypted.
 20 | 
 21 | ```mermaid
 22 | graph LR
 23 |   A[Phone] -->|Encrypted| VPN
 24 |   B[Laptop] -->|Encrypted| VPN
 25 |   C[Computer] -->|Encrypted| VPN
 26 | 
 27 |   VPN[OpenVPN Server]
 28 | 
 29 |   VPN --> I[Internet]
 30 | ```
 31 | 
 32 | ## Usage
 33 | 
 34 | First, get the script and make it executable:
 35 | 
 36 | ```bash
 37 | curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh
 38 | chmod +x openvpn-install.sh
 39 | ```
 40 | 
 41 | Then run it:
 42 | 
 43 | ```sh
 44 | ./openvpn-install.sh
 45 | ```
 46 | 
 47 | You need to run the script as root and have the TUN module enabled.
 48 | 
 49 | The first time you run it, you'll have to follow the assistant and answer a few questions to setup your VPN server.
 50 | 
 51 | When OpenVPN is installed, you can run the script again, and you will get the choice to:
 52 | 
 53 | - Add a client
 54 | - Remove a client
 55 | - Uninstall OpenVPN
 56 | 
 57 | In your home directory, you will have `.ovpn` files. These are the client configuration files. Download them from your server and connect using your favorite OpenVPN client.
 58 | 
 59 | If you have any question, head to the [FAQ](#faq) first. And if you need help, you can open a [discussion](https://github.com/angristan/openvpn-install/discussions). Please search existing issues and dicussions first.
 60 | 
 61 | ### Headless install
 62 | 
 63 | It's also possible to run the script headless, e.g. without waiting for user input, in an automated manner.
 64 | 
 65 | Example usage:
 66 | 
 67 | ```bash
 68 | AUTO_INSTALL=y ./openvpn-install.sh
 69 | 
 70 | # or
 71 | 
 72 | export AUTO_INSTALL=y
 73 | ./openvpn-install.sh
 74 | ```
 75 | 
 76 | A default set of variables will then be set, by passing the need for user input.
 77 | 
 78 | If you want to customise your installation, you can export them or specify them on the same line, as shown above.
 79 | 
 80 | - `APPROVE_INSTALL=y`
 81 | - `APPROVE_IP=y`
 82 | - `IPV6_SUPPORT=n`
 83 | - `PORT_CHOICE=1`
 84 | - `PROTOCOL_CHOICE=1`
 85 | - `DNS=1`
 86 | - `COMPRESSION_ENABLED=n`
 87 | - `CUSTOMIZE_ENC=n`
 88 | - `CLIENT=clientname`
 89 | - `PASS=1`
 90 | 
 91 | If the server is behind NAT, you can specify its endpoint with the `ENDPOINT` variable. If the endpoint is the public IP address which it is behind, you can use `ENDPOINT=$(curl -4 ifconfig.co)` (the script will default to this). The endpoint can be an IPv4 or a domain.
 92 | 
 93 | Other variables can be set depending on your choice (encryption, compression). You can search for them in the `installQuestions()` function of the script.
 94 | 
 95 | Password-protected clients are not supported by the headless installation method since user input is expected by Easy-RSA.
 96 | 
 97 | The headless install is more-or-less idempotent, in that it has been made safe to run multiple times with the same parameters, e.g. by a state provisioner like Ansible/Terraform/Salt/Chef/Puppet. It will only install and regenerate the Easy-RSA PKI if it doesn't already exist, and it will only install OpenVPN and other upstream dependencies if OpenVPN isn't already installed. It will recreate all local config and re-generate the client file on each headless run.
 98 | 
 99 | ### Headless User Addition
100 | 
101 | It's also possible to automate the addition of a new user. Here, the key is to provide the (string) value of the `MENU_OPTION` variable along with the remaining mandatory variables before invoking the script.
102 | 
103 | The following Bash script adds a new user `foo` to an existing OpenVPN configuration
104 | 
105 | ```bash
106 | #!/bin/bash
107 | export MENU_OPTION="1"
108 | export CLIENT="foo"
109 | export PASS="1"
110 | ./openvpn-install.sh
111 | ```
112 | 
113 | ## Features
114 | 
115 | - Installs and configures a ready-to-use OpenVPN server
116 | - Iptables rules and forwarding managed in a seamless way
117 | - If needed, the script can cleanly remove OpenVPN, including configuration and iptables rules
118 | - Customisable encryption settings, enhanced default settings (see [Security and Encryption](#security-and-encryption) below)
119 | - OpenVPN 2.4 features, mainly encryption improvements (see [Security and Encryption](#security-and-encryption) below)
120 | - Variety of DNS resolvers to be pushed to the clients
121 | - Choice to use a self-hosted resolver with Unbound (supports already existing Unbound installations)
122 | - Choice between TCP and UDP
123 | - NATed IPv6 support
124 | - Compression disabled by default to prevent VORACLE. LZ4 (v1/v2) and LZ0 algorithms available otherwise.
125 | - Unprivileged mode: run as `nobody`/`nogroup`
126 | - Block DNS leaks on Windows 10
127 | - Randomised server certificate name
128 | - Choice to protect clients with a password (private key encryption)
129 | - Many other little things!
130 | 
131 | ## Compatibility
132 | 
133 | The script supports these Linux distributions:
134 | 
135 | |                        | Support |
136 | | ---------------------- | ------- |
137 | | AlmaLinux 8            | ✅      |
138 | | Amazon Linux 2         | ✅      |
139 | | Amazon Linux >= 2023.6 | ✅      |
140 | | Arch Linux             | ✅      |
141 | | CentOS 7               | ✅      |
142 | | CentOS Stream >= 8     | ✅ 🤖   |
143 | | Debian >= 10           | ✅ 🤖   |
144 | | Fedora >= 35           | ✅ 🤖   |
145 | | Oracle Linux 8         | ✅      |
146 | | Rocky Linux 8          | ✅      |
147 | | Ubuntu >= 18.04        | ✅ 🤖   |
148 | 
149 | To be noted:
150 | 
151 | - The script is regularly tested against the distributions marked with a 🤖 only.
152 |   - It's only tested on `amd64` architecture.
153 | - It should work on older versions such as Debian 8+, Ubuntu 16.04+ and previous Fedora releases. But versions not in the table above are not officially supported.
154 |   - It should also support versions between the LTS versions, but these are not tested.
155 | - The script requires `systemd`.
156 | 
157 | ## Fork
158 | 
159 | This script is based on the great work of [Nyr and its contributors](https://github.com/Nyr/openvpn-install).
160 | 
161 | Since 2016, the two scripts have diverged and are not alike anymore, especially under the hood. The main goal of the script was enhanced security. But since then, the script has been completely rewritten and a lot a features have been added. The script is only compatible with recent distributions though, so if you need to use a very old server or client, I advise using Nyr's script.
162 | 
163 | ## FAQ
164 | 
165 | More Q&A in [FAQ.md](FAQ.md).
166 | 
167 | **Q:** Which provider do you recommend?
168 | 
169 | **A:** I recommend these:
170 | 
171 | - [Vultr](https://www.vultr.com/?ref=8948982-8H): Worldwide locations, IPv6 support, starting at \$5/month
172 | - [Hetzner](https://hetzner.cloud/?ref=ywtlvZsjgeDq): Germany, Finland and USA. IPv6, 20 TB of traffic, starting at 4.5€/month
173 | - [Digital Ocean](https://m.do.co/c/ed0ba143fe53): Worldwide locations, IPv6 support, starting at \$4/month
174 | 
175 | ---
176 | 
177 | **Q:** Which OpenVPN client do you recommend?
178 | 
179 | **A:** If possible, an official OpenVPN 2.4 client.
180 | 
181 | - Windows: [The official OpenVPN community client](https://openvpn.net/index.php/download/community-downloads.html).
182 | - Linux: The `openvpn` package from your distribution. There is an [official APT repository](https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos) for Debian/Ubuntu based distributions.
183 | - macOS: [Tunnelblick](https://tunnelblick.net/), [Viscosity](https://www.sparklabs.com/viscosity/), [OpenVPN for Mac](https://openvpn.net/client-connect-vpn-for-mac-os/).
184 | - Android: [OpenVPN for Android](https://play.google.com/store/apps/details?id=de.blinkt.openvpn).
185 | - iOS: [The official OpenVPN Connect client](https://itunes.apple.com/us/app/openvpn-connect/id590379981).
186 | 
187 | ---
188 | 
189 | **Q:** Am I safe from the NSA by using your script?
190 | 
191 | **A:** Please review your threat models. Even if this script has security in mind and uses state-of-the-art encryption, you shouldn't be using a VPN if you want to hide from the NSA.
192 | 
193 | ---
194 | 
195 | **Q:** Is there an OpenVPN documentation?
196 | 
197 | **A:** Yes, please head to the [OpenVPN Manual](https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage), which references all the options.
198 | 
199 | ---
200 | 
201 | More Q&A in [FAQ.md](FAQ.md).
202 | 
203 | ## One-stop solutions for public cloud
204 | 
205 | Solutions that provision a ready to use OpenVPN server based on this script in one go are available for:
206 | 
207 | - AWS using Terraform at [`openvpn-terraform-install`](https://github.com/dumrauf/openvpn-terraform-install)
208 | - Terraform AWS module [`openvpn-ephemeral`](https://registry.terraform.io/modules/paulmarsicloud/openvpn-ephemeral/aws/latest)
209 | 
210 | ## Contributing
211 | 
212 | ## Discuss changes
213 | 
214 | Please open an issue before submitting a PR if you want to discuss a change, especially if it's a big one.
215 | 
216 | ### Code formatting
217 | 
218 | We use [shellcheck](https://github.com/koalaman/shellcheck) and [shfmt](https://github.com/mvdan/sh) to enforce bash styling guidelines and good practices. They are executed for each commit / PR with GitHub Actions, so you can check the configuration [here](https://github.com/angristan/openvpn-install/blob/master/.github/workflows/push.yml).
219 | 
220 | ## Security and Encryption
221 | 
222 | > **Warning**
223 | > This has not been updated for OpenVPN 2.5 and later.
224 | 
225 | OpenVPN's default settings are pretty weak regarding encryption. This script aims to improve that.
226 | 
227 | OpenVPN 2.4 was a great update regarding encryption. It added support for ECDSA, ECDH, AES GCM, NCP and tls-crypt.
228 | 
229 | If you want more information about an option mentioned below, head to the [OpenVPN manual](https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage). It is very complete.
230 | 
231 | Most of OpenVPN's encryption-related stuff is managed by [Easy-RSA](https://github.com/OpenVPN/easy-rsa). Defaults parameters are in the [vars.example](https://github.com/OpenVPN/easy-rsa/blob/v3.0.7/easyrsa3/vars.example) file.
232 | 
233 | ### Compression
234 | 
235 | By default, OpenVPN doesn't enable compression. This script provides support for LZ0 and LZ4 (v1/v2) algorithms, the latter being more efficient.
236 | 
237 | However, it is discouraged to use compression since the [VORACLE attack](https://protonvpn.com/blog/voracle-attack/) makes use of it.
238 | 
239 | ### TLS version
240 | 
241 | OpenVPN accepts TLS 1.0 by default, which is nearly [20 years old](https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.0).
242 | 
243 | With `tls-version-min 1.2` we enforce TLS 1.2, which the best protocol available currently for OpenVPN.
244 | 
245 | TLS 1.2 is supported since OpenVPN 2.3.3.
246 | 
247 | ### Certificate
248 | 
249 | OpenVPN uses an RSA certificate with a 2048 bits key by default.
250 | 
251 | OpenVPN 2.4 added support for ECDSA. Elliptic curve cryptography is faster, lighter and more secure.
252 | 
253 | This script provides:
254 | 
255 | - ECDSA: `prime256v1`/`secp384r1`/`secp521r1` curves
256 | - RSA: `2048`/`3072`/`4096` bits keys
257 | 
258 | It defaults to ECDSA with `prime256v1`.
259 | 
260 | OpenVPN uses `SHA-256` as the signature hash by default, and so does the script. It provides no other choice as of now.
261 | 
262 | ### Data channel
263 | 
264 | By default, OpenVPN uses `BF-CBC` as the data channel cipher. Blowfish is an old (1993) and weak algorithm. Even the official OpenVPN documentation admits it.
265 | 
266 | > The default is BF-CBC, an abbreviation for Blowfish in Cipher Block Chaining mode.
267 | >
268 | > Using BF-CBC is no longer recommended, because of its 64-bit block size. This small block size allows attacks based on collisions, as demonstrated by SWEET32. See <https://community.openvpn.net/openvpn/wiki/SWEET32> for details.
269 | > Security researchers at INRIA published an attack on 64-bit block ciphers, such as 3DES and Blowfish. They show that they are able to recover plaintext when the same data is sent often enough, and show how they can use cross-site scripting vulnerabilities to send data of interest often enough. This works over HTTPS, but also works for HTTP-over-OpenVPN. See <https://sweet32.info/> for a much better and more elaborate explanation.
270 | >
271 | > OpenVPN's default cipher, BF-CBC, is affected by this attack.
272 | 
273 | Indeed, AES is today's standard. It's the fastest and more secure cipher available today. [SEED](https://en.wikipedia.org/wiki/SEED) and [Camellia](<https://en.wikipedia.org/wiki/Camellia_(cipher)>) are not vulnerable to date but are slower than AES and relatively less trusted.
274 | 
275 | > Of the currently supported ciphers, OpenVPN currently recommends using AES-256-CBC or AES-128-CBC. OpenVPN 2.4 and newer will also support GCM. For 2.4+, we recommend using AES-256-GCM or AES-128-GCM.
276 | 
277 | AES-256 is 40% slower than AES-128, and there isn't any real reason to use a 256 bits key over a 128 bits key with AES. (Source: [1](http://security.stackexchange.com/questions/14068/why-most-people-use-256-bit-encryption-instead-of-128-bit),[2](http://security.stackexchange.com/questions/6141/amount-of-simple-operations-that-is-safely-out-of-reach-for-all-humanity/6149#6149)). Moreover, AES-256 is more vulnerable to [Timing attacks](https://en.wikipedia.org/wiki/Timing_attack).
278 | 
279 | AES-GCM is an [AEAD cipher](https://en.wikipedia.org/wiki/Authenticated_encryption) which means it simultaneously provides confidentiality, integrity, and authenticity assurances on the data.
280 | 
281 | The script supports the following ciphers:
282 | 
283 | - `AES-128-GCM`
284 | - `AES-192-GCM`
285 | - `AES-256-GCM`
286 | - `AES-128-CBC`
287 | - `AES-192-CBC`
288 | - `AES-256-CBC`
289 | 
290 | And defaults to `AES-128-GCM`.
291 | 
292 | OpenVPN 2.4 added a feature called "NCP": _Negotiable Crypto Parameters_. It means you can provide a cipher suite like with HTTPS. It is set to `AES-256-GCM:AES-128-GCM` by default and overrides the `--cipher` parameter when used with an OpenVPN 2.4 client. For the sake of simplicity, the script set both the `--cipher` and `--ncp-cipher` to the cipher chosen above.
293 | 
294 | ### Control channel
295 | 
296 | OpenVPN 2.4 will negotiate the best cipher available by default (e.g ECDHE+AES-256-GCM)
297 | 
298 | The script proposes the following options, depending on the certificate:
299 | 
300 | - ECDSA:
301 |   - `TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256`
302 |   - `TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384`
303 | - RSA:
304 |   - `TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256`
305 |   - `TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384`
306 | 
307 | It defaults to `TLS-ECDHE-*-WITH-AES-128-GCM-SHA256`.
308 | 
309 | ### Diffie-Hellman key exchange
310 | 
311 | OpenVPN uses a 2048 bits DH key by default.
312 | 
313 | OpenVPN 2.4 added support for ECDH keys. Elliptic curve cryptography is faster, lighter and more secure.
314 | 
315 | Also, generating a classic DH keys can take a long, looong time. ECDH keys are ephemeral: they are generated on-the-fly.
316 | 
317 | The script provides the following options:
318 | 
319 | - ECDH: `prime256v1`/`secp384r1`/`secp521r1` curves
320 | - DH: `2048`/`3072`/`4096` bits keys
321 | 
322 | It defaults to `prime256v1`.
323 | 
324 | ### HMAC digest algorithm
325 | 
326 | From the OpenVPN wiki, about `--auth`:
327 | 
328 | > Authenticate data channel packets and (if enabled) tls-auth control channel packets with HMAC using message digest algorithm alg. (The default is SHA1 ). HMAC is a commonly used message authentication algorithm (MAC) that uses a data string, a secure hash algorithm, and a key, to produce a digital signature.
329 | >
330 | > If an AEAD cipher mode (e.g. GCM) is chosen, the specified --auth algorithm is ignored for the data channel, and the authentication method of the AEAD cipher is used instead. Note that alg still specifies the digest used for tls-auth.
331 | 
332 | The script provides the following choices:
333 | 
334 | - `SHA256`
335 | - `SHA384`
336 | - `SHA512`
337 | 
338 | It defaults to `SHA256`.
339 | 
340 | ### `tls-auth` and `tls-crypt`
341 | 
342 | From the OpenVPN wiki, about `tls-auth`:
343 | 
344 | > Add an additional layer of HMAC authentication on top of the TLS control channel to mitigate DoS attacks and attacks on the TLS stack.
345 | >
346 | > In a nutshell, --tls-auth enables a kind of "HMAC firewall" on OpenVPN's TCP/UDP port, where TLS control channel packets bearing an incorrect HMAC signature can be dropped immediately without response.
347 | 
348 | About `tls-crypt`:
349 | 
350 | > Encrypt and authenticate all control channel packets with the key from keyfile. (See --tls-auth for more background.)
351 | >
352 | > Encrypting (and authenticating) control channel packets:
353 | >
354 | > - provides more privacy by hiding the certificate used for the TLS connection,
355 | > - makes it harder to identify OpenVPN traffic as such,
356 | > - provides "poor-man's" post-quantum security, against attackers who will never know the pre-shared key (i.e. no forward secrecy).
357 | 
358 | So both provide an additional layer of security and mitigate DoS attacks. They aren't used by default by OpenVPN.
359 | 
360 | `tls-crypt` is an OpenVPN 2.4 feature that provides encryption in addition to authentication (unlike `tls-auth`). It is more privacy-friendly.
361 | 
362 | The script supports both and uses `tls-crypt` by default.
363 | 
364 | ## Say thanks
365 | 
366 | You can [say thanks](https://saythanks.io/to/angristan) if you want!
367 | 
368 | ## Credits & Licence
369 | 
370 | Many thanks to the [contributors](https://github.com/Angristan/OpenVPN-install/graphs/contributors) and Nyr's original work.
371 | 
372 | This project is under the [MIT Licence](https://raw.githubusercontent.com/Angristan/openvpn-install/master/LICENSE)
373 | 
374 | ## Star History
375 | 
376 | [![Star History Chart](https://api.star-history.com/svg?repos=angristan/openvpn-install&type=Date)](https://star-history.com/#angristan/openvpn-install&Date)
377 | 


--------------------------------------------------------------------------------
/openvpn-install.sh:
--------------------------------------------------------------------------------
   1 | #!/bin/bash
   2 | # shellcheck disable=SC1091,SC2164,SC2034,SC1072,SC1073,SC1009
   3 | 
   4 | # Secure OpenVPN server installer for Debian, Ubuntu, CentOS, Amazon Linux 2, Fedora, Oracle Linux 8, Arch Linux, Rocky Linux and AlmaLinux.
   5 | # https://github.com/angristan/openvpn-install
   6 | 
   7 | function isRoot() {
   8 | 	if [ "$EUID" -ne 0 ]; then
   9 | 		return 1
  10 | 	fi
  11 | }
  12 | 
  13 | function tunAvailable() {
  14 | 	if [ ! -e /dev/net/tun ]; then
  15 | 		return 1
  16 | 	fi
  17 | }
  18 | 
  19 | function checkOS() {
  20 | 	if [[ -e /etc/debian_version ]]; then
  21 | 		OS="debian"
  22 | 		source /etc/os-release
  23 | 
  24 | 		if [[ $ID == "debian" || $ID == "raspbian" ]]; then
  25 | 			if [[ $VERSION_ID -lt 9 ]]; then
  26 | 				echo "⚠️ Your version of Debian is not supported."
  27 | 				echo ""
  28 | 				echo "However, if you're using Debian >= 9 or unstable/testing, you can continue at your own risk."
  29 | 				echo ""
  30 | 				until [[ $CONTINUE =~ (y|n) ]]; do
  31 | 					read -rp "Continue? [y/n]: " -e CONTINUE
  32 | 				done
  33 | 				if [[ $CONTINUE == "n" ]]; then
  34 | 					exit 1
  35 | 				fi
  36 | 			fi
  37 | 		elif [[ $ID == "ubuntu" ]]; then
  38 | 			OS="ubuntu"
  39 | 			MAJOR_UBUNTU_VERSION=$(echo "$VERSION_ID" | cut -d '.' -f1)
  40 | 			if [[ $MAJOR_UBUNTU_VERSION -lt 16 ]]; then
  41 | 				echo "⚠️ Your version of Ubuntu is not supported."
  42 | 				echo ""
  43 | 				echo "However, if you're using Ubuntu >= 16.04 or beta, you can continue at your own risk."
  44 | 				echo ""
  45 | 				until [[ $CONTINUE =~ (y|n) ]]; do
  46 | 					read -rp "Continue? [y/n]: " -e CONTINUE
  47 | 				done
  48 | 				if [[ $CONTINUE == "n" ]]; then
  49 | 					exit 1
  50 | 				fi
  51 | 			fi
  52 | 		fi
  53 | 	elif [[ -e /etc/system-release ]]; then
  54 | 		source /etc/os-release
  55 | 		if [[ $ID == "fedora" || $ID_LIKE == "fedora" ]]; then
  56 | 			OS="fedora"
  57 | 		fi
  58 | 		if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]]; then
  59 | 			OS="centos"
  60 | 			if [[ ${VERSION_ID%.*} -lt 7 ]]; then
  61 | 				echo "⚠️ Your version of CentOS is not supported."
  62 | 				echo ""
  63 | 				echo "The script only supports CentOS 7 and CentOS 8."
  64 | 				echo ""
  65 | 				exit 1
  66 | 			fi
  67 | 		fi
  68 | 		if [[ $ID == "ol" ]]; then
  69 | 			OS="oracle"
  70 | 			if [[ ! $VERSION_ID =~ (8) ]]; then
  71 | 				echo "Your version of Oracle Linux is not supported."
  72 | 				echo ""
  73 | 				echo "The script only supports Oracle Linux 8."
  74 | 				exit 1
  75 | 			fi
  76 | 		fi
  77 | 		if [[ $ID == "amzn" ]]; then
  78 | 			if [[ $VERSION_ID == "2" ]]; then
  79 | 				OS="amzn"
  80 | 			elif [[ "$(echo "$PRETTY_NAME" | cut -c 1-18)" == "Amazon Linux 2023." ]] && [[ "$(echo "$PRETTY_NAME" | cut -c 19)" -ge 6 ]]; then
  81 | 				OS="amzn2023"
  82 | 			else
  83 | 				echo "⚠️ Your version of Amazon Linux is not supported."
  84 | 				echo ""
  85 | 				echo "The script only supports Amazon Linux 2 or Amazon Linux 2023.6+"
  86 | 				echo ""
  87 | 				exit 1
  88 | 			fi
  89 | 		fi
  90 | 	elif [[ -e /etc/arch-release ]]; then
  91 | 		OS=arch
  92 | 	else
  93 | 		echo "It looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS, Amazon Linux 2, Oracle Linux 8 or Arch Linux system."
  94 | 		exit 1
  95 | 	fi
  96 | }
  97 | 
  98 | function initialCheck() {
  99 | 	if ! isRoot; then
 100 | 		echo "Sorry, you need to run this script as root."
 101 | 		exit 1
 102 | 	fi
 103 | 	if ! tunAvailable; then
 104 | 		echo "TUN is not available."
 105 | 		exit 1
 106 | 	fi
 107 | 	checkOS
 108 | }
 109 | 
 110 | function installUnbound() {
 111 | 	# If Unbound isn't installed, install it
 112 | 	if [[ ! -e /etc/unbound/unbound.conf ]]; then
 113 | 
 114 | 		if [[ $OS =~ (debian|ubuntu) ]]; then
 115 | 			apt-get install -y unbound
 116 | 
 117 | 			# Configuration
 118 | 			echo 'interface: 10.8.0.1
 119 | access-control: 10.8.0.1/24 allow
 120 | hide-identity: yes
 121 | hide-version: yes
 122 | use-caps-for-id: yes
 123 | prefetch: yes' >>/etc/unbound/unbound.conf
 124 | 
 125 | 		elif [[ $OS =~ (centos|amzn|oracle) ]]; then
 126 | 			yum install -y unbound
 127 | 
 128 | 			# Configuration
 129 | 			sed -i 's|# interface: 0.0.0.0$|interface: 10.8.0.1|' /etc/unbound/unbound.conf
 130 | 			sed -i 's|# access-control: 127.0.0.0/8 allow|access-control: 10.8.0.1/24 allow|' /etc/unbound/unbound.conf
 131 | 			sed -i 's|# hide-identity: no|hide-identity: yes|' /etc/unbound/unbound.conf
 132 | 			sed -i 's|# hide-version: no|hide-version: yes|' /etc/unbound/unbound.conf
 133 | 			sed -i 's|use-caps-for-id: no|use-caps-for-id: yes|' /etc/unbound/unbound.conf
 134 | 
 135 | 		elif [[ $OS == "fedora" ]]; then
 136 | 			dnf install -y unbound
 137 | 
 138 | 			# Configuration
 139 | 			sed -i 's|# interface: 0.0.0.0$|interface: 10.8.0.1|' /etc/unbound/unbound.conf
 140 | 			sed -i 's|# access-control: 127.0.0.0/8 allow|access-control: 10.8.0.1/24 allow|' /etc/unbound/unbound.conf
 141 | 			sed -i 's|# hide-identity: no|hide-identity: yes|' /etc/unbound/unbound.conf
 142 | 			sed -i 's|# hide-version: no|hide-version: yes|' /etc/unbound/unbound.conf
 143 | 			sed -i 's|# use-caps-for-id: no|use-caps-for-id: yes|' /etc/unbound/unbound.conf
 144 | 
 145 | 		elif [[ $OS == "arch" ]]; then
 146 | 			pacman -Syu --noconfirm unbound
 147 | 
 148 | 			# Get root servers list
 149 | 			curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
 150 | 
 151 | 			if [[ ! -f /etc/unbound/unbound.conf.old ]]; then
 152 | 				mv /etc/unbound/unbound.conf /etc/unbound/unbound.conf.old
 153 | 			fi
 154 | 
 155 | 			echo 'server:
 156 | 	use-syslog: yes
 157 | 	do-daemonize: no
 158 | 	username: "unbound"
 159 | 	directory: "/etc/unbound"
 160 | 	trust-anchor-file: trusted-key.key
 161 | 	root-hints: root.hints
 162 | 	interface: 10.8.0.1
 163 | 	access-control: 10.8.0.1/24 allow
 164 | 	port: 53
 165 | 	num-threads: 2
 166 | 	use-caps-for-id: yes
 167 | 	harden-glue: yes
 168 | 	hide-identity: yes
 169 | 	hide-version: yes
 170 | 	qname-minimisation: yes
 171 | 	prefetch: yes' >/etc/unbound/unbound.conf
 172 | 		fi
 173 | 
 174 | 		# IPv6 DNS for all OS
 175 | 		if [[ $IPV6_SUPPORT == 'y' ]]; then
 176 | 			echo 'interface: fd42:42:42:42::1
 177 | access-control: fd42:42:42:42::/112 allow' >>/etc/unbound/unbound.conf
 178 | 		fi
 179 | 
 180 | 		if [[ ! $OS =~ (fedora|centos|amzn|oracle) ]]; then
 181 | 			# DNS Rebinding fix
 182 | 			echo "private-address: 10.0.0.0/8
 183 | private-address: fd42:42:42:42::/112
 184 | private-address: 172.16.0.0/12
 185 | private-address: 192.168.0.0/16
 186 | private-address: 169.254.0.0/16
 187 | private-address: fd00::/8
 188 | private-address: fe80::/10
 189 | private-address: 127.0.0.0/8
 190 | private-address: ::ffff:0:0/96" >>/etc/unbound/unbound.conf
 191 | 		fi
 192 | 	else # Unbound is already installed
 193 | 		echo 'include: /etc/unbound/openvpn.conf' >>/etc/unbound/unbound.conf
 194 | 
 195 | 		# Add Unbound 'server' for the OpenVPN subnet
 196 | 		echo 'server:
 197 | interface: 10.8.0.1
 198 | access-control: 10.8.0.1/24 allow
 199 | hide-identity: yes
 200 | hide-version: yes
 201 | use-caps-for-id: yes
 202 | prefetch: yes
 203 | private-address: 10.0.0.0/8
 204 | private-address: fd42:42:42:42::/112
 205 | private-address: 172.16.0.0/12
 206 | private-address: 192.168.0.0/16
 207 | private-address: 169.254.0.0/16
 208 | private-address: fd00::/8
 209 | private-address: fe80::/10
 210 | private-address: 127.0.0.0/8
 211 | private-address: ::ffff:0:0/96' >/etc/unbound/openvpn.conf
 212 | 		if [[ $IPV6_SUPPORT == 'y' ]]; then
 213 | 			echo 'interface: fd42:42:42:42::1
 214 | access-control: fd42:42:42:42::/112 allow' >>/etc/unbound/openvpn.conf
 215 | 		fi
 216 | 	fi
 217 | 
 218 | 	systemctl enable unbound
 219 | 	systemctl restart unbound
 220 | }
 221 | 
 222 | function resolvePublicIP() {
 223 | 	# IP version flags, we'll use as default the IPv4
 224 | 	CURL_IP_VERSION_FLAG="-4"
 225 | 	DIG_IP_VERSION_FLAG="-4"
 226 | 
 227 | 	# Behind NAT, we'll default to the publicly reachable IPv4/IPv6.
 228 | 	if [[ $IPV6_SUPPORT == "y" ]]; then
 229 | 		CURL_IP_VERSION_FLAG=""
 230 | 		DIG_IP_VERSION_FLAG="-6"
 231 | 	fi
 232 | 
 233 | 	# If there is no public ip yet, we'll try to solve it using: https://api.seeip.org
 234 | 	if [[ -z $PUBLIC_IP ]]; then
 235 | 		PUBLIC_IP=$(curl -f -m 5 -sS --retry 2 --retry-connrefused "$CURL_IP_VERSION_FLAG" https://api.seeip.org 2>/dev/null)
 236 | 	fi
 237 | 
 238 | 	# If there is no public ip yet, we'll try to solve it using: https://ifconfig.me
 239 | 	if [[ -z $PUBLIC_IP ]]; then
 240 | 		PUBLIC_IP=$(curl -f -m 5 -sS --retry 2 --retry-connrefused "$CURL_IP_VERSION_FLAG" https://ifconfig.me 2>/dev/null)
 241 | 	fi
 242 | 
 243 | 	# If there is no public ip yet, we'll try to solve it using: https://api.ipify.org
 244 | 	if [[ -z $PUBLIC_IP ]]; then
 245 | 		PUBLIC_IP=$(curl -f -m 5 -sS --retry 2 --retry-connrefused "$CURL_IP_VERSION_FLAG" https://api.ipify.org 2>/dev/null)
 246 | 	fi
 247 | 
 248 | 	# If there is no public ip yet, we'll try to solve it using: ns1.google.com
 249 | 	if [[ -z $PUBLIC_IP ]]; then
 250 | 		PUBLIC_IP=$(dig $DIG_IP_VERSION_FLAG TXT +short o-o.myaddr.l.google.com @ns1.google.com | tr -d '"')
 251 | 	fi
 252 | 
 253 | 	if [[ -z $PUBLIC_IP ]]; then
 254 | 		echo >&2 echo "Couldn't solve the public IP"
 255 | 		exit 1
 256 | 	fi
 257 | 
 258 | 	echo "$PUBLIC_IP"
 259 | }
 260 | 
 261 | function installQuestions() {
 262 | 	echo "Welcome to the OpenVPN installer!"
 263 | 	echo "The git repository is available at: https://github.com/angristan/openvpn-install"
 264 | 	echo ""
 265 | 
 266 | 	echo "I need to ask you a few questions before starting the setup."
 267 | 	echo "You can leave the default options and just press enter if you are okay with them."
 268 | 	echo ""
 269 | 	echo "I need to know the IPv4 address of the network interface you want OpenVPN listening to."
 270 | 	echo "Unless your server is behind NAT, it should be your public IPv4 address."
 271 | 
 272 | 	# Detect public IPv4 address and pre-fill for the user
 273 | 	IP=$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | head -1)
 274 | 
 275 | 	if [[ -z $IP ]]; then
 276 | 		# Detect public IPv6 address
 277 | 		IP=$(ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1)
 278 | 	fi
 279 | 	APPROVE_IP=${APPROVE_IP:-n}
 280 | 	if [[ $APPROVE_IP =~ n ]]; then
 281 | 		read -rp "IP address: " -e -i "$IP" IP
 282 | 	fi
 283 | 	# If $IP is a private IP address, the server must be behind NAT
 284 | 	if echo "$IP" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then
 285 | 		echo ""
 286 | 		echo "It seems this server is behind NAT. What is its public IPv4 address or hostname?"
 287 | 		echo "We need it for the clients to connect to the server."
 288 | 
 289 | 		if [[ -z $ENDPOINT ]]; then
 290 | 			DEFAULT_ENDPOINT=$(resolvePublicIP)
 291 | 		fi
 292 | 
 293 | 		until [[ $ENDPOINT != "" ]]; do
 294 | 			read -rp "Public IPv4 address or hostname: " -e -i "$DEFAULT_ENDPOINT" ENDPOINT
 295 | 		done
 296 | 	fi
 297 | 
 298 | 	echo ""
 299 | 	echo "Checking for IPv6 connectivity..."
 300 | 	echo ""
 301 | 	# "ping6" and "ping -6" availability varies depending on the distribution
 302 | 	if type ping6 >/dev/null 2>&1; then
 303 | 		PING6="ping6 -c3 ipv6.google.com > /dev/null 2>&1"
 304 | 	else
 305 | 		PING6="ping -6 -c3 ipv6.google.com > /dev/null 2>&1"
 306 | 	fi
 307 | 	if eval "$PING6"; then
 308 | 		echo "Your host appears to have IPv6 connectivity."
 309 | 		SUGGESTION="y"
 310 | 	else
 311 | 		echo "Your host does not appear to have IPv6 connectivity."
 312 | 		SUGGESTION="n"
 313 | 	fi
 314 | 	echo ""
 315 | 	# Ask the user if they want to enable IPv6 regardless its availability.
 316 | 	until [[ $IPV6_SUPPORT =~ (y|n) ]]; do
 317 | 		read -rp "Do you want to enable IPv6 support (NAT)? [y/n]: " -e -i $SUGGESTION IPV6_SUPPORT
 318 | 	done
 319 | 	echo ""
 320 | 	echo "What port do you want OpenVPN to listen to?"
 321 | 	echo "   1) Default: 1194"
 322 | 	echo "   2) Custom"
 323 | 	echo "   3) Random [49152-65535]"
 324 | 	until [[ $PORT_CHOICE =~ ^[1-3]$ ]]; do
 325 | 		read -rp "Port choice [1-3]: " -e -i 1 PORT_CHOICE
 326 | 	done
 327 | 	case $PORT_CHOICE in
 328 | 	1)
 329 | 		PORT="1194"
 330 | 		;;
 331 | 	2)
 332 | 		until [[ $PORT =~ ^[0-9]+$ ]] && [ "$PORT" -ge 1 ] && [ "$PORT" -le 65535 ]; do
 333 | 			read -rp "Custom port [1-65535]: " -e -i 1194 PORT
 334 | 		done
 335 | 		;;
 336 | 	3)
 337 | 		# Generate random number within private ports range
 338 | 		PORT=$(shuf -i49152-65535 -n1)
 339 | 		echo "Random Port: $PORT"
 340 | 		;;
 341 | 	esac
 342 | 	echo ""
 343 | 	echo "What protocol do you want OpenVPN to use?"
 344 | 	echo "UDP is faster. Unless it is not available, you shouldn't use TCP."
 345 | 	echo "   1) UDP"
 346 | 	echo "   2) TCP"
 347 | 	until [[ $PROTOCOL_CHOICE =~ ^[1-2]$ ]]; do
 348 | 		read -rp "Protocol [1-2]: " -e -i 1 PROTOCOL_CHOICE
 349 | 	done
 350 | 	case $PROTOCOL_CHOICE in
 351 | 	1)
 352 | 		PROTOCOL="udp"
 353 | 		;;
 354 | 	2)
 355 | 		PROTOCOL="tcp"
 356 | 		;;
 357 | 	esac
 358 | 	echo ""
 359 | 	echo "What DNS resolvers do you want to use with the VPN?"
 360 | 	echo "   1) Current system resolvers (from /etc/resolv.conf)"
 361 | 	echo "   2) Self-hosted DNS Resolver (Unbound)"
 362 | 	echo "   3) Cloudflare (Anycast: worldwide)"
 363 | 	echo "   4) Quad9 (Anycast: worldwide)"
 364 | 	echo "   5) Quad9 uncensored (Anycast: worldwide)"
 365 | 	echo "   6) FDN (France)"
 366 | 	echo "   7) DNS.WATCH (Germany)"
 367 | 	echo "   8) OpenDNS (Anycast: worldwide)"
 368 | 	echo "   9) Google (Anycast: worldwide)"
 369 | 	echo "   10) Yandex Basic (Russia)"
 370 | 	echo "   11) AdGuard DNS (Anycast: worldwide)"
 371 | 	echo "   12) NextDNS (Anycast: worldwide)"
 372 | 	echo "   13) Custom"
 373 | 	until [[ $DNS =~ ^[0-9]+$ ]] && [ "$DNS" -ge 1 ] && [ "$DNS" -le 13 ]; do
 374 | 		read -rp "DNS [1-12]: " -e -i 11 DNS
 375 | 		if [[ $DNS == 2 ]] && [[ -e /etc/unbound/unbound.conf ]]; then
 376 | 			echo ""
 377 | 			echo "Unbound is already installed."
 378 | 			echo "You can allow the script to configure it in order to use it from your OpenVPN clients"
 379 | 			echo "We will simply add a second server to /etc/unbound/unbound.conf for the OpenVPN subnet."
 380 | 			echo "No changes are made to the current configuration."
 381 | 			echo ""
 382 | 
 383 | 			until [[ $CONTINUE =~ (y|n) ]]; do
 384 | 				read -rp "Apply configuration changes to Unbound? [y/n]: " -e CONTINUE
 385 | 			done
 386 | 			if [[ $CONTINUE == "n" ]]; then
 387 | 				# Break the loop and cleanup
 388 | 				unset DNS
 389 | 				unset CONTINUE
 390 | 			fi
 391 | 		elif [[ $DNS == "13" ]]; then
 392 | 			until [[ $DNS1 =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do
 393 | 				read -rp "Primary DNS: " -e DNS1
 394 | 			done
 395 | 			until [[ $DNS2 =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do
 396 | 				read -rp "Secondary DNS (optional): " -e DNS2
 397 | 				if [[ $DNS2 == "" ]]; then
 398 | 					break
 399 | 				fi
 400 | 			done
 401 | 		fi
 402 | 	done
 403 | 	echo ""
 404 | 	echo "Do you want to use compression? It is not recommended since the VORACLE attack makes use of it."
 405 | 	until [[ $COMPRESSION_ENABLED =~ (y|n) ]]; do
 406 | 		read -rp"Enable compression? [y/n]: " -e -i n COMPRESSION_ENABLED
 407 | 	done
 408 | 	if [[ $COMPRESSION_ENABLED == "y" ]]; then
 409 | 		echo "Choose which compression algorithm you want to use: (they are ordered by efficiency)"
 410 | 		echo "   1) LZ4-v2"
 411 | 		echo "   2) LZ4"
 412 | 		echo "   3) LZ0"
 413 | 		until [[ $COMPRESSION_CHOICE =~ ^[1-3]$ ]]; do
 414 | 			read -rp"Compression algorithm [1-3]: " -e -i 1 COMPRESSION_CHOICE
 415 | 		done
 416 | 		case $COMPRESSION_CHOICE in
 417 | 		1)
 418 | 			COMPRESSION_ALG="lz4-v2"
 419 | 			;;
 420 | 		2)
 421 | 			COMPRESSION_ALG="lz4"
 422 | 			;;
 423 | 		3)
 424 | 			COMPRESSION_ALG="lzo"
 425 | 			;;
 426 | 		esac
 427 | 	fi
 428 | 	echo ""
 429 | 	echo "Do you want to customize encryption settings?"
 430 | 	echo "Unless you know what you're doing, you should stick with the default parameters provided by the script."
 431 | 	echo "Note that whatever you choose, all the choices presented in the script are safe (unlike OpenVPN's defaults)."
 432 | 	echo "See https://github.com/angristan/openvpn-install#security-and-encryption to learn more."
 433 | 	echo ""
 434 | 	until [[ $CUSTOMIZE_ENC =~ (y|n) ]]; do
 435 | 		read -rp "Customize encryption settings? [y/n]: " -e -i n CUSTOMIZE_ENC
 436 | 	done
 437 | 	if [[ $CUSTOMIZE_ENC == "n" ]]; then
 438 | 		# Use default, sane and fast parameters
 439 | 		CIPHER="AES-128-GCM"
 440 | 		CERT_TYPE="1" # ECDSA
 441 | 		CERT_CURVE="prime256v1"
 442 | 		CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"
 443 | 		DH_TYPE="1" # ECDH
 444 | 		DH_CURVE="prime256v1"
 445 | 		HMAC_ALG="SHA256"
 446 | 		TLS_SIG="1" # tls-crypt
 447 | 	else
 448 | 		echo ""
 449 | 		echo "Choose which cipher you want to use for the data channel:"
 450 | 		echo "   1) AES-128-GCM (recommended)"
 451 | 		echo "   2) AES-192-GCM"
 452 | 		echo "   3) AES-256-GCM"
 453 | 		echo "   4) AES-128-CBC"
 454 | 		echo "   5) AES-192-CBC"
 455 | 		echo "   6) AES-256-CBC"
 456 | 		until [[ $CIPHER_CHOICE =~ ^[1-6]$ ]]; do
 457 | 			read -rp "Cipher [1-6]: " -e -i 1 CIPHER_CHOICE
 458 | 		done
 459 | 		case $CIPHER_CHOICE in
 460 | 		1)
 461 | 			CIPHER="AES-128-GCM"
 462 | 			;;
 463 | 		2)
 464 | 			CIPHER="AES-192-GCM"
 465 | 			;;
 466 | 		3)
 467 | 			CIPHER="AES-256-GCM"
 468 | 			;;
 469 | 		4)
 470 | 			CIPHER="AES-128-CBC"
 471 | 			;;
 472 | 		5)
 473 | 			CIPHER="AES-192-CBC"
 474 | 			;;
 475 | 		6)
 476 | 			CIPHER="AES-256-CBC"
 477 | 			;;
 478 | 		esac
 479 | 		echo ""
 480 | 		echo "Choose what kind of certificate you want to use:"
 481 | 		echo "   1) ECDSA (recommended)"
 482 | 		echo "   2) RSA"
 483 | 		until [[ $CERT_TYPE =~ ^[1-2]$ ]]; do
 484 | 			read -rp"Certificate key type [1-2]: " -e -i 1 CERT_TYPE
 485 | 		done
 486 | 		case $CERT_TYPE in
 487 | 		1)
 488 | 			echo ""
 489 | 			echo "Choose which curve you want to use for the certificate's key:"
 490 | 			echo "   1) prime256v1 (recommended)"
 491 | 			echo "   2) secp384r1"
 492 | 			echo "   3) secp521r1"
 493 | 			until [[ $CERT_CURVE_CHOICE =~ ^[1-3]$ ]]; do
 494 | 				read -rp"Curve [1-3]: " -e -i 1 CERT_CURVE_CHOICE
 495 | 			done
 496 | 			case $CERT_CURVE_CHOICE in
 497 | 			1)
 498 | 				CERT_CURVE="prime256v1"
 499 | 				;;
 500 | 			2)
 501 | 				CERT_CURVE="secp384r1"
 502 | 				;;
 503 | 			3)
 504 | 				CERT_CURVE="secp521r1"
 505 | 				;;
 506 | 			esac
 507 | 			;;
 508 | 		2)
 509 | 			echo ""
 510 | 			echo "Choose which size you want to use for the certificate's RSA key:"
 511 | 			echo "   1) 2048 bits (recommended)"
 512 | 			echo "   2) 3072 bits"
 513 | 			echo "   3) 4096 bits"
 514 | 			until [[ $RSA_KEY_SIZE_CHOICE =~ ^[1-3]$ ]]; do
 515 | 				read -rp "RSA key size [1-3]: " -e -i 1 RSA_KEY_SIZE_CHOICE
 516 | 			done
 517 | 			case $RSA_KEY_SIZE_CHOICE in
 518 | 			1)
 519 | 				RSA_KEY_SIZE="2048"
 520 | 				;;
 521 | 			2)
 522 | 				RSA_KEY_SIZE="3072"
 523 | 				;;
 524 | 			3)
 525 | 				RSA_KEY_SIZE="4096"
 526 | 				;;
 527 | 			esac
 528 | 			;;
 529 | 		esac
 530 | 		echo ""
 531 | 		echo "Choose which cipher you want to use for the control channel:"
 532 | 		case $CERT_TYPE in
 533 | 		1)
 534 | 			echo "   1) ECDHE-ECDSA-AES-128-GCM-SHA256 (recommended)"
 535 | 			echo "   2) ECDHE-ECDSA-AES-256-GCM-SHA384"
 536 | 			until [[ $CC_CIPHER_CHOICE =~ ^[1-2]$ ]]; do
 537 | 				read -rp"Control channel cipher [1-2]: " -e -i 1 CC_CIPHER_CHOICE
 538 | 			done
 539 | 			case $CC_CIPHER_CHOICE in
 540 | 			1)
 541 | 				CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"
 542 | 				;;
 543 | 			2)
 544 | 				CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384"
 545 | 				;;
 546 | 			esac
 547 | 			;;
 548 | 		2)
 549 | 			echo "   1) ECDHE-RSA-AES-128-GCM-SHA256 (recommended)"
 550 | 			echo "   2) ECDHE-RSA-AES-256-GCM-SHA384"
 551 | 			until [[ $CC_CIPHER_CHOICE =~ ^[1-2]$ ]]; do
 552 | 				read -rp"Control channel cipher [1-2]: " -e -i 1 CC_CIPHER_CHOICE
 553 | 			done
 554 | 			case $CC_CIPHER_CHOICE in
 555 | 			1)
 556 | 				CC_CIPHER="TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"
 557 | 				;;
 558 | 			2)
 559 | 				CC_CIPHER="TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384"
 560 | 				;;
 561 | 			esac
 562 | 			;;
 563 | 		esac
 564 | 		echo ""
 565 | 		echo "Choose what kind of Diffie-Hellman key you want to use:"
 566 | 		echo "   1) ECDH (recommended)"
 567 | 		echo "   2) DH"
 568 | 		until [[ $DH_TYPE =~ [1-2] ]]; do
 569 | 			read -rp"DH key type [1-2]: " -e -i 1 DH_TYPE
 570 | 		done
 571 | 		case $DH_TYPE in
 572 | 		1)
 573 | 			echo ""
 574 | 			echo "Choose which curve you want to use for the ECDH key:"
 575 | 			echo "   1) prime256v1 (recommended)"
 576 | 			echo "   2) secp384r1"
 577 | 			echo "   3) secp521r1"
 578 | 			while [[ $DH_CURVE_CHOICE != "1" && $DH_CURVE_CHOICE != "2" && $DH_CURVE_CHOICE != "3" ]]; do
 579 | 				read -rp"Curve [1-3]: " -e -i 1 DH_CURVE_CHOICE
 580 | 			done
 581 | 			case $DH_CURVE_CHOICE in
 582 | 			1)
 583 | 				DH_CURVE="prime256v1"
 584 | 				;;
 585 | 			2)
 586 | 				DH_CURVE="secp384r1"
 587 | 				;;
 588 | 			3)
 589 | 				DH_CURVE="secp521r1"
 590 | 				;;
 591 | 			esac
 592 | 			;;
 593 | 		2)
 594 | 			echo ""
 595 | 			echo "Choose what size of Diffie-Hellman key you want to use:"
 596 | 			echo "   1) 2048 bits (recommended)"
 597 | 			echo "   2) 3072 bits"
 598 | 			echo "   3) 4096 bits"
 599 | 			until [[ $DH_KEY_SIZE_CHOICE =~ ^[1-3]$ ]]; do
 600 | 				read -rp "DH key size [1-3]: " -e -i 1 DH_KEY_SIZE_CHOICE
 601 | 			done
 602 | 			case $DH_KEY_SIZE_CHOICE in
 603 | 			1)
 604 | 				DH_KEY_SIZE="2048"
 605 | 				;;
 606 | 			2)
 607 | 				DH_KEY_SIZE="3072"
 608 | 				;;
 609 | 			3)
 610 | 				DH_KEY_SIZE="4096"
 611 | 				;;
 612 | 			esac
 613 | 			;;
 614 | 		esac
 615 | 		echo ""
 616 | 		# The "auth" options behaves differently with AEAD ciphers
 617 | 		if [[ $CIPHER =~ CBC$ ]]; then
 618 | 			echo "The digest algorithm authenticates data channel packets and tls-auth packets from the control channel."
 619 | 		elif [[ $CIPHER =~ GCM$ ]]; then
 620 | 			echo "The digest algorithm authenticates tls-auth packets from the control channel."
 621 | 		fi
 622 | 		echo "Which digest algorithm do you want to use for HMAC?"
 623 | 		echo "   1) SHA-256 (recommended)"
 624 | 		echo "   2) SHA-384"
 625 | 		echo "   3) SHA-512"
 626 | 		until [[ $HMAC_ALG_CHOICE =~ ^[1-3]$ ]]; do
 627 | 			read -rp "Digest algorithm [1-3]: " -e -i 1 HMAC_ALG_CHOICE
 628 | 		done
 629 | 		case $HMAC_ALG_CHOICE in
 630 | 		1)
 631 | 			HMAC_ALG="SHA256"
 632 | 			;;
 633 | 		2)
 634 | 			HMAC_ALG="SHA384"
 635 | 			;;
 636 | 		3)
 637 | 			HMAC_ALG="SHA512"
 638 | 			;;
 639 | 		esac
 640 | 		echo ""
 641 | 		echo "You can add an additional layer of security to the control channel with tls-auth and tls-crypt"
 642 | 		echo "tls-auth authenticates the packets, while tls-crypt authenticate and encrypt them."
 643 | 		echo "   1) tls-crypt (recommended)"
 644 | 		echo "   2) tls-auth"
 645 | 		until [[ $TLS_SIG =~ [1-2] ]]; do
 646 | 			read -rp "Control channel additional security mechanism [1-2]: " -e -i 1 TLS_SIG
 647 | 		done
 648 | 	fi
 649 | 	echo ""
 650 | 	echo "Okay, that was all I needed. We are ready to setup your OpenVPN server now."
 651 | 	echo "You will be able to generate a client at the end of the installation."
 652 | 	APPROVE_INSTALL=${APPROVE_INSTALL:-n}
 653 | 	if [[ $APPROVE_INSTALL =~ n ]]; then
 654 | 		read -n1 -r -p "Press any key to continue..."
 655 | 	fi
 656 | }
 657 | 
 658 | function installOpenVPN() {
 659 | 	if [[ $AUTO_INSTALL == "y" ]]; then
 660 | 		# Set default choices so that no questions will be asked.
 661 | 		APPROVE_INSTALL=${APPROVE_INSTALL:-y}
 662 | 		APPROVE_IP=${APPROVE_IP:-y}
 663 | 		IPV6_SUPPORT=${IPV6_SUPPORT:-n}
 664 | 		PORT_CHOICE=${PORT_CHOICE:-1}
 665 | 		PROTOCOL_CHOICE=${PROTOCOL_CHOICE:-1}
 666 | 		DNS=${DNS:-1}
 667 | 		COMPRESSION_ENABLED=${COMPRESSION_ENABLED:-n}
 668 | 		CUSTOMIZE_ENC=${CUSTOMIZE_ENC:-n}
 669 | 		CLIENT=${CLIENT:-client}
 670 | 		PASS=${PASS:-1}
 671 | 		CONTINUE=${CONTINUE:-y}
 672 | 
 673 | 		if [[ -z $ENDPOINT ]]; then
 674 | 			ENDPOINT=$(resolvePublicIP)
 675 | 		fi
 676 | 	fi
 677 | 
 678 | 	# Run setup questions first, and set other variables if auto-install
 679 | 	installQuestions
 680 | 
 681 | 	# Get the "public" interface from the default route
 682 | 	NIC=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
 683 | 	if [[ -z $NIC ]] && [[ $IPV6_SUPPORT == 'y' ]]; then
 684 | 		NIC=$(ip -6 route show default | sed -ne 's/^default .* dev \([^ ]*\) .*$/\1/p')
 685 | 	fi
 686 | 
 687 | 	# $NIC can not be empty for script rm-openvpn-rules.sh
 688 | 	if [[ -z $NIC ]]; then
 689 | 		echo
 690 | 		echo "Could not detect public interface."
 691 | 		echo "This needs for setup MASQUERADE."
 692 | 		until [[ $CONTINUE =~ (y|n) ]]; do
 693 | 			read -rp "Continue? [y/n]: " -e CONTINUE
 694 | 		done
 695 | 		if [[ $CONTINUE == "n" ]]; then
 696 | 			exit 1
 697 | 		fi
 698 | 	fi
 699 | 
 700 | 	# If OpenVPN isn't installed yet, install it. This script is more-or-less
 701 | 	# idempotent on multiple runs, but will only install OpenVPN from upstream
 702 | 	# the first time.
 703 | 	if [[ ! -e /etc/openvpn/server.conf ]]; then
 704 | 		if [[ $OS =~ (debian|ubuntu) ]]; then
 705 | 			apt-get update
 706 | 			apt-get -y install ca-certificates gnupg
 707 | 			# We add the OpenVPN repo to get the latest version.
 708 | 			if [[ $VERSION_ID == "16.04" ]]; then
 709 | 				echo "deb http://build.openvpn.net/debian/openvpn/stable xenial main" >/etc/apt/sources.list.d/openvpn.list
 710 | 				wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add -
 711 | 				apt-get update
 712 | 			fi
 713 | 			# Ubuntu > 16.04 and Debian > 8 have OpenVPN >= 2.4 without the need of a third party repository.
 714 | 			apt-get install -y openvpn iptables openssl wget ca-certificates curl
 715 | 		elif [[ $OS == 'centos' ]]; then
 716 | 			yum install -y epel-release
 717 | 			yum install -y openvpn iptables openssl wget ca-certificates curl tar 'policycoreutils-python*'
 718 | 		elif [[ $OS == 'oracle' ]]; then
 719 | 			yum install -y oracle-epel-release-el8
 720 | 			yum-config-manager --enable ol8_developer_EPEL
 721 | 			yum install -y openvpn iptables openssl wget ca-certificates curl tar policycoreutils-python-utils
 722 | 		elif [[ $OS == 'amzn' ]]; then
 723 | 			amazon-linux-extras install -y epel
 724 | 			yum install -y openvpn iptables openssl wget ca-certificates curl
 725 | 		elif [[ $OS == 'amzn2023' ]]; then
 726 | 			dnf install -y openvpn iptables openssl wget ca-certificates
 727 | 		elif [[ $OS == 'fedora' ]]; then
 728 | 			dnf install -y openvpn iptables openssl wget ca-certificates curl policycoreutils-python-utils
 729 | 		elif [[ $OS == 'arch' ]]; then
 730 | 			# Install required dependencies and upgrade the system
 731 | 			pacman --needed --noconfirm -Syu openvpn iptables openssl wget ca-certificates curl
 732 | 		fi
 733 | 		# An old version of easy-rsa was available by default in some openvpn packages
 734 | 		if [[ -d /etc/openvpn/easy-rsa/ ]]; then
 735 | 			rm -rf /etc/openvpn/easy-rsa/
 736 | 		fi
 737 | 	fi
 738 | 
 739 | 	# Find out if the machine uses nogroup or nobody for the permissionless group
 740 | 	if grep -qs "^nogroup:" /etc/group; then
 741 | 		NOGROUP=nogroup
 742 | 	else
 743 | 		NOGROUP=nobody
 744 | 	fi
 745 | 
 746 | 	# Install the latest version of easy-rsa from source, if not already installed.
 747 | 	if [[ ! -d /etc/openvpn/easy-rsa/ ]]; then
 748 | 		local version="3.1.2"
 749 | 		wget -O ~/easy-rsa.tgz https://github.com/OpenVPN/easy-rsa/releases/download/v${version}/EasyRSA-${version}.tgz
 750 | 		mkdir -p /etc/openvpn/easy-rsa
 751 | 		tar xzf ~/easy-rsa.tgz --strip-components=1 --no-same-owner --directory /etc/openvpn/easy-rsa
 752 | 		rm -f ~/easy-rsa.tgz
 753 | 
 754 | 		cd /etc/openvpn/easy-rsa/ || return
 755 | 		case $CERT_TYPE in
 756 | 		1)
 757 | 			echo "set_var EASYRSA_ALGO ec" >vars
 758 | 			echo "set_var EASYRSA_CURVE $CERT_CURVE" >>vars
 759 | 			;;
 760 | 		2)
 761 | 			echo "set_var EASYRSA_KEY_SIZE $RSA_KEY_SIZE" >vars
 762 | 			;;
 763 | 		esac
 764 | 
 765 | 		# Generate a random, alphanumeric identifier of 16 characters for CN and one for server name
 766 | 		SERVER_CN="cn_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
 767 | 		echo "$SERVER_CN" >SERVER_CN_GENERATED
 768 | 		SERVER_NAME="server_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
 769 | 		echo "$SERVER_NAME" >SERVER_NAME_GENERATED
 770 | 
 771 | 		# Create the PKI, set up the CA, the DH params and the server certificate
 772 | 		./easyrsa init-pki
 773 | 		EASYRSA_CA_EXPIRE=3650 ./easyrsa --batch --req-cn="$SERVER_CN" build-ca nopass
 774 | 
 775 | 		if [[ $DH_TYPE == "2" ]]; then
 776 | 			# ECDH keys are generated on-the-fly so we don't need to generate them beforehand
 777 | 			openssl dhparam -out dh.pem $DH_KEY_SIZE
 778 | 		fi
 779 | 
 780 | 		EASYRSA_CERT_EXPIRE=3650 ./easyrsa --batch build-server-full "$SERVER_NAME" nopass
 781 | 		EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
 782 | 
 783 | 		case $TLS_SIG in
 784 | 		1)
 785 | 			# Generate tls-crypt key
 786 | 			openvpn --genkey --secret /etc/openvpn/tls-crypt.key
 787 | 			;;
 788 | 		2)
 789 | 			# Generate tls-auth key
 790 | 			openvpn --genkey --secret /etc/openvpn/tls-auth.key
 791 | 			;;
 792 | 		esac
 793 | 	else
 794 | 		# If easy-rsa is already installed, grab the generated SERVER_NAME
 795 | 		# for client configs
 796 | 		cd /etc/openvpn/easy-rsa/ || return
 797 | 		SERVER_NAME=$(cat SERVER_NAME_GENERATED)
 798 | 	fi
 799 | 
 800 | 	# Move all the generated files
 801 | 	cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn
 802 | 	if [[ $DH_TYPE == "2" ]]; then
 803 | 		cp dh.pem /etc/openvpn
 804 | 	fi
 805 | 
 806 | 	# Make cert revocation list readable for non-root
 807 | 	chmod 644 /etc/openvpn/crl.pem
 808 | 
 809 | 	# Generate server.conf
 810 | 	echo "port $PORT" >/etc/openvpn/server.conf
 811 | 	if [[ $IPV6_SUPPORT == 'n' ]]; then
 812 | 		echo "proto $PROTOCOL" >>/etc/openvpn/server.conf
 813 | 	elif [[ $IPV6_SUPPORT == 'y' ]]; then
 814 | 		echo "proto ${PROTOCOL}6" >>/etc/openvpn/server.conf
 815 | 	fi
 816 | 
 817 | 	echo "dev tun
 818 | user nobody
 819 | group $NOGROUP
 820 | persist-key
 821 | persist-tun
 822 | keepalive 10 120
 823 | topology subnet
 824 | server 10.8.0.0 255.255.255.0
 825 | ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server.conf
 826 | 
 827 | 	# DNS resolvers
 828 | 	case $DNS in
 829 | 	1) # Current system resolvers
 830 | 		# Locate the proper resolv.conf
 831 | 		# Needed for systems running systemd-resolved
 832 | 		if grep -q "127.0.0.53" "/etc/resolv.conf"; then
 833 | 			RESOLVCONF='/run/systemd/resolve/resolv.conf'
 834 | 		else
 835 | 			RESOLVCONF='/etc/resolv.conf'
 836 | 		fi
 837 | 		# Obtain the resolvers from resolv.conf and use them for OpenVPN
 838 | 		sed -ne 's/^nameserver[[:space:]]\+\([^[:space:]]\+\).*$/\1/p' $RESOLVCONF | while read -r line; do
 839 | 			# Copy, if it's a IPv4 |or| if IPv6 is enabled, IPv4/IPv6 does not matter
 840 | 			if [[ $line =~ ^[0-9.]*$ ]] || [[ $IPV6_SUPPORT == 'y' ]]; then
 841 | 				echo "push \"dhcp-option DNS $line\"" >>/etc/openvpn/server.conf
 842 | 			fi
 843 | 		done
 844 | 		;;
 845 | 	2) # Self-hosted DNS resolver (Unbound)
 846 | 		echo 'push "dhcp-option DNS 10.8.0.1"' >>/etc/openvpn/server.conf
 847 | 		if [[ $IPV6_SUPPORT == 'y' ]]; then
 848 | 			echo 'push "dhcp-option DNS fd42:42:42:42::1"' >>/etc/openvpn/server.conf
 849 | 		fi
 850 | 		;;
 851 | 	3) # Cloudflare
 852 | 		echo 'push "dhcp-option DNS 1.0.0.1"' >>/etc/openvpn/server.conf
 853 | 		echo 'push "dhcp-option DNS 1.1.1.1"' >>/etc/openvpn/server.conf
 854 | 		;;
 855 | 	4) # Quad9
 856 | 		echo 'push "dhcp-option DNS 9.9.9.9"' >>/etc/openvpn/server.conf
 857 | 		echo 'push "dhcp-option DNS 149.112.112.112"' >>/etc/openvpn/server.conf
 858 | 		;;
 859 | 	5) # Quad9 uncensored
 860 | 		echo 'push "dhcp-option DNS 9.9.9.10"' >>/etc/openvpn/server.conf
 861 | 		echo 'push "dhcp-option DNS 149.112.112.10"' >>/etc/openvpn/server.conf
 862 | 		;;
 863 | 	6) # FDN
 864 | 		echo 'push "dhcp-option DNS 80.67.169.40"' >>/etc/openvpn/server.conf
 865 | 		echo 'push "dhcp-option DNS 80.67.169.12"' >>/etc/openvpn/server.conf
 866 | 		;;
 867 | 	7) # DNS.WATCH
 868 | 		echo 'push "dhcp-option DNS 84.200.69.80"' >>/etc/openvpn/server.conf
 869 | 		echo 'push "dhcp-option DNS 84.200.70.40"' >>/etc/openvpn/server.conf
 870 | 		;;
 871 | 	8) # OpenDNS
 872 | 		echo 'push "dhcp-option DNS 208.67.222.222"' >>/etc/openvpn/server.conf
 873 | 		echo 'push "dhcp-option DNS 208.67.220.220"' >>/etc/openvpn/server.conf
 874 | 		;;
 875 | 	9) # Google
 876 | 		echo 'push "dhcp-option DNS 8.8.8.8"' >>/etc/openvpn/server.conf
 877 | 		echo 'push "dhcp-option DNS 8.8.4.4"' >>/etc/openvpn/server.conf
 878 | 		;;
 879 | 	10) # Yandex Basic
 880 | 		echo 'push "dhcp-option DNS 77.88.8.8"' >>/etc/openvpn/server.conf
 881 | 		echo 'push "dhcp-option DNS 77.88.8.1"' >>/etc/openvpn/server.conf
 882 | 		;;
 883 | 	11) # AdGuard DNS
 884 | 		echo 'push "dhcp-option DNS 94.140.14.14"' >>/etc/openvpn/server.conf
 885 | 		echo 'push "dhcp-option DNS 94.140.15.15"' >>/etc/openvpn/server.conf
 886 | 		;;
 887 | 	12) # NextDNS
 888 | 		echo 'push "dhcp-option DNS 45.90.28.167"' >>/etc/openvpn/server.conf
 889 | 		echo 'push "dhcp-option DNS 45.90.30.167"' >>/etc/openvpn/server.conf
 890 | 		;;
 891 | 	13) # Custom DNS
 892 | 		echo "push \"dhcp-option DNS $DNS1\"" >>/etc/openvpn/server.conf
 893 | 		if [[ $DNS2 != "" ]]; then
 894 | 			echo "push \"dhcp-option DNS $DNS2\"" >>/etc/openvpn/server.conf
 895 | 		fi
 896 | 		;;
 897 | 	esac
 898 | 	echo 'push "redirect-gateway def1 bypass-dhcp"' >>/etc/openvpn/server.conf
 899 | 
 900 | 	# IPv6 network settings if needed
 901 | 	if [[ $IPV6_SUPPORT == 'y' ]]; then
 902 | 		echo 'server-ipv6 fd42:42:42:42::/112
 903 | tun-ipv6
 904 | push tun-ipv6
 905 | push "route-ipv6 2000::/3"
 906 | push "redirect-gateway ipv6"' >>/etc/openvpn/server.conf
 907 | 	fi
 908 | 
 909 | 	if [[ $COMPRESSION_ENABLED == "y" ]]; then
 910 | 		echo "compress $COMPRESSION_ALG" >>/etc/openvpn/server.conf
 911 | 	fi
 912 | 
 913 | 	if [[ $DH_TYPE == "1" ]]; then
 914 | 		echo "dh none" >>/etc/openvpn/server.conf
 915 | 		echo "ecdh-curve $DH_CURVE" >>/etc/openvpn/server.conf
 916 | 	elif [[ $DH_TYPE == "2" ]]; then
 917 | 		echo "dh dh.pem" >>/etc/openvpn/server.conf
 918 | 	fi
 919 | 
 920 | 	case $TLS_SIG in
 921 | 	1)
 922 | 		echo "tls-crypt tls-crypt.key" >>/etc/openvpn/server.conf
 923 | 		;;
 924 | 	2)
 925 | 		echo "tls-auth tls-auth.key 0" >>/etc/openvpn/server.conf
 926 | 		;;
 927 | 	esac
 928 | 
 929 | 	echo "crl-verify crl.pem
 930 | ca ca.crt
 931 | cert $SERVER_NAME.crt
 932 | key $SERVER_NAME.key
 933 | auth $HMAC_ALG
 934 | cipher $CIPHER
 935 | ncp-ciphers $CIPHER
 936 | tls-server
 937 | tls-version-min 1.2
 938 | tls-cipher $CC_CIPHER
 939 | client-config-dir /etc/openvpn/ccd
 940 | status /var/log/openvpn/status.log
 941 | verb 3" >>/etc/openvpn/server.conf
 942 | 
 943 | 	# Create client-config-dir dir
 944 | 	mkdir -p /etc/openvpn/ccd
 945 | 	# Create log dir
 946 | 	mkdir -p /var/log/openvpn
 947 | 
 948 | 	# Enable routing
 949 | 	echo 'net.ipv4.ip_forward=1' >/etc/sysctl.d/99-openvpn.conf
 950 | 	if [[ $IPV6_SUPPORT == 'y' ]]; then
 951 | 		echo 'net.ipv6.conf.all.forwarding=1' >>/etc/sysctl.d/99-openvpn.conf
 952 | 	fi
 953 | 	# Apply sysctl rules
 954 | 	sysctl --system
 955 | 
 956 | 	# If SELinux is enabled and a custom port was selected, we need this
 957 | 	if hash sestatus 2>/dev/null; then
 958 | 		if sestatus | grep "Current mode" | grep -qs "enforcing"; then
 959 | 			if [[ $PORT != '1194' ]]; then
 960 | 				semanage port -a -t openvpn_port_t -p "$PROTOCOL" "$PORT"
 961 | 			fi
 962 | 		fi
 963 | 	fi
 964 | 
 965 | 	# Finally, restart and enable OpenVPN
 966 | 	if [[ $OS == 'arch' || $OS == 'fedora' || $OS == 'centos' || $OS == 'oracle' || $OS == 'amzn2023' ]]; then
 967 | 		# Don't modify package-provided service
 968 | 		cp /usr/lib/systemd/system/openvpn-server@.service /etc/systemd/system/openvpn-server@.service
 969 | 
 970 | 		# Workaround to fix OpenVPN service on OpenVZ
 971 | 		sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn-server@.service
 972 | 		# Another workaround to keep using /etc/openvpn/
 973 | 		sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn-server@.service
 974 | 
 975 | 		systemctl daemon-reload
 976 | 		systemctl enable openvpn-server@server
 977 | 		systemctl restart openvpn-server@server
 978 | 	elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then
 979 | 		# On Ubuntu 16.04, we use the package from the OpenVPN repo
 980 | 		# This package uses a sysvinit service
 981 | 		systemctl enable openvpn
 982 | 		systemctl start openvpn
 983 | 	else
 984 | 		# Don't modify package-provided service
 985 | 		cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service
 986 | 
 987 | 		# Workaround to fix OpenVPN service on OpenVZ
 988 | 		sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn\@.service
 989 | 		# Another workaround to keep using /etc/openvpn/
 990 | 		sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn\@.service
 991 | 
 992 | 		systemctl daemon-reload
 993 | 		systemctl enable openvpn@server
 994 | 		systemctl restart openvpn@server
 995 | 	fi
 996 | 
 997 | 	if [[ $DNS == 2 ]]; then
 998 | 		installUnbound
 999 | 	fi
1000 | 
1001 | 	# Add iptables rules in two scripts
1002 | 	mkdir -p /etc/iptables
1003 | 
1004 | 	# Script to add rules
1005 | 	echo "#!/bin/sh
1006 | iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -o $NIC -j MASQUERADE
1007 | iptables -I INPUT 1 -i tun0 -j ACCEPT
1008 | iptables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
1009 | iptables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
1010 | iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/add-openvpn-rules.sh
1011 | 
1012 | 	if [[ $IPV6_SUPPORT == 'y' ]]; then
1013 | 		echo "ip6tables -t nat -I POSTROUTING 1 -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
1014 | ip6tables -I INPUT 1 -i tun0 -j ACCEPT
1015 | ip6tables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
1016 | ip6tables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
1017 | ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
1018 | 	fi
1019 | 
1020 | 	# Script to remove rules
1021 | 	echo "#!/bin/sh
1022 | iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o $NIC -j MASQUERADE
1023 | iptables -D INPUT -i tun0 -j ACCEPT
1024 | iptables -D FORWARD -i $NIC -o tun0 -j ACCEPT
1025 | iptables -D FORWARD -i tun0 -o $NIC -j ACCEPT
1026 | iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/rm-openvpn-rules.sh
1027 | 
1028 | 	if [[ $IPV6_SUPPORT == 'y' ]]; then
1029 | 		echo "ip6tables -t nat -D POSTROUTING -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
1030 | ip6tables -D INPUT -i tun0 -j ACCEPT
1031 | ip6tables -D FORWARD -i $NIC -o tun0 -j ACCEPT
1032 | ip6tables -D FORWARD -i tun0 -o $NIC -j ACCEPT
1033 | ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
1034 | 	fi
1035 | 
1036 | 	chmod +x /etc/iptables/add-openvpn-rules.sh
1037 | 	chmod +x /etc/iptables/rm-openvpn-rules.sh
1038 | 
1039 | 	# Handle the rules via a systemd script
1040 | 	echo "[Unit]
1041 | Description=iptables rules for OpenVPN
1042 | Before=network-online.target
1043 | Wants=network-online.target
1044 | 
1045 | [Service]
1046 | Type=oneshot
1047 | ExecStart=/etc/iptables/add-openvpn-rules.sh
1048 | ExecStop=/etc/iptables/rm-openvpn-rules.sh
1049 | RemainAfterExit=yes
1050 | 
1051 | [Install]
1052 | WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
1053 | 
1054 | 	# Enable service and apply rules
1055 | 	systemctl daemon-reload
1056 | 	systemctl enable iptables-openvpn
1057 | 	systemctl start iptables-openvpn
1058 | 
1059 | 	# If the server is behind a NAT, use the correct IP address for the clients to connect to
1060 | 	if [[ $ENDPOINT != "" ]]; then
1061 | 		IP=$ENDPOINT
1062 | 	fi
1063 | 
1064 | 	# client-template.txt is created so we have a template to add further users later
1065 | 	echo "client" >/etc/openvpn/client-template.txt
1066 | 	if [[ $PROTOCOL == 'udp' ]]; then
1067 | 		echo "proto udp" >>/etc/openvpn/client-template.txt
1068 | 		echo "explicit-exit-notify" >>/etc/openvpn/client-template.txt
1069 | 	elif [[ $PROTOCOL == 'tcp' ]]; then
1070 | 		echo "proto tcp-client" >>/etc/openvpn/client-template.txt
1071 | 	fi
1072 | 	echo "remote $IP $PORT
1073 | dev tun
1074 | resolv-retry infinite
1075 | nobind
1076 | persist-key
1077 | persist-tun
1078 | remote-cert-tls server
1079 | verify-x509-name $SERVER_NAME name
1080 | auth $HMAC_ALG
1081 | auth-nocache
1082 | cipher $CIPHER
1083 | tls-client
1084 | tls-version-min 1.2
1085 | tls-cipher $CC_CIPHER
1086 | ignore-unknown-option block-outside-dns
1087 | setenv opt block-outside-dns # Prevent Windows 10 DNS leak
1088 | verb 3" >>/etc/openvpn/client-template.txt
1089 | 
1090 | 	if [[ $COMPRESSION_ENABLED == "y" ]]; then
1091 | 		echo "compress $COMPRESSION_ALG" >>/etc/openvpn/client-template.txt
1092 | 	fi
1093 | 
1094 | 	# Generate the custom client.ovpn
1095 | 	newClient
1096 | 	echo "If you want to add more clients, you simply need to run this script another time!"
1097 | }
1098 | 
1099 | function newClient() {
1100 | 	echo ""
1101 | 	echo "Tell me a name for the client."
1102 | 	echo "The name must consist of alphanumeric character. It may also include an underscore or a dash."
1103 | 
1104 | 	until [[ $CLIENT =~ ^[a-zA-Z0-9_-]+$ ]]; do
1105 | 		read -rp "Client name: " -e CLIENT
1106 | 	done
1107 | 
1108 | 	echo ""
1109 | 	echo "Do you want to protect the configuration file with a password?"
1110 | 	echo "(e.g. encrypt the private key with a password)"
1111 | 	echo "   1) Add a passwordless client"
1112 | 	echo "   2) Use a password for the client"
1113 | 
1114 | 	until [[ $PASS =~ ^[1-2]$ ]]; do
1115 | 		read -rp "Select an option [1-2]: " -e -i 1 PASS
1116 | 	done
1117 | 
1118 | 	CLIENTEXISTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c -E "/CN=$CLIENT\
quot;)
1119 | 	if [[ $CLIENTEXISTS == '1' ]]; then
1120 | 		echo ""
1121 | 		echo "The specified client CN was already found in easy-rsa, please choose another name."
1122 | 		exit
1123 | 	else
1124 | 		cd /etc/openvpn/easy-rsa/ || return
1125 | 		case $PASS in
1126 | 		1)
1127 | 			EASYRSA_CERT_EXPIRE=3650 ./easyrsa --batch build-client-full "$CLIENT" nopass
1128 | 			;;
1129 | 		2)
1130 | 			echo "⚠️ You will be asked for the client password below ⚠️"
1131 | 			EASYRSA_CERT_EXPIRE=3650 ./easyrsa --batch build-client-full "$CLIENT"
1132 | 			;;
1133 | 		esac
1134 | 		echo "Client $CLIENT added."
1135 | 	fi
1136 | 
1137 | 	# Home directory of the user, where the client configuration will be written
1138 | 	if [ -e "/home/${CLIENT}" ]; then
1139 | 		# if $1 is a user name
1140 | 		homeDir="/home/${CLIENT}"
1141 | 	elif [ "${SUDO_USER}" ]; then
1142 | 		# if not, use SUDO_USER
1143 | 		if [ "${SUDO_USER}" == "root" ]; then
1144 | 			# If running sudo as root
1145 | 			homeDir="/root"
1146 | 		else
1147 | 			homeDir="/home/${SUDO_USER}"
1148 | 		fi
1149 | 	else
1150 | 		# if not SUDO_USER, use /root
1151 | 		homeDir="/root"
1152 | 	fi
1153 | 
1154 | 	# Determine if we use tls-auth or tls-crypt
1155 | 	if grep -qs "^tls-crypt" /etc/openvpn/server.conf; then
1156 | 		TLS_SIG="1"
1157 | 	elif grep -qs "^tls-auth" /etc/openvpn/server.conf; then
1158 | 		TLS_SIG="2"
1159 | 	fi
1160 | 
1161 | 	# Generates the custom client.ovpn
1162 | 	cp /etc/openvpn/client-template.txt "$homeDir/$CLIENT.ovpn"
1163 | 	{
1164 | 		echo "<ca>"
1165 | 		cat "/etc/openvpn/easy-rsa/pki/ca.crt"
1166 | 		echo "</ca>"
1167 | 
1168 | 		echo "<cert>"
1169 | 		awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/easy-rsa/pki/issued/$CLIENT.crt"
1170 | 		echo "</cert>"
1171 | 
1172 | 		echo "<key>"
1173 | 		cat "/etc/openvpn/easy-rsa/pki/private/$CLIENT.key"
1174 | 		echo "</key>"
1175 | 
1176 | 		case $TLS_SIG in
1177 | 		1)
1178 | 			echo "<tls-crypt>"
1179 | 			cat /etc/openvpn/tls-crypt.key
1180 | 			echo "</tls-crypt>"
1181 | 			;;
1182 | 		2)
1183 | 			echo "key-direction 1"
1184 | 			echo "<tls-auth>"
1185 | 			cat /etc/openvpn/tls-auth.key
1186 | 			echo "</tls-auth>"
1187 | 			;;
1188 | 		esac
1189 | 	} >>"$homeDir/$CLIENT.ovpn"
1190 | 
1191 | 	echo ""
1192 | 	echo "The configuration file has been written to $homeDir/$CLIENT.ovpn."
1193 | 	echo "Download the .ovpn file and import it in your OpenVPN client."
1194 | 
1195 | 	exit 0
1196 | }
1197 | 
1198 | function revokeClient() {
1199 | 	NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^V")
1200 | 	if [[ $NUMBEROFCLIENTS == '0' ]]; then
1201 | 		echo ""
1202 | 		echo "You have no existing clients!"
1203 | 		exit 1
1204 | 	fi
1205 | 
1206 | 	echo ""
1207 | 	echo "Select the existing client certificate you want to revoke"
1208 | 	tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
1209 | 	until [[ $CLIENTNUMBER -ge 1 && $CLIENTNUMBER -le $NUMBEROFCLIENTS ]]; do
1210 | 		if [[ $CLIENTNUMBER == '1' ]]; then
1211 | 			read -rp "Select one client [1]: " CLIENTNUMBER
1212 | 		else
1213 | 			read -rp "Select one client [1-$NUMBEROFCLIENTS]: " CLIENTNUMBER
1214 | 		fi
1215 | 	done
1216 | 	CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p)
1217 | 	cd /etc/openvpn/easy-rsa/ || return
1218 | 	./easyrsa --batch revoke "$CLIENT"
1219 | 	EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
1220 | 	rm -f /etc/openvpn/crl.pem
1221 | 	cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem
1222 | 	chmod 644 /etc/openvpn/crl.pem
1223 | 	find /home/ -maxdepth 2 -name "$CLIENT.ovpn" -delete
1224 | 	rm -f "/root/$CLIENT.ovpn"
1225 | 	sed -i "/^$CLIENT,.*/d" /etc/openvpn/ipp.txt
1226 | 	cp /etc/openvpn/easy-rsa/pki/index.txt{,.bk}
1227 | 
1228 | 	echo ""
1229 | 	echo "Certificate for client $CLIENT revoked."
1230 | }
1231 | 
1232 | function removeUnbound() {
1233 | 	# Remove OpenVPN-related config
1234 | 	sed -i '/include: \/etc\/unbound\/openvpn.conf/d' /etc/unbound/unbound.conf
1235 | 	rm /etc/unbound/openvpn.conf
1236 | 
1237 | 	until [[ $REMOVE_UNBOUND =~ (y|n) ]]; do
1238 | 		echo ""
1239 | 		echo "If you were already using Unbound before installing OpenVPN, I removed the configuration related to OpenVPN."
1240 | 		read -rp "Do you want to completely remove Unbound? [y/n]: " -e REMOVE_UNBOUND
1241 | 	done
1242 | 
1243 | 	if [[ $REMOVE_UNBOUND == 'y' ]]; then
1244 | 		# Stop Unbound
1245 | 		systemctl stop unbound
1246 | 
1247 | 		if [[ $OS =~ (debian|ubuntu) ]]; then
1248 | 			apt-get remove --purge -y unbound
1249 | 		elif [[ $OS == 'arch' ]]; then
1250 | 			pacman --noconfirm -R unbound
1251 | 		elif [[ $OS =~ (centos|amzn|oracle) ]]; then
1252 | 			yum remove -y unbound
1253 | 		elif [[ $OS == 'fedora' ]]; then
1254 | 			dnf remove -y unbound
1255 | 		fi
1256 | 
1257 | 		rm -rf /etc/unbound/
1258 | 
1259 | 		echo ""
1260 | 		echo "Unbound removed!"
1261 | 	else
1262 | 		systemctl restart unbound
1263 | 		echo ""
1264 | 		echo "Unbound wasn't removed."
1265 | 	fi
1266 | }
1267 | 
1268 | function removeOpenVPN() {
1269 | 	echo ""
1270 | 	read -rp "Do you really want to remove OpenVPN? [y/n]: " -e -i n REMOVE
1271 | 	if [[ $REMOVE == 'y' ]]; then
1272 | 		# Get OpenVPN port from the configuration
1273 | 		PORT=$(grep '^port ' /etc/openvpn/server.conf | cut -d " " -f 2)
1274 | 		PROTOCOL=$(grep '^proto ' /etc/openvpn/server.conf | cut -d " " -f 2)
1275 | 
1276 | 		# Stop OpenVPN
1277 | 		if [[ $OS =~ (fedora|arch|centos|oracle) ]]; then
1278 | 			systemctl disable openvpn-server@server
1279 | 			systemctl stop openvpn-server@server
1280 | 			# Remove customised service
1281 | 			rm /etc/systemd/system/openvpn-server@.service
1282 | 		elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then
1283 | 			systemctl disable openvpn
1284 | 			systemctl stop openvpn
1285 | 		else
1286 | 			systemctl disable openvpn@server
1287 | 			systemctl stop openvpn@server
1288 | 			# Remove customised service
1289 | 			rm /etc/systemd/system/openvpn\@.service
1290 | 		fi
1291 | 
1292 | 		# Remove the iptables rules related to the script
1293 | 		systemctl stop iptables-openvpn
1294 | 		# Cleanup
1295 | 		systemctl disable iptables-openvpn
1296 | 		rm /etc/systemd/system/iptables-openvpn.service
1297 | 		systemctl daemon-reload
1298 | 		rm /etc/iptables/add-openvpn-rules.sh
1299 | 		rm /etc/iptables/rm-openvpn-rules.sh
1300 | 
1301 | 		# SELinux
1302 | 		if hash sestatus 2>/dev/null; then
1303 | 			if sestatus | grep "Current mode" | grep -qs "enforcing"; then
1304 | 				if [[ $PORT != '1194' ]]; then
1305 | 					semanage port -d -t openvpn_port_t -p "$PROTOCOL" "$PORT"
1306 | 				fi
1307 | 			fi
1308 | 		fi
1309 | 
1310 | 		if [[ $OS =~ (debian|ubuntu) ]]; then
1311 | 			apt-get remove --purge -y openvpn
1312 | 			if [[ -e /etc/apt/sources.list.d/openvpn.list ]]; then
1313 | 				rm /etc/apt/sources.list.d/openvpn.list
1314 | 				apt-get update
1315 | 			fi
1316 | 		elif [[ $OS == 'arch' ]]; then
1317 | 			pacman --noconfirm -R openvpn
1318 | 		elif [[ $OS =~ (centos|amzn|oracle) ]]; then
1319 | 			yum remove -y openvpn
1320 | 		elif [[ $OS == 'fedora' ]]; then
1321 | 			dnf remove -y openvpn
1322 | 		fi
1323 | 
1324 | 		# Cleanup
1325 | 		find /home/ -maxdepth 2 -name "*.ovpn" -delete
1326 | 		find /root/ -maxdepth 1 -name "*.ovpn" -delete
1327 | 		rm -rf /etc/openvpn
1328 | 		rm -rf /usr/share/doc/openvpn*
1329 | 		rm -f /etc/sysctl.d/99-openvpn.conf
1330 | 		rm -rf /var/log/openvpn
1331 | 
1332 | 		# Unbound
1333 | 		if [[ -e /etc/unbound/openvpn.conf ]]; then
1334 | 			removeUnbound
1335 | 		fi
1336 | 		echo ""
1337 | 		echo "OpenVPN removed!"
1338 | 	else
1339 | 		echo ""
1340 | 		echo "Removal aborted!"
1341 | 	fi
1342 | }
1343 | 
1344 | function manageMenu() {
1345 | 	echo "Welcome to OpenVPN-install!"
1346 | 	echo "The git repository is available at: https://github.com/angristan/openvpn-install"
1347 | 	echo ""
1348 | 	echo "It looks like OpenVPN is already installed."
1349 | 	echo ""
1350 | 	echo "What do you want to do?"
1351 | 	echo "   1) Add a new user"
1352 | 	echo "   2) Revoke existing user"
1353 | 	echo "   3) Remove OpenVPN"
1354 | 	echo "   4) Exit"
1355 | 	until [[ $MENU_OPTION =~ ^[1-4]$ ]]; do
1356 | 		read -rp "Select an option [1-4]: " MENU_OPTION
1357 | 	done
1358 | 
1359 | 	case $MENU_OPTION in
1360 | 	1)
1361 | 		newClient
1362 | 		;;
1363 | 	2)
1364 | 		revokeClient
1365 | 		;;
1366 | 	3)
1367 | 		removeOpenVPN
1368 | 		;;
1369 | 	4)
1370 | 		exit 0
1371 | 		;;
1372 | 	esac
1373 | }
1374 | 
1375 | # Check for root, TUN, OS...
1376 | initialCheck
1377 | 
1378 | # Check if OpenVPN is already installed
1379 | if [[ -e /etc/openvpn/server.conf && $AUTO_INSTALL != "y" ]]; then
1380 | 	manageMenu
1381 | else
1382 | 	installOpenVPN
1383 | fi
1384 | 


--------------------------------------------------------------------------------