2013-05-14 14:04:19 +02:00
#!/bin/bash
2025-12-04 18:11:11 +01:00
# shellcheck disable=SC1091,SC2034
# SC1091: Not following /etc/os-release (sourced dynamically)
# SC2034: Variables used indirectly or exported for subprocesses
2013-05-14 14:04:19 +02:00
2025-12-10 17:54:00 +01:00
# Secure OpenVPN server installer for Debian, Ubuntu, CentOS, Amazon Linux 2023, Fedora, Oracle Linux, Arch Linux, Rocky Linux and AlmaLinux.
2018-09-20 17:16:04 +02:00
# https://github.com/angristan/openvpn-install
2013-05-14 14:04:19 +02:00
2025-12-04 18:30:43 +01:00
# Configuration constants
2025-12-09 23:33:57 +01:00
readonly DEFAULT_CERT_VALIDITY_DURATION_DAYS = 3650 # 10 years
readonly DEFAULT_CRL_VALIDITY_DURATION_DAYS = 5475 # 15 years
2025-12-13 19:48:07 +01:00
readonly EASYRSA_VERSION = "3.2.5"
readonly EASYRSA_SHA256 = "662ee3b453155aeb1dff7096ec052cd83176c460cfa82ac130ef8568ec4df490"
2025-12-04 18:30:43 +01:00
2025-12-09 15:52:37 +01:00
# =============================================================================
# Logging Configuration
# =============================================================================
# Set VERBOSE=1 to see command output, VERBOSE=0 (default) for quiet mode
# Set LOG_FILE to customize log location (default: openvpn-install.log in current dir)
# Set LOG_FILE="" to disable file logging
VERBOSE = ${ VERBOSE :- 0 }
LOG_FILE = ${ LOG_FILE :- openvpn -install.log }
# Color definitions (disabled if not a terminal, unless FORCE_COLOR=1)
if [[ -t 1 ]] || [[ $FORCE_COLOR == "1" ]] ; then
readonly COLOR_RESET = '\033[0m'
readonly COLOR_RED = '\033[0;31m'
readonly COLOR_GREEN = '\033[0;32m'
readonly COLOR_YELLOW = '\033[0;33m'
readonly COLOR_BLUE = '\033[0;34m'
readonly COLOR_CYAN = '\033[0;36m'
readonly COLOR_DIM = '\033[0;90m'
readonly COLOR_BOLD = '\033[1m'
else
readonly COLOR_RESET = ''
readonly COLOR_RED = ''
readonly COLOR_GREEN = ''
readonly COLOR_YELLOW = ''
readonly COLOR_BLUE = ''
readonly COLOR_CYAN = ''
readonly COLOR_DIM = ''
readonly COLOR_BOLD = ''
fi
# Write to log file (no colors, with timestamp)
_log_to_file() {
if [[ -n " $LOG_FILE " ]] ; then
echo " $( date '+%Y-%m-%d %H:%M:%S' ) $* " >>" $LOG_FILE "
fi
}
# Logging functions
log_info() {
echo -e " ${ COLOR_BLUE } [INFO] ${ COLOR_RESET } $* "
_log_to_file "[INFO] $* "
}
log_warn() {
echo -e " ${ COLOR_YELLOW } [WARN] ${ COLOR_RESET } $* "
_log_to_file "[WARN] $* "
}
log_error() {
echo -e " ${ COLOR_RED } [ERROR] ${ COLOR_RESET } $* " >& 2
_log_to_file "[ERROR] $* "
if [[ -n " $LOG_FILE " ]] ; then
echo -e " ${ COLOR_YELLOW } Check the log file for details: ${ LOG_FILE }${ COLOR_RESET } " >& 2
fi
}
log_fatal() {
echo -e " ${ COLOR_RED } [ERROR] ${ COLOR_RESET } $* " >& 2
_log_to_file "[FATAL] $* "
if [[ -n " $LOG_FILE " ]] ; then
echo -e " ${ COLOR_YELLOW } Check the log file for details: ${ LOG_FILE }${ COLOR_RESET } " >& 2
_log_to_file "Script exited with error"
fi
exit 1
}
log_success() {
echo -e " ${ COLOR_GREEN } [OK] ${ COLOR_RESET } $* "
_log_to_file "[OK] $* "
}
log_debug() {
if [[ $VERBOSE -eq 1 ]] ; then
echo -e " ${ COLOR_DIM } [DEBUG] ${ COLOR_RESET } $* "
fi
_log_to_file "[DEBUG] $* "
}
log_prompt() {
# For user-facing prompts/questions (no prefix, just cyan)
# Skip display in auto-install mode
if [[ $AUTO_INSTALL != "y" ]] ; then
echo -e " ${ COLOR_CYAN } $* ${ COLOR_RESET } "
fi
_log_to_file "[PROMPT] $* "
}
log_header() {
# For section headers
# Skip display in auto-install mode
if [[ $AUTO_INSTALL != "y" ]] ; then
echo ""
echo -e " ${ COLOR_BOLD }${ COLOR_BLUE } === $* === ${ COLOR_RESET } "
echo ""
fi
_log_to_file "=== $* ==="
}
log_menu() {
# For menu options - only show in interactive mode
if [[ $AUTO_INSTALL != "y" ]] ; then
echo " $@ "
fi
}
# Run a command with optional output suppression
# Usage: run_cmd "description" command [args...]
run_cmd() {
local desc = " $1 "
shift
2025-12-09 21:41:08 +01:00
# Display the command being run
echo -e " ${ COLOR_DIM } > $* ${ COLOR_RESET } "
2025-12-09 15:52:37 +01:00
_log_to_file "[CMD] $* "
if [[ $VERBOSE -eq 1 ]] ; then
if [[ -n " $LOG_FILE " ]] ; then
" $@ " 2>& 1 | tee -a " $LOG_FILE "
else
" $@ "
fi
else
if [[ -n " $LOG_FILE " ]] ; then
" $@ " >>" $LOG_FILE " 2>& 1
else
" $@ " >/dev/null 2>& 1
fi
fi
local ret = $?
if [[ $ret -eq 0 ]] ; then
log_debug " $desc completed successfully"
else
log_error " $desc failed with exit code $ret "
fi
return $ret
}
2025-12-13 13:31:54 +01:00
# Run a command that must succeed, exit on failure
# Usage: run_cmd_fatal "description" command [args...]
run_cmd_fatal() {
local desc = " $1 "
shift
if ! run_cmd " $desc " " $@ " ; then
log_fatal " $desc failed"
fi
}
2020-04-27 14:59:19 +02:00
function isRoot() {
2018-09-20 00:05:02 +02:00
if [ " $EUID " -ne 0 ] ; then
return 1
fi
}
2013-05-14 14:04:19 +02:00
2020-04-27 14:59:19 +02:00
function tunAvailable() {
2018-09-20 00:05:02 +02:00
if [ ! -e /dev/net/tun ] ; then
return 1
fi
}
2014-03-12 21:06:57 +01:00
2020-04-27 14:59:19 +02:00
function checkOS() {
2018-09-20 00:05:02 +02:00
if [[ -e /etc/debian_version ]] ; then
2018-09-29 20:14:44 +02:00
OS = "debian"
2018-09-21 04:00:16 +08:00
source /etc/os-release
2018-09-23 22:22:59 +02:00
2020-04-27 14:59:19 +02:00
if [[ $ID == "debian" || $ID == "raspbian" ]] ; then
2025-12-07 12:27:41 +01:00
if [[ $VERSION_ID -lt 11 ]] ; then
2025-12-09 15:52:37 +01:00
log_warn "Your version of Debian is not supported."
log_info "However, if you're using Debian >= 11 or unstable/testing, you can continue at your own risk."
2018-09-23 22:22:59 +02:00
until [[ $CONTINUE = ~ ( y| n) ]] ; do
read -rp "Continue? [y/n]: " -e CONTINUE
done
2020-04-27 14:59:19 +02:00
if [[ $CONTINUE == "n" ]] ; then
2018-09-23 22:22:59 +02:00
exit 1
fi
fi
2020-04-27 14:59:19 +02:00
elif [[ $ID == "ubuntu" ]] ; then
2018-09-23 22:22:59 +02:00
OS = "ubuntu"
2020-04-27 13:35:32 +02:00
MAJOR_UBUNTU_VERSION = $( echo " $VERSION_ID " | cut -d '.' -f1)
2025-12-07 12:27:41 +01:00
if [[ $MAJOR_UBUNTU_VERSION -lt 18 ]] ; then
2025-12-09 15:52:37 +01:00
log_warn "Your version of Ubuntu is not supported."
log_info "However, if you're using Ubuntu >= 18.04 or beta, you can continue at your own risk."
2018-09-23 22:22:59 +02:00
until [[ $CONTINUE = ~ ( y| n) ]] ; do
read -rp "Continue? [y/n]: " -e CONTINUE
done
2020-04-27 14:59:19 +02:00
if [[ $CONTINUE == "n" ]] ; then
2018-09-23 22:22:59 +02:00
exit 1
fi
2018-09-20 00:05:02 +02:00
fi
2017-11-12 22:51:54 +01:00
fi
2025-12-12 04:22:12 +08:00
elif [[ -e /etc/os-release ]] ; then
2019-08-19 23:25:48 +02:00
source /etc/os-release
2021-02-14 10:54:53 +01:00
if [[ $ID == "fedora" || $ID_LIKE == "fedora" ]] ; then
2020-01-27 18:08:06 +01:00
OS = "fedora"
fi
2025-12-12 04:22:12 +08:00
if [[ $ID == "opensuse-tumbleweed" ]] ; then
OS = "opensuse"
fi
if [[ $ID == "opensuse-leap" ]] ; then
OS = "opensuse"
if [[ ${ VERSION_ID %.* } -lt 16 ]] ; then
log_info "The script only supports openSUSE Leap 16+."
log_fatal "Your version of openSUSE Leap is not supported."
fi
fi
2021-08-27 21:24:53 +08:00
if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]] ; then
2019-08-20 13:36:16 +02:00
OS = "centos"
fi
2021-03-22 10:48:15 +01:00
if [[ $ID == "ol" ]] ; then
OS = "oracle"
2025-12-11 20:22:00 +01:00
fi
if [[ $OS = ~ ( centos| oracle) ]] && [[ ${ VERSION_ID %.* } -lt 8 ]] ; then
log_info "The script only supports CentOS Stream / Rocky Linux / AlmaLinux / Oracle Linux version 8+."
log_fatal "Your version is not supported."
2021-03-22 10:48:15 +01:00
fi
2020-04-27 14:59:19 +02:00
if [[ $ID == "amzn" ]] ; then
2025-12-10 17:54:00 +01:00
if [[ " $( echo " $PRETTY_NAME " | cut -c 1-18) " == "Amazon Linux 2023." ]] && [[ " $( echo " $PRETTY_NAME " | cut -c 19) " -ge 6 ]] ; then
2025-03-10 05:24:45 -04:00
OS = "amzn2023"
else
2025-12-10 17:54:00 +01:00
log_info "The script only supports Amazon Linux 2023.6+"
log_info "Amazon Linux 2 is EOL and no longer supported."
2025-12-09 15:52:37 +01:00
log_fatal "Your version of Amazon Linux is not supported."
2019-08-19 23:25:48 +02:00
fi
fi
2025-12-12 04:22:12 +08:00
if [[ $ID == "arch" ]] ; then
OS = "arch"
fi
2018-09-23 16:27:36 +02:00
elif [[ -e /etc/arch-release ]] ; then
OS = arch
2018-09-20 00:05:02 +02:00
else
2025-12-12 04:22:12 +08:00
log_fatal "It looks like you aren't running this installer on a Debian, Ubuntu, Fedora, openSUSE, CentOS, Amazon Linux 2023, Oracle Linux, Arch Linux, Rocky Linux or AlmaLinux system."
2018-09-16 01:26:30 +02:00
fi
2018-09-20 00:05:02 +02:00
}
2013-05-14 14:04:19 +02:00
2025-12-13 10:55:36 +01:00
function checkArchPendingKernelUpgrade() {
if [[ $OS != "arch" ]] ; then
return 0
fi
# Check if running kernel's modules are available
# (detects if kernel was upgraded but system not rebooted)
# Skip this check in containers - they share host kernel but have their own /lib/modules
if [[ -f /.dockerenv ]] || grep -qE '(docker|lxc|containerd)' /proc/1/cgroup 2>/dev/null; then
log_info "Running in container, skipping kernel modules check"
else
local running_kernel
running_kernel = $( uname -r)
if [[ ! -d "/lib/modules/ ${ running_kernel } " ]] ; then
log_error "Kernel modules for running kernel ( $running_kernel ) not found!"
log_info "This usually means the kernel was upgraded but the system wasn't rebooted."
log_fatal "Please reboot your system and run this script again."
fi
fi
log_info "Checking for pending kernel upgrades on Arch Linux..."
# Sync package database to check for updates
if ! pacman -Sy & >/dev/null; then
log_warn "Failed to sync package database, skipping kernel upgrade check"
return 0
fi
# Check for pending linux kernel upgrades
local pending_kernels
pending_kernels = $( pacman -Qu 2>/dev/null | grep -E '^linux' || true )
if [[ -n " $pending_kernels " ]] ; then
log_warn "Linux kernel upgrade(s) pending:"
echo " $pending_kernels " | while read -r line; do
log_info " $line "
done
echo ""
log_info "This script uses 'pacman -Syu' which will upgrade your kernel."
log_info "After a kernel upgrade, the TUN module won't be available until you reboot."
echo ""
log_info "Please upgrade your system and reboot first:"
log_info " sudo pacman -Syu"
log_info " sudo reboot"
echo ""
log_fatal "Aborting. Run this script again after upgrading and rebooting."
fi
log_success "No pending kernel upgrades"
}
2020-04-27 14:59:19 +02:00
function initialCheck() {
2025-12-12 23:47:09 +01:00
log_debug "Checking root privileges..."
2018-09-20 00:05:02 +02:00
if ! isRoot; then
2025-12-09 15:52:37 +01:00
log_fatal "Sorry, you need to run this script as root."
2017-11-12 22:51:54 +01:00
fi
2025-12-12 23:47:09 +01:00
log_debug "Root check passed"
log_debug "Checking TUN device availability..."
2018-09-20 00:05:02 +02:00
if ! tunAvailable; then
2025-12-09 15:52:37 +01:00
log_fatal "TUN is not available."
2018-09-20 00:05:02 +02:00
fi
2025-12-12 23:47:09 +01:00
log_debug "TUN device available at /dev/net/tun"
log_debug "Detecting operating system..."
2018-09-20 00:05:02 +02:00
checkOS
2025-12-12 23:47:09 +01:00
log_info "Detected OS: $OS ( ${ PRETTY_NAME :- unknown } )"
2025-12-13 10:55:36 +01:00
checkArchPendingKernelUpgrade
2014-10-23 00:19:08 +02:00
}
2025-12-10 00:11:25 +01:00
# Check if OpenVPN version is at least the specified version
# Usage: openvpnVersionAtLeast "2.5"
# Returns 0 if version is >= specified, 1 otherwise
function openvpnVersionAtLeast() {
local required_version = " $1 "
local installed_version
if ! command -v openvpn & >/dev/null; then
return 1
fi
installed_version = $( openvpn --version 2>/dev/null | head -1 | awk '{print $2}' )
if [[ -z " $installed_version " ]] ; then
return 1
fi
# Compare versions using sort -V
if [[ " $( printf '%s\n' " $required_version " " $installed_version " | sort -V | head -n1) " == " $required_version " ]] ; then
return 0
fi
return 1
}
2025-12-10 18:53:45 +01:00
# Check if kernel version is at least the specified version
# Usage: kernelVersionAtLeast "6.16"
# Returns 0 if version is >= specified, 1 otherwise
function kernelVersionAtLeast() {
local required_version = " $1 "
local kernel_version
kernel_version = $( uname -r | cut -d'-' -f1)
if [[ -z " $kernel_version " ]] ; then
return 1
fi
if [[ " $( printf '%s\n' " $required_version " " $kernel_version " | sort -V | head -n1) " == " $required_version " ]] ; then
return 0
fi
return 1
}
# Check if Data Channel Offload (DCO) is available
# DCO requires: OpenVPN 2.6+, kernel support (Linux 6.16+ or ovpn-dco module)
# Returns 0 if DCO is available, 1 otherwise
function isDCOAvailable() {
# DCO requires OpenVPN 2.6+
if ! openvpnVersionAtLeast "2.6" ; then
return 1
fi
# DCO is built into Linux 6.16+, or available via ovpn-dco module
if kernelVersionAtLeast "6.16" ; then
return 0
elif lsmod 2>/dev/null | grep -q "^ovpn_dco" || modinfo ovpn-dco & >/dev/null; then
return 0
fi
return 1
}
2025-12-09 19:45:56 +01:00
function installOpenVPNRepo() {
log_info "Setting up official OpenVPN repository..."
if [[ $OS = ~ ( debian| ubuntu) ]] ; then
run_cmd "Update package lists" apt-get update
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing prerequisites" apt-get install -y ca-certificates curl
2025-12-09 19:45:56 +01:00
# Create keyrings directory
run_cmd "Creating keyrings directory" mkdir -p /etc/apt/keyrings
# Download and install GPG key
if ! run_cmd "Downloading OpenVPN GPG key" curl -fsSL https://swupdate.openvpn.net/repos/repo-public.gpg -o /etc/apt/keyrings/openvpn-repo-public.asc; then
log_fatal "Failed to download OpenVPN repository GPG key"
fi
# Add repository - using stable release
if [[ -z " ${ VERSION_CODENAME } " ]] ; then
log_fatal "VERSION_CODENAME is not set. Unable to configure OpenVPN repository."
fi
echo "deb [arch= $( dpkg --print-architecture) signed-by=/etc/apt/keyrings/openvpn-repo-public.asc] https://build.openvpn.net/debian/openvpn/stable ${ VERSION_CODENAME } main" >/etc/apt/sources.list.d/openvpn-aptrepo.list
log_info "Updating package lists with new repository..."
run_cmd "Update package lists" apt-get update
log_info "OpenVPN official repository configured"
elif [[ $OS = ~ ( centos| oracle) ]] ; then
# For RHEL-based systems, use Fedora Copr (OpenVPN 2.6 stable)
# EPEL is required for pkcs11-helper dependency
log_info "Configuring OpenVPN Copr repository for RHEL-based system..."
2025-12-11 20:22:00 +01:00
# Oracle Linux uses oracle-epel-release-el* instead of epel-release
if [[ $OS == "oracle" ]] ; then
EPEL_PACKAGE = "oracle-epel-release-el ${ VERSION_ID %.* } "
else
EPEL_PACKAGE = "epel-release"
fi
2025-12-09 19:45:56 +01:00
if ! command -v dnf & >/dev/null; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing EPEL repository" yum install -y " $EPEL_PACKAGE "
run_cmd_fatal "Installing yum-plugin-copr" yum install -y yum-plugin-copr
run_cmd_fatal "Enabling OpenVPN Copr repo" yum copr enable -y @OpenVPN/openvpn-release-2.6
2025-12-09 19:45:56 +01:00
else
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing EPEL repository" dnf install -y " $EPEL_PACKAGE "
run_cmd_fatal "Installing dnf-plugins-core" dnf install -y dnf-plugins-core
run_cmd_fatal "Enabling OpenVPN Copr repo" dnf copr enable -y @OpenVPN/openvpn-release-2.6
2025-12-09 19:45:56 +01:00
fi
log_info "OpenVPN Copr repository configured"
elif [[ $OS == "fedora" ]] ; then
2025-12-13 13:31:54 +01:00
# Fedora already ships with recent OpenVPN 2.6.x, no Copr needed
log_info "Fedora already has recent OpenVPN packages, using distribution version"
2025-12-09 19:45:56 +01:00
else
log_info "No official OpenVPN repository available for this OS, using distribution packages"
fi
}
2020-04-27 14:59:19 +02:00
function installUnbound() {
2025-12-09 15:52:37 +01:00
log_info "Installing Unbound DNS resolver..."
2018-09-16 00:53:33 +02:00
2025-12-11 13:14:56 +01:00
# Install Unbound if not present
if [[ ! -e /etc/unbound/unbound.conf ]] ; then
2020-04-27 14:59:19 +02:00
if [[ $OS = ~ ( debian| ubuntu) ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing Unbound" apt-get install -y unbound
2025-12-10 17:54:00 +01:00
elif [[ $OS = ~ ( centos| oracle) ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing Unbound" yum install -y unbound
2025-12-10 17:54:00 +01:00
elif [[ $OS = ~ ( fedora| amzn2023) ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing Unbound" dnf install -y unbound
2025-12-12 04:22:12 +08:00
elif [[ $OS == "opensuse" ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing Unbound" zypper install -y unbound
2020-04-27 14:59:19 +02:00
elif [[ $OS == "arch" ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing Unbound" pacman -Syu --noconfirm unbound
2018-09-16 00:53:33 +02:00
fi
2025-12-11 13:14:56 +01:00
fi
2020-05-01 00:10:11 +02:00
2025-12-11 13:14:56 +01:00
# Configure Unbound for OpenVPN (runs whether freshly installed or pre-existing)
# Create conf.d directory (works on all distros)
run_cmd "Creating Unbound config directory" mkdir -p /etc/unbound/unbound.conf.d
2018-09-16 00:53:33 +02:00
2025-12-11 13:14:56 +01:00
# Ensure main config includes conf.d directory
# Modern Debian/Ubuntu use include-toplevel, others need include directive
if ! grep -qE "include(-toplevel)?:\s*.*/etc/unbound/unbound.conf.d" /etc/unbound/unbound.conf 2>/dev/null; then
# Add include directive for conf.d if not present
echo 'include: "/etc/unbound/unbound.conf.d/*.conf"' >>/etc/unbound/unbound.conf
fi
# Generate OpenVPN-specific Unbound configuration
# Using consistent best-practice settings across all distros
{
echo 'server:'
echo ' # OpenVPN DNS resolver configuration'
echo ' interface: 10.8.0.1'
echo ' access-control: 10.8.0.0/24 allow'
echo ''
echo ' # Security hardening'
echo ' hide-identity: yes'
echo ' hide-version: yes'
echo ' harden-glue: yes'
echo ' harden-dnssec-stripped: yes'
echo ''
echo ' # Performance optimizations'
echo ' prefetch: yes'
echo ' use-caps-for-id: yes'
echo ' qname-minimisation: yes'
echo ''
echo ' # Allow binding before tun interface exists'
echo ' ip-freebind: yes'
echo ''
echo ' # DNS rebinding protection'
echo ' private-address: 10.0.0.0/8'
echo ' private-address: 172.16.0.0/12'
echo ' private-address: 192.168.0.0/16'
echo ' private-address: 169.254.0.0/16'
echo ' private-address: 127.0.0.0/8'
echo ' private-address: fd00::/8'
echo ' private-address: fe80::/10'
echo ' private-address: ::ffff:0:0/96'
# IPv6 support
2020-05-01 00:10:11 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2025-12-11 13:14:56 +01:00
echo ''
echo ' # IPv6 VPN support'
echo ' interface: fd42:42:42:42::1'
echo ' access-control: fd42:42:42:42::/112 allow'
echo ' private-address: fd42:42:42:42::/112'
2020-05-01 00:04:38 +02:00
fi
2025-12-12 04:22:12 +08:00
# Disable remote-control (requires SSL certs on openSUSE)
if [[ $OS == "opensuse" ]] ; then
echo ''
echo 'remote-control:'
echo ' control-enable: no'
fi
2025-12-11 13:14:56 +01:00
} >/etc/unbound/unbound.conf.d/openvpn.conf
2018-09-16 00:53:33 +02:00
2025-12-09 15:52:37 +01:00
run_cmd "Enabling Unbound service" systemctl enable unbound
run_cmd "Starting Unbound service" systemctl restart unbound
2025-12-11 13:14:56 +01:00
# Validate Unbound is running
for i in { 1..10} ; do
if pgrep -x unbound >/dev/null; then
return 0
fi
sleep 1
done
log_fatal "Unbound failed to start. Check 'journalctl -u unbound' for details."
2018-09-16 00:53:33 +02:00
}
2024-11-07 19:55:14 +00:00
function resolvePublicIP() {
# IP version flags, we'll use as default the IPv4
CURL_IP_VERSION_FLAG = "-4"
DIG_IP_VERSION_FLAG = "-4"
# Behind NAT, we'll default to the publicly reachable IPv4/IPv6.
if [[ $IPV6_SUPPORT == "y" ]] ; then
CURL_IP_VERSION_FLAG = ""
DIG_IP_VERSION_FLAG = "-6"
fi
# If there is no public ip yet, we'll try to solve it using: https://api.seeip.org
if [[ -z $PUBLIC_IP ]] ; then
PUBLIC_IP = $( curl -f -m 5 -sS --retry 2 --retry-connrefused " $CURL_IP_VERSION_FLAG " https://api.seeip.org 2>/dev/null)
fi
# If there is no public ip yet, we'll try to solve it using: https://ifconfig.me
if [[ -z $PUBLIC_IP ]] ; then
PUBLIC_IP = $( curl -f -m 5 -sS --retry 2 --retry-connrefused " $CURL_IP_VERSION_FLAG " https://ifconfig.me 2>/dev/null)
fi
# If there is no public ip yet, we'll try to solve it using: https://api.ipify.org
if [[ -z $PUBLIC_IP ]] ; then
PUBLIC_IP = $( curl -f -m 5 -sS --retry 2 --retry-connrefused " $CURL_IP_VERSION_FLAG " https://api.ipify.org 2>/dev/null)
fi
# If there is no public ip yet, we'll try to solve it using: ns1.google.com
if [[ -z $PUBLIC_IP ]] ; then
PUBLIC_IP = $( dig $DIG_IP_VERSION_FLAG TXT +short o-o.myaddr.l.google.com @ns1.google.com | tr -d '"' )
fi
if [[ -z $PUBLIC_IP ]] ; then
2025-12-09 15:52:37 +01:00
log_fatal "Couldn't solve the public IP"
2024-11-07 19:55:14 +00:00
fi
echo " $PUBLIC_IP "
}
2020-04-27 14:59:19 +02:00
function installQuestions() {
2025-12-09 15:52:37 +01:00
log_header "OpenVPN Installer"
log_prompt "The git repository is available at: https://github.com/angristan/openvpn-install"
2018-07-15 15:25:59 +06:00
2025-12-09 15:52:37 +01:00
log_prompt "I need to ask you a few questions before starting the setup."
log_prompt "You can leave the default options and just press enter if you are okay with them."
log_menu ""
log_prompt "I need to know the IPv4 address of the network interface you want OpenVPN listening to."
log_prompt "Unless your server is behind NAT, it should be your public IPv4 address."
2018-07-15 15:25:59 +06:00
2018-09-20 00:05:02 +02:00
# Detect public IPv4 address and pre-fill for the user
2020-04-27 16:24:30 +02:00
IP = $( ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | head -1)
2020-10-20 16:42:35 +02:00
2020-04-27 16:25:20 +02:00
if [[ -z $IP ]] ; then
2020-04-27 16:24:30 +02:00
# Detect public IPv6 address
IP = $( ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1)
fi
2019-02-25 20:02:50 +01:00
APPROVE_IP = ${ APPROVE_IP :- n }
if [[ $APPROVE_IP = ~ n ]] ; then
read -rp "IP address: " -e -i " $IP " IP
fi
2025-12-09 22:12:23 +05:00
# If $IP is a private IP address, the server must be behind NAT
2018-09-16 17:55:50 +02:00
if echo " $IP " | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)' ; then
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "It seems this server is behind NAT. What is its public IPv4 address or hostname?"
log_prompt "We need it for the clients to connect to the server."
2020-10-20 16:42:35 +02:00
2024-11-07 19:55:14 +00:00
if [[ -z $ENDPOINT ]] ; then
DEFAULT_ENDPOINT = $( resolvePublicIP)
fi
2020-04-27 14:59:19 +02:00
until [[ $ENDPOINT != "" ]] ; do
2024-11-07 19:55:14 +00:00
read -rp "Public IPv4 address or hostname: " -e -i " $DEFAULT_ENDPOINT " ENDPOINT
2018-09-21 21:53:39 +02:00
done
2018-09-16 17:55:50 +02:00
fi
2018-09-20 00:05:02 +02:00
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Checking for IPv6 connectivity..."
2018-10-01 21:00:26 +02:00
# "ping6" and "ping -6" availability varies depending on the distribution
2020-04-27 14:59:19 +02:00
if type ping6 >/dev/null 2>& 1; then
2018-10-01 21:00:26 +02:00
PING6 = "ping6 -c3 ipv6.google.com > /dev/null 2>&1"
else
PING6 = "ping -6 -c3 ipv6.google.com > /dev/null 2>&1"
fi
if eval " $PING6 " ; then
2025-12-09 15:52:37 +01:00
log_prompt "Your host appears to have IPv6 connectivity."
2018-09-20 00:05:02 +02:00
SUGGESTION = "y"
2018-09-16 17:55:50 +02:00
else
2025-12-09 15:52:37 +01:00
log_prompt "Your host does not appear to have IPv6 connectivity."
2018-09-20 00:05:02 +02:00
SUGGESTION = "n"
2018-09-16 17:55:50 +02:00
fi
2025-12-09 15:52:37 +01:00
log_menu ""
2018-09-20 00:05:02 +02:00
# Ask the user if they want to enable IPv6 regardless its availability.
2018-09-22 15:23:01 +02:00
until [[ $IPV6_SUPPORT = ~ ( y| n) ]] ; do
2018-09-20 00:05:02 +02:00
read -rp "Do you want to enable IPv6 support (NAT)? [y/n]: " -e -i $SUGGESTION IPV6_SUPPORT
2018-09-16 17:55:50 +02:00
done
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "What port do you want OpenVPN to listen to?"
log_menu " 1) Default: 1194"
log_menu " 2) Custom"
log_menu " 3) Random [49152-65535]"
2020-04-27 14:59:19 +02:00
until [[ $PORT_CHOICE = ~ ^[ 1-3] $ ]] ; do
2018-09-21 23:48:11 +02:00
read -rp "Port choice [1-3]: " -e -i 1 PORT_CHOICE
2018-08-18 09:57:24 -04:00
done
case $PORT_CHOICE in
2020-04-27 14:59:19 +02:00
1)
PORT = "1194"
2018-08-18 09:57:24 -04:00
;;
2020-04-27 14:59:19 +02:00
2)
until [[ $PORT = ~ ^[ 0-9] +$ ]] && [ " $PORT " -ge 1 ] && [ " $PORT " -le 65535 ] ; do
read -rp "Custom port [1-65535]: " -e -i 1194 PORT
done
2018-08-18 09:57:24 -04:00
;;
2020-04-27 14:59:19 +02:00
3)
# Generate random number within private ports range
PORT = $( shuf -i49152-65535 -n1)
2025-12-09 15:52:37 +01:00
log_info "Random Port: $PORT "
2018-08-18 09:57:24 -04:00
;;
esac
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "What protocol do you want OpenVPN to use?"
log_prompt "UDP is faster. Unless it is not available, you shouldn't use TCP."
log_menu " 1) UDP"
log_menu " 2) TCP"
2020-04-27 14:59:19 +02:00
until [[ $PROTOCOL_CHOICE = ~ ^[ 1-2] $ ]] ; do
2018-09-20 00:05:02 +02:00
read -rp "Protocol [1-2]: " -e -i 1 PROTOCOL_CHOICE
2017-11-12 22:51:54 +01:00
done
2018-09-20 00:05:02 +02:00
case $PROTOCOL_CHOICE in
2020-04-27 14:59:19 +02:00
1)
PROTOCOL = "udp"
2018-09-20 00:05:02 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
PROTOCOL = "tcp"
2018-09-20 00:05:02 +02:00
;;
esac
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "What DNS resolvers do you want to use with the VPN?"
log_menu " 1) Current system resolvers (from /etc/resolv.conf)"
log_menu " 2) Self-hosted DNS Resolver (Unbound)"
log_menu " 3) Cloudflare (Anycast: worldwide)"
log_menu " 4) Quad9 (Anycast: worldwide)"
log_menu " 5) Quad9 uncensored (Anycast: worldwide)"
log_menu " 6) FDN (France)"
log_menu " 7) DNS.WATCH (Germany)"
log_menu " 8) OpenDNS (Anycast: worldwide)"
log_menu " 9) Google (Anycast: worldwide)"
log_menu " 10) Yandex Basic (Russia)"
log_menu " 11) AdGuard DNS (Anycast: worldwide)"
log_menu " 12) NextDNS (Anycast: worldwide)"
log_menu " 13) Custom"
2020-04-27 14:59:19 +02:00
until [[ $DNS = ~ ^[ 0-9] +$ ]] && [ " $DNS " -ge 1 ] && [ " $DNS " -le 13 ] ; do
2025-12-13 19:32:07 +01:00
read -rp "DNS [1-13]: " -e -i 3 DNS
2020-04-27 14:59:19 +02:00
if [[ $DNS == 2 ]] && [[ -e /etc/unbound/unbound.conf ]] ; then
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Unbound is already installed."
log_prompt "You can allow the script to configure it in order to use it from your OpenVPN clients"
log_prompt "We will simply add a second server to /etc/unbound/unbound.conf for the OpenVPN subnet."
log_prompt "No changes are made to the current configuration."
log_menu ""
2020-04-27 14:59:19 +02:00
until [[ $CONTINUE = ~ ( y| n) ]] ; do
read -rp "Apply configuration changes to Unbound? [y/n]: " -e CONTINUE
done
if [[ $CONTINUE == "n" ]] ; then
# Break the loop and cleanup
unset DNS
unset CONTINUE
2018-09-16 00:53:33 +02:00
fi
2020-04-27 14:59:19 +02:00
elif [[ $DNS == "13" ]] ; then
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
read -rp "Primary DNS: " -e DNS1
done
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
read -rp "Secondary DNS (optional): " -e DNS2
if [[ $DNS2 == "" ]] ; then
break
fi
done
fi
2017-11-12 22:51:54 +01:00
done
2025-12-09 15:52:37 +01:00
log_menu ""
2025-12-09 18:30:57 +01:00
log_prompt "Do you want to allow a single .ovpn profile to be used on multiple devices simultaneously?"
2025-12-12 00:04:51 +01:00
log_prompt "Note: Enabling this disables persistent IP addresses for clients."
2025-12-09 18:30:57 +01:00
until [[ $MULTI_CLIENT = ~ ( y| n) ]] ; do
read -rp "Allow multiple devices per client? [y/n]: " -e -i n MULTI_CLIENT
done
2025-12-09 22:12:23 +05:00
log_menu ""
2025-12-09 15:52:37 +01:00
log_prompt "Do you want to use compression? It is not recommended since the VORACLE attack makes use of it."
2018-09-22 14:07:51 +02:00
until [[ $COMPRESSION_ENABLED = ~ ( y| n) ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "Enable compression? [y/n]: " -e -i n COMPRESSION_ENABLED
2018-09-22 14:07:51 +02:00
done
2020-04-27 14:59:19 +02:00
if [[ $COMPRESSION_ENABLED == "y" ]] ; then
2025-12-09 15:52:37 +01:00
log_prompt "Choose which compression algorithm you want to use: (they are ordered by efficiency)"
log_menu " 1) LZ4-v2"
log_menu " 2) LZ4"
log_menu " 3) LZ0"
2019-07-05 17:49:31 +02:00
until [[ $COMPRESSION_CHOICE = ~ ^[ 1-3] $ ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "Compression algorithm [1-3]: " -e -i 1 COMPRESSION_CHOICE
2018-09-22 14:07:51 +02:00
done
case $COMPRESSION_CHOICE in
2020-04-27 14:59:19 +02:00
1)
2019-07-05 17:49:31 +02:00
COMPRESSION_ALG = "lz4-v2"
2018-09-22 14:07:51 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2019-07-05 17:49:31 +02:00
COMPRESSION_ALG = "lz4"
;;
2020-04-27 14:59:19 +02:00
3)
2018-09-22 14:07:51 +02:00
COMPRESSION_ALG = "lzo"
;;
esac
fi
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Do you want to customize encryption settings?"
log_prompt "Unless you know what you're doing, you should stick with the default parameters provided by the script."
log_prompt "Note that whatever you choose, all the choices presented in the script are safe (unlike OpenVPN's defaults)."
log_prompt "See https://github.com/angristan/openvpn-install#security-and-encryption to learn more."
log_menu ""
2018-09-21 17:17:41 +02:00
until [[ $CUSTOMIZE_ENC = ~ ( y| n) ]] ; do
read -rp "Customize encryption settings? [y/n]: " -e -i n CUSTOMIZE_ENC
2017-11-12 22:51:54 +01:00
done
2020-04-27 14:59:19 +02:00
if [[ $CUSTOMIZE_ENC == "n" ]] ; then
2018-09-28 14:36:00 +00:00
# Use default, sane and fast parameters
2018-09-22 22:33:25 +02:00
CIPHER = "AES-128-GCM"
CERT_TYPE = "1" # ECDSA
2018-09-23 17:06:15 +02:00
CERT_CURVE = "prime256v1"
2018-09-22 15:11:15 +02:00
CC_CIPHER = "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"
2018-09-22 22:33:25 +02:00
DH_TYPE = "1" # ECDH
2018-09-23 17:06:15 +02:00
DH_CURVE = "prime256v1"
2018-09-22 17:51:38 +02:00
HMAC_ALG = "SHA256"
2025-12-13 14:32:38 +01:00
TLS_SIG = "1" # tls-crypt-v2
2018-09-21 17:17:41 +02:00
else
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose which cipher you want to use for the data channel:"
log_menu " 1) AES-128-GCM (recommended)"
log_menu " 2) AES-192-GCM"
log_menu " 3) AES-256-GCM"
log_menu " 4) AES-128-CBC"
log_menu " 5) AES-192-CBC"
log_menu " 6) AES-256-CBC"
2025-12-10 00:11:25 +01:00
log_menu " 7) CHACHA20-POLY1305 (requires OpenVPN 2.5+, good for devices without AES-NI)"
until [[ $CIPHER_CHOICE = ~ ^[ 1-7] $ ]] ; do
read -rp "Cipher [1-7]: " -e -i 1 CIPHER_CHOICE
2018-09-21 17:17:41 +02:00
done
case $CIPHER_CHOICE in
2020-04-27 14:59:19 +02:00
1)
CIPHER = "AES-128-GCM"
2018-09-21 17:17:41 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
CIPHER = "AES-192-GCM"
2018-09-21 17:17:41 +02:00
;;
2020-04-27 14:59:19 +02:00
3)
CIPHER = "AES-256-GCM"
2018-09-22 14:20:20 +02:00
;;
2020-04-27 14:59:19 +02:00
4)
CIPHER = "AES-128-CBC"
2018-09-22 14:20:20 +02:00
;;
2020-04-27 14:59:19 +02:00
5)
CIPHER = "AES-192-CBC"
2018-09-22 14:20:20 +02:00
;;
2020-04-27 14:59:19 +02:00
6)
CIPHER = "AES-256-CBC"
2018-09-21 17:17:41 +02:00
;;
2025-12-10 00:11:25 +01:00
7)
CIPHER = "CHACHA20-POLY1305"
;;
2018-09-21 17:17:41 +02:00
esac
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose what kind of certificate you want to use:"
log_menu " 1) ECDSA (recommended)"
log_menu " 2) RSA"
2018-09-22 15:23:01 +02:00
until [[ $CERT_TYPE = ~ ^[ 1-2] $ ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "Certificate key type [1-2]: " -e -i 1 CERT_TYPE
2018-09-21 17:17:41 +02:00
done
2018-09-22 15:11:15 +02:00
case $CERT_TYPE in
2020-04-27 14:59:19 +02:00
1)
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose which curve you want to use for the certificate's key:"
log_menu " 1) prime256v1 (recommended)"
log_menu " 2) secp384r1"
log_menu " 3) secp521r1"
2020-04-27 14:59:19 +02:00
until [[ $CERT_CURVE_CHOICE = ~ ^[ 1-3] $ ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "Curve [1-3]: " -e -i 1 CERT_CURVE_CHOICE
2020-04-27 14:59:19 +02:00
done
case $CERT_CURVE_CHOICE in
2018-09-21 17:17:41 +02:00
1)
2020-04-27 14:59:19 +02:00
CERT_CURVE = "prime256v1"
;;
2)
CERT_CURVE = "secp384r1"
;;
3)
CERT_CURVE = "secp521r1"
;;
esac
2018-09-21 17:17:41 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose which size you want to use for the certificate's RSA key:"
log_menu " 1) 2048 bits (recommended)"
log_menu " 2) 3072 bits"
log_menu " 3) 4096 bits"
2020-04-27 14:59:19 +02:00
until [[ $RSA_KEY_SIZE_CHOICE = ~ ^[ 1-3] $ ]] ; do
read -rp "RSA key size [1-3]: " -e -i 1 RSA_KEY_SIZE_CHOICE
done
case $RSA_KEY_SIZE_CHOICE in
1)
RSA_KEY_SIZE = "2048"
;;
2018-09-21 17:17:41 +02:00
2)
2020-04-27 14:59:19 +02:00
RSA_KEY_SIZE = "3072"
;;
3)
RSA_KEY_SIZE = "4096"
;;
esac
2018-09-21 17:17:41 +02:00
;;
2018-09-22 15:11:15 +02:00
esac
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose which cipher you want to use for the control channel:"
2018-09-22 15:11:15 +02:00
case $CERT_TYPE in
2020-04-27 14:59:19 +02:00
1)
2025-12-09 15:52:37 +01:00
log_menu " 1) ECDHE-ECDSA-AES-128-GCM-SHA256 (recommended)"
log_menu " 2) ECDHE-ECDSA-AES-256-GCM-SHA384"
2025-12-10 00:11:25 +01:00
log_menu " 3) ECDHE-ECDSA-CHACHA20-POLY1305 (requires OpenVPN 2.5+)"
until [[ $CC_CIPHER_CHOICE = ~ ^[ 1-3] $ ]] ; do
read -rp "Control channel cipher [1-3]: " -e -i 1 CC_CIPHER_CHOICE
2020-04-27 14:59:19 +02:00
done
case $CC_CIPHER_CHOICE in
2018-09-22 15:11:15 +02:00
1)
2020-04-27 14:59:19 +02:00
CC_CIPHER = "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"
;;
2)
CC_CIPHER = "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384"
;;
2025-12-10 00:11:25 +01:00
3)
CC_CIPHER = "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"
;;
2020-04-27 14:59:19 +02:00
esac
2018-09-22 15:11:15 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-09 15:52:37 +01:00
log_menu " 1) ECDHE-RSA-AES-128-GCM-SHA256 (recommended)"
log_menu " 2) ECDHE-RSA-AES-256-GCM-SHA384"
2025-12-10 00:11:25 +01:00
log_menu " 3) ECDHE-RSA-CHACHA20-POLY1305 (requires OpenVPN 2.5+)"
until [[ $CC_CIPHER_CHOICE = ~ ^[ 1-3] $ ]] ; do
read -rp "Control channel cipher [1-3]: " -e -i 1 CC_CIPHER_CHOICE
2020-04-27 14:59:19 +02:00
done
case $CC_CIPHER_CHOICE in
1)
CC_CIPHER = "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"
;;
2018-09-22 15:11:15 +02:00
2)
2020-04-27 14:59:19 +02:00
CC_CIPHER = "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384"
;;
2025-12-10 00:11:25 +01:00
3)
CC_CIPHER = "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"
;;
2020-04-27 14:59:19 +02:00
esac
2018-09-21 17:17:41 +02:00
;;
esac
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose what kind of Diffie-Hellman key you want to use:"
log_menu " 1) ECDH (recommended)"
log_menu " 2) DH"
2018-09-22 16:41:28 +02:00
until [[ $DH_TYPE = ~ [ 1-2] ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "DH key type [1-2]: " -e -i 1 DH_TYPE
2018-09-21 17:17:41 +02:00
done
2018-09-22 16:41:28 +02:00
case $DH_TYPE in
2020-04-27 14:59:19 +02:00
1)
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose which curve you want to use for the ECDH key:"
log_menu " 1) prime256v1 (recommended)"
log_menu " 2) secp384r1"
log_menu " 3) secp521r1"
2020-04-27 14:59:19 +02:00
while [[ $DH_CURVE_CHOICE != "1" && $DH_CURVE_CHOICE != "2" && $DH_CURVE_CHOICE != "3" ]] ; do
2025-12-09 15:52:37 +01:00
read -rp "Curve [1-3]: " -e -i 1 DH_CURVE_CHOICE
2020-04-27 14:59:19 +02:00
done
case $DH_CURVE_CHOICE in
2018-09-21 17:17:41 +02:00
1)
2020-04-27 14:59:19 +02:00
DH_CURVE = "prime256v1"
;;
2)
DH_CURVE = "secp384r1"
;;
3)
DH_CURVE = "secp521r1"
;;
esac
2018-09-21 17:17:41 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Choose what size of Diffie-Hellman key you want to use:"
log_menu " 1) 2048 bits (recommended)"
log_menu " 2) 3072 bits"
log_menu " 3) 4096 bits"
2020-04-27 14:59:19 +02:00
until [[ $DH_KEY_SIZE_CHOICE = ~ ^[ 1-3] $ ]] ; do
read -rp "DH key size [1-3]: " -e -i 1 DH_KEY_SIZE_CHOICE
done
case $DH_KEY_SIZE_CHOICE in
1)
DH_KEY_SIZE = "2048"
;;
2018-09-21 17:17:41 +02:00
2)
2020-04-27 14:59:19 +02:00
DH_KEY_SIZE = "3072"
;;
3)
DH_KEY_SIZE = "4096"
;;
esac
2018-09-21 17:17:41 +02:00
;;
esac
2025-12-09 15:52:37 +01:00
log_menu ""
2025-12-10 00:11:25 +01:00
# The "auth" options behaves differently with AEAD ciphers (GCM, ChaCha20-Poly1305)
2020-04-27 14:59:19 +02:00
if [[ $CIPHER = ~ CBC$ ]] ; then
2025-12-09 15:52:37 +01:00
log_prompt "The digest algorithm authenticates data channel packets and tls-auth packets from the control channel."
2025-12-10 00:11:25 +01:00
elif [[ $CIPHER = ~ GCM$ ]] || [[ $CIPHER == "CHACHA20-POLY1305" ]] ; then
2025-12-09 15:52:37 +01:00
log_prompt "The digest algorithm authenticates tls-auth packets from the control channel."
2018-09-22 17:51:38 +02:00
fi
2025-12-09 15:52:37 +01:00
log_prompt "Which digest algorithm do you want to use for HMAC?"
log_menu " 1) SHA-256 (recommended)"
log_menu " 2) SHA-384"
log_menu " 3) SHA-512"
2018-09-22 17:51:38 +02:00
until [[ $HMAC_ALG_CHOICE = ~ ^[ 1-3] $ ]] ; do
read -rp "Digest algorithm [1-3]: " -e -i 1 HMAC_ALG_CHOICE
done
case $HMAC_ALG_CHOICE in
2020-04-27 14:59:19 +02:00
1)
HMAC_ALG = "SHA256"
2018-09-22 17:51:38 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
HMAC_ALG = "SHA384"
2018-09-22 17:51:38 +02:00
;;
2020-04-27 14:59:19 +02:00
3)
HMAC_ALG = "SHA512"
2018-09-22 17:51:38 +02:00
;;
esac
2025-12-09 15:52:37 +01:00
log_menu ""
2025-12-13 14:32:38 +01:00
log_prompt "You can add an additional layer of security to the control channel."
log_menu " 1) tls-crypt-v2 (recommended): Encrypts control channel, unique key per client"
log_menu " 2) tls-crypt: Encrypts control channel, shared key for all clients"
log_menu " 3) tls-auth: Authenticates control channel, no encryption"
until [[ $TLS_SIG = ~ ^[ 1-3] $ ]] ; do
read -rp "Control channel additional security mechanism [1-3]: " -e -i 1 TLS_SIG
2018-09-22 22:34:10 +02:00
done
2018-09-21 17:17:41 +02:00
fi
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Okay, that was all I needed. We are ready to setup your OpenVPN server now."
log_prompt "You will be able to generate a client at the end of the installation."
2019-02-25 20:02:50 +01:00
APPROVE_INSTALL = ${ APPROVE_INSTALL :- n }
if [[ $APPROVE_INSTALL = ~ n ]] ; then
read -n1 -r -p "Press any key to continue..."
fi
2018-09-22 17:59:21 +02:00
}
2020-04-27 14:59:19 +02:00
function installOpenVPN() {
2019-02-25 21:30:46 +01:00
if [[ $AUTO_INSTALL == "y" ]] ; then
# Set default choices so that no questions will be asked.
APPROVE_INSTALL = ${ APPROVE_INSTALL :- y }
APPROVE_IP = ${ APPROVE_IP :- y }
IPV6_SUPPORT = ${ IPV6_SUPPORT :- n }
PORT_CHOICE = ${ PORT_CHOICE :- 1 }
PROTOCOL_CHOICE = ${ PROTOCOL_CHOICE :- 1 }
2025-12-13 19:32:07 +01:00
DNS = ${ DNS :- 3 }
2019-02-25 21:30:46 +01:00
COMPRESSION_ENABLED = ${ COMPRESSION_ENABLED :- n }
2025-12-09 18:30:57 +01:00
MULTI_CLIENT = ${ MULTI_CLIENT :- n }
2019-02-25 21:30:46 +01:00
CUSTOMIZE_ENC = ${ CUSTOMIZE_ENC :- n }
CLIENT = ${ CLIENT :- client }
PASS = ${ PASS :- 1 }
2025-12-09 23:33:57 +01:00
CLIENT_CERT_DURATION_DAYS = ${ CLIENT_CERT_DURATION_DAYS :- $DEFAULT_CERT_VALIDITY_DURATION_DAYS }
SERVER_CERT_DURATION_DAYS = ${ SERVER_CERT_DURATION_DAYS :- $DEFAULT_CERT_VALIDITY_DURATION_DAYS }
2019-02-25 23:31:18 +01:00
CONTINUE = ${ CONTINUE :- y }
2025-12-13 15:57:02 -03:00
NEW_CLIENT = ${ NEW_CLIENT :- y }
2019-02-25 21:30:46 +01:00
2024-11-07 19:55:14 +00:00
if [[ -z $ENDPOINT ]] ; then
ENDPOINT = $( resolvePublicIP)
2020-04-27 04:56:34 -07:00
fi
2025-12-09 15:52:37 +01:00
# Log auto-install mode and parameters
log_info "=== OpenVPN Auto-Install ==="
log_info "Running in auto-install mode with the following settings:"
log_info " ENDPOINT= $ENDPOINT "
log_info " IPV6_SUPPORT= $IPV6_SUPPORT "
log_info " PORT_CHOICE= $PORT_CHOICE "
log_info " PROTOCOL_CHOICE= $PROTOCOL_CHOICE "
log_info " DNS= $DNS "
log_info " COMPRESSION_ENABLED= $COMPRESSION_ENABLED "
2025-12-09 18:30:57 +01:00
log_info " MULTI_CLIENT= $MULTI_CLIENT "
2025-12-09 15:52:37 +01:00
log_info " CUSTOMIZE_ENC= $CUSTOMIZE_ENC "
log_info " CLIENT= $CLIENT "
log_info " PASS= $PASS "
2025-12-09 23:33:57 +01:00
log_info " CLIENT_CERT_DURATION_DAYS= $CLIENT_CERT_DURATION_DAYS "
log_info " SERVER_CERT_DURATION_DAYS= $SERVER_CERT_DURATION_DAYS "
2019-02-25 21:30:46 +01:00
fi
2017-11-12 22:51:54 +01:00
2022-01-07 14:54:46 +00:00
# Run setup questions first, and set other variables if auto-install
2019-02-25 21:54:36 +01:00
installQuestions
2018-09-20 00:05:02 +02:00
# Get the "public" interface from the default route
NIC = $( ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
2020-04-27 14:59:19 +02:00
if [[ -z $NIC ]] && [[ $IPV6_SUPPORT == 'y' ]] ; then
2020-03-26 21:22:22 +01:00
NIC = $( ip -6 route show default | sed -ne 's/^default .* dev \([^ ]*\) .*$/\1/p' )
fi
2018-09-20 00:05:02 +02:00
2020-03-26 21:27:16 +01:00
# $NIC can not be empty for script rm-openvpn-rules.sh
2020-04-27 14:59:19 +02:00
if [[ -z $NIC ]] ; then
2025-12-09 15:52:37 +01:00
log_warn "Could not detect public interface."
log_info "This needs for setup MASQUERADE."
2020-04-27 14:59:19 +02:00
until [[ $CONTINUE = ~ ( y| n) ]] ; do
read -rp "Continue? [y/n]: " -e CONTINUE
done
if [[ $CONTINUE == "n" ]] ; then
exit 1
fi
fi
2020-03-26 21:27:16 +01:00
2020-04-27 04:56:34 -07:00
# If OpenVPN isn't installed yet, install it. This script is more-or-less
# idempotent on multiple runs, but will only install OpenVPN from upstream
# the first time.
2025-12-12 22:09:18 +01:00
if [[ ! -e /etc/openvpn/server/server.conf ]] ; then
2025-12-09 15:52:37 +01:00
log_header "Installing OpenVPN"
2025-12-09 19:45:56 +01:00
# Setup official OpenVPN repository for latest versions
installOpenVPNRepo
log_info "Installing OpenVPN and dependencies..."
2020-04-27 14:59:19 +02:00
if [[ $OS = ~ ( debian| ubuntu) ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" apt-get install -y openvpn iptables openssl curl ca-certificates tar dnsutils
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'centos' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" yum install -y openvpn iptables openssl ca-certificates curl tar bind-utils 'policycoreutils-python*'
2021-03-22 10:48:15 +01:00
elif [[ $OS == 'oracle' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" yum install -y openvpn iptables openssl ca-certificates curl tar bind-utils policycoreutils-python-utils
2025-03-10 05:24:45 -04:00
elif [[ $OS == 'amzn2023' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" dnf install -y openvpn iptables openssl ca-certificates curl tar bind-utils
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'fedora' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" dnf install -y openvpn iptables openssl ca-certificates curl tar bind-utils policycoreutils-python-utils
2025-12-12 04:22:12 +08:00
elif [[ $OS == 'opensuse' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" zypper install -y openvpn iptables openssl ca-certificates curl tar bind-utils
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'arch' ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Installing OpenVPN" pacman --needed --noconfirm -Syu openvpn iptables openssl ca-certificates curl tar bind
2017-11-12 22:51:54 +01:00
fi
2025-12-10 00:11:25 +01:00
# Verify ChaCha20-Poly1305 compatibility if selected
if [[ $CIPHER == "CHACHA20-POLY1305" ]] || [[ $CC_CIPHER = ~ CHACHA20 ]] ; then
local installed_version
installed_version = $( openvpn --version 2>/dev/null | head -1 | awk '{print $2}' )
if ! openvpnVersionAtLeast "2.5" ; then
log_fatal "ChaCha20-Poly1305 requires OpenVPN 2.5 or later. Installed version: $installed_version "
fi
log_info "OpenVPN version supports ChaCha20-Poly1305"
fi
2025-12-10 18:53:45 +01:00
# Check Data Channel Offload (DCO) availability
if isDCOAvailable; then
# Check if configuration is DCO-compatible
if [[ $PROTOCOL == "udp" ]] && [[ $COMPRESSION_ENABLED == "n" ]] && [[ $CIPHER = ~ ( GCM| CHACHA20-POLY1305) ]] ; then
log_info "Data Channel Offload (DCO) is available and will be used for improved performance"
else
log_info "Data Channel Offload (DCO) is available but not enabled (requires UDP, AEAD cipher, no compression)"
fi
else
log_info "Data Channel Offload (DCO) is not available (requires OpenVPN 2.6+ and kernel support)"
fi
2025-12-12 22:09:18 +01:00
# Create the server directory (OpenVPN 2.4+ directory structure)
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating server directory" mkdir -p /etc/openvpn/server
2018-09-17 01:11:30 +02:00
fi
2025-12-12 22:09:18 +01:00
# Determine which user/group OpenVPN should run as
# - Fedora/RHEL/Amazon create 'openvpn' user with 'openvpn' group
# - Arch creates 'openvpn' user with 'network' group
# - Debian/Ubuntu/openSUSE don't create a dedicated user, use 'nobody'
#
# Also check if the systemd service file already handles user/group switching.
# If so, we shouldn't add user/group to config (would cause double privilege drop).
SYSTEMD_HANDLES_USER = false
for service_file in /usr/lib/systemd/system/openvpn-server@.service /lib/systemd/system/openvpn-server@.service; do
if [[ -f " $service_file " ]] && grep -q "^User=" " $service_file " ; then
SYSTEMD_HANDLES_USER = true
break
fi
done
if id openvpn & >/dev/null; then
OPENVPN_USER = openvpn
# Get the openvpn user's primary group (e.g., 'openvpn' on Fedora, 'network' on Arch)
OPENVPN_GROUP = $( id -gn openvpn 2>/dev/null || echo openvpn)
2017-11-12 22:51:54 +01:00
else
2025-12-12 22:09:18 +01:00
OPENVPN_USER = nobody
if grep -qs "^nogroup:" /etc/group; then
OPENVPN_GROUP = nogroup
else
OPENVPN_GROUP = nobody
fi
2017-11-12 22:51:54 +01:00
fi
2020-04-27 19:20:40 +02:00
# Install the latest version of easy-rsa from source, if not already installed.
2025-12-12 22:09:18 +01:00
if [[ ! -d /etc/openvpn/server/easy-rsa/ ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Downloading Easy-RSA v ${ EASYRSA_VERSION } " curl -fL --retry 5 -o ~/easy-rsa.tgz "https://github.com/OpenVPN/easy-rsa/releases/download/v ${ EASYRSA_VERSION } /EasyRSA- ${ EASYRSA_VERSION } .tgz"
2025-12-09 15:52:37 +01:00
log_info "Verifying Easy-RSA checksum..."
CHECKSUM_OUTPUT = $( echo " ${ EASYRSA_SHA256 } $HOME /easy-rsa.tgz" | sha256sum -c 2>& 1) || {
_log_to_file "[CHECKSUM] $CHECKSUM_OUTPUT "
run_cmd "Cleaning up failed download" rm -f ~/easy-rsa.tgz
log_fatal "SHA256 checksum verification failed for easy-rsa download!"
}
_log_to_file "[CHECKSUM] $CHECKSUM_OUTPUT "
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating Easy-RSA directory" mkdir -p /etc/openvpn/server/easy-rsa
run_cmd_fatal "Extracting Easy-RSA" tar xzf ~/easy-rsa.tgz --strip-components= 1 --no-same-owner --directory /etc/openvpn/server/easy-rsa
2025-12-09 15:52:37 +01:00
run_cmd "Cleaning up archive" rm -f ~/easy-rsa.tgz
2020-04-27 04:56:34 -07:00
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2020-04-27 04:56:34 -07:00
case $CERT_TYPE in
2020-04-27 14:59:19 +02:00
1)
echo "set_var EASYRSA_ALGO ec" >vars
echo "set_var EASYRSA_CURVE $CERT_CURVE " >>vars
2020-04-27 04:56:34 -07:00
;;
2020-04-27 14:59:19 +02:00
2)
echo "set_var EASYRSA_KEY_SIZE $RSA_KEY_SIZE " >vars
2020-04-27 04:56:34 -07:00
;;
esac
2018-09-20 00:05:02 +02:00
2020-04-27 04:56:34 -07:00
# Generate a random, alphanumeric identifier of 16 characters for CN and one for server name
SERVER_CN = "cn_ $( head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1) "
2020-04-27 14:59:19 +02:00
echo " $SERVER_CN " >SERVER_CN_GENERATED
2020-04-27 04:56:34 -07:00
SERVER_NAME = "server_ $( head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1) "
2020-04-27 14:59:19 +02:00
echo " $SERVER_NAME " >SERVER_NAME_GENERATED
2018-09-23 16:27:36 +02:00
2020-04-27 04:56:34 -07:00
# Create the PKI, set up the CA, the DH params and the server certificate
2025-12-09 15:52:37 +01:00
log_info "Initializing PKI..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Initializing PKI" ./easyrsa init-pki
2025-12-09 23:33:57 +01:00
export EASYRSA_CA_EXPIRE = $DEFAULT_CERT_VALIDITY_DURATION_DAYS
2025-12-09 15:52:37 +01:00
log_info "Building CA..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Building CA" ./easyrsa --batch --req-cn= " $SERVER_CN " build-ca nopass
2018-09-23 16:27:36 +02:00
2020-04-27 04:56:34 -07:00
if [[ $DH_TYPE == "2" ]] ; then
# ECDH keys are generated on-the-fly so we don't need to generate them beforehand
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Generating DH parameters (this may take a while)" openssl dhparam -out dh.pem " $DH_KEY_SIZE "
2020-04-27 04:56:34 -07:00
fi
2019-06-30 23:06:33 +02:00
2025-12-09 23:33:57 +01:00
export EASYRSA_CERT_EXPIRE = ${ SERVER_CERT_DURATION_DAYS :- $DEFAULT_CERT_VALIDITY_DURATION_DAYS }
2025-12-09 15:52:37 +01:00
log_info "Building server certificate..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Building server certificate" ./easyrsa --batch build-server-full " $SERVER_NAME " nopass
2025-12-09 23:33:57 +01:00
export EASYRSA_CRL_DAYS = $DEFAULT_CRL_VALIDITY_DURATION_DAYS
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Generating CRL" ./easyrsa gen-crl
2019-06-30 23:06:33 +02:00
2025-12-09 15:52:37 +01:00
log_info "Generating TLS key..."
2020-04-27 04:56:34 -07:00
case $TLS_SIG in
2020-04-27 14:59:19 +02:00
1)
2025-12-13 14:32:38 +01:00
# Generate tls-crypt-v2 server key
run_cmd_fatal "Generating tls-crypt-v2 server key" openvpn --genkey tls-crypt-v2-server /etc/openvpn/server/tls-crypt-v2.key
;;
2)
2020-04-27 14:59:19 +02:00
# Generate tls-crypt key
2025-12-13 18:59:40 +01:00
run_cmd_fatal "Generating tls-crypt key" openvpn --genkey secret /etc/openvpn/server/tls-crypt.key
2020-04-27 04:56:34 -07:00
;;
2025-12-13 14:32:38 +01:00
3)
2020-04-27 14:59:19 +02:00
# Generate tls-auth key
2025-12-13 18:59:40 +01:00
run_cmd_fatal "Generating tls-auth key" openvpn --genkey secret /etc/openvpn/server/tls-auth.key
2020-04-27 04:56:34 -07:00
;;
esac
else
# If easy-rsa is already installed, grab the generated SERVER_NAME
# for client configs
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2020-04-27 04:56:34 -07:00
SERVER_NAME = $( cat SERVER_NAME_GENERATED)
fi
2019-06-30 23:06:33 +02:00
2017-11-12 22:51:54 +01:00
# Move all the generated files
2025-12-09 15:52:37 +01:00
log_info "Copying certificates..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Copying certificates to /etc/openvpn/server" cp pki/ca.crt pki/private/ca.key "pki/issued/ $SERVER_NAME .crt" "pki/private/ $SERVER_NAME .key" /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server
2018-09-22 16:41:28 +02:00
if [[ $DH_TYPE == "2" ]] ; then
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Copying DH parameters" cp dh.pem /etc/openvpn/server
2018-09-22 16:41:28 +02:00
fi
2019-06-30 23:06:33 +02:00
2017-11-12 22:51:54 +01:00
# Make cert revocation list readable for non-root
2025-12-12 22:09:18 +01:00
run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
2017-11-12 22:51:54 +01:00
# Generate server.conf
2025-12-09 15:52:37 +01:00
log_info "Generating server configuration..."
2025-12-12 22:09:18 +01:00
echo "port $PORT " >/etc/openvpn/server/server.conf
2020-04-27 14:59:19 +02:00
if [[ $IPV6_SUPPORT == 'n' ]] ; then
2025-12-12 22:09:18 +01:00
echo "proto $PROTOCOL " >>/etc/openvpn/server/server.conf
2020-04-27 14:59:19 +02:00
elif [[ $IPV6_SUPPORT == 'y' ]] ; then
2025-12-12 22:09:18 +01:00
echo "proto ${ PROTOCOL } 6" >>/etc/openvpn/server/server.conf
2018-09-16 17:55:50 +02:00
fi
2018-09-20 00:05:02 +02:00
2025-12-09 22:12:23 +05:00
if [[ $MULTI_CLIENT == "y" ]] ; then
2025-12-12 22:09:18 +01:00
echo "duplicate-cn" >>/etc/openvpn/server/server.conf
2025-12-09 22:12:23 +05:00
fi
2025-12-12 22:09:18 +01:00
echo "dev tun" >>/etc/openvpn/server/server.conf
# Only add user/group if systemd doesn't handle it (avoids double privilege drop)
if [[ $SYSTEMD_HANDLES_USER == "false" ]] ; then
echo "user $OPENVPN_USER
group $OPENVPN_GROUP " >>/etc/openvpn/server/server.conf
fi
echo "persist-key
2016-11-28 22:13:32 +01:00
persist-tun
keepalive 10 120
2015-09-12 21:48:08 +02:00
topology subnet
2025-12-12 22:09:18 +01:00
server 10.8.0.0 255.255.255.0" >>/etc/openvpn/server/server.conf
2025-12-12 00:04:51 +01:00
# ifconfig-pool-persist is incompatible with duplicate-cn
if [[ $MULTI_CLIENT != "y" ]] ; then
2025-12-12 22:09:18 +01:00
echo "ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server/server.conf
2025-12-12 00:04:51 +01:00
fi
2018-09-22 14:21:20 +02:00
2017-11-12 22:51:54 +01:00
# DNS resolvers
case $DNS in
2020-04-27 14:59:19 +02:00
1) # Current system resolvers
# Locate the proper resolv.conf
# Needed for systems running systemd-resolved
if grep -q "127.0.0.53" "/etc/resolv.conf" ; then
RESOLVCONF = '/run/systemd/resolve/resolv.conf'
else
RESOLVCONF = '/etc/resolv.conf'
fi
# Obtain the resolvers from resolv.conf and use them for OpenVPN
2020-04-27 18:04:18 +02:00
sed -ne 's/^nameserver[[:space:]]\+\([^[:space:]]\+\).*$/\1/p' $RESOLVCONF | while read -r line; do
# Copy, if it's a IPv4 |or| if IPv6 is enabled, IPv4/IPv6 does not matter
if [[ $line = ~ ^[ 0-9.] *$ ]] || [[ $IPV6_SUPPORT == 'y' ]] ; then
2025-12-12 22:09:18 +01:00
echo "push \"dhcp-option DNS $line \"" >>/etc/openvpn/server/server.conf
2020-04-27 18:04:18 +02:00
fi
2020-04-27 14:59:19 +02:00
done
2017-11-12 22:51:54 +01:00
;;
2020-05-01 00:10:11 +02:00
2) # Self-hosted DNS resolver (Unbound)
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 10.8.0.1"' >>/etc/openvpn/server/server.conf
2020-05-01 00:10:11 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS fd42:42:42:42::1"' >>/etc/openvpn/server/server.conf
2020-05-01 00:10:11 +02:00
fi
2018-09-16 00:53:33 +02:00
;;
2020-04-27 14:59:19 +02:00
3) # Cloudflare
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 1.0.0.1"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 1.1.1.1"' >>/etc/openvpn/server/server.conf
2018-04-01 23:12:05 +02:00
;;
2020-04-27 14:59:19 +02:00
4) # Quad9
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 9.9.9.9"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 149.112.112.112"' >>/etc/openvpn/server/server.conf
2017-11-29 11:21:33 +01:00
;;
2020-04-27 14:59:19 +02:00
5) # Quad9 uncensored
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 9.9.9.10"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 149.112.112.10"' >>/etc/openvpn/server/server.conf
2018-09-24 11:42:29 +02:00
;;
2020-04-27 14:59:19 +02:00
6) # FDN
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 80.67.169.40"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 80.67.169.12"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
7) # DNS.WATCH
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 84.200.69.80"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 84.200.70.40"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
8) # OpenDNS
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 208.67.222.222"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 208.67.220.220"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
9) # Google
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 8.8.8.8"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 8.8.4.4"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
10) # Yandex Basic
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 77.88.8.8"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 77.88.8.1"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
11) # AdGuard DNS
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 94.140.14.14"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 94.140.15.15"' >>/etc/openvpn/server/server.conf
2017-11-12 22:51:54 +01:00
;;
2020-04-27 14:59:19 +02:00
12) # NextDNS
2025-12-12 22:09:18 +01:00
echo 'push "dhcp-option DNS 45.90.28.167"' >>/etc/openvpn/server/server.conf
echo 'push "dhcp-option DNS 45.90.30.167"' >>/etc/openvpn/server/server.conf
2020-03-03 23:04:18 +01:00
;;
2020-04-27 14:59:19 +02:00
13) # Custom DNS
2025-12-12 22:09:18 +01:00
echo "push \"dhcp-option DNS $DNS1 \"" >>/etc/openvpn/server/server.conf
2020-04-27 14:59:19 +02:00
if [[ $DNS2 != "" ]] ; then
2025-12-12 22:09:18 +01:00
echo "push \"dhcp-option DNS $DNS2 \"" >>/etc/openvpn/server/server.conf
2019-08-20 21:02:47 +02:00
fi
;;
2017-11-12 22:51:54 +01:00
esac
2025-12-12 22:09:18 +01:00
echo 'push "redirect-gateway def1 bypass-dhcp"' >>/etc/openvpn/server/server.conf
2018-09-16 17:55:50 +02:00
2018-09-20 00:05:02 +02:00
# IPv6 network settings if needed
2020-04-27 14:59:19 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2018-09-16 17:55:50 +02:00
echo 'server-ipv6 fd42:42:42:42::/112
tun-ipv6
push tun-ipv6
push "route-ipv6 2000::/3"
2025-12-12 22:09:18 +01:00
push "redirect-gateway ipv6"' >>/etc/openvpn/server/server.conf
2018-09-16 17:55:50 +02:00
fi
2020-04-27 14:59:19 +02:00
if [[ $COMPRESSION_ENABLED == "y" ]] ; then
2025-12-12 22:09:18 +01:00
echo "compress $COMPRESSION_ALG " >>/etc/openvpn/server/server.conf
2018-09-22 16:41:28 +02:00
fi
if [[ $DH_TYPE == "1" ]] ; then
2025-12-12 22:09:18 +01:00
echo "dh none" >>/etc/openvpn/server/server.conf
echo "ecdh-curve $DH_CURVE " >>/etc/openvpn/server/server.conf
2018-09-22 16:41:28 +02:00
elif [[ $DH_TYPE == "2" ]] ; then
2025-12-12 22:09:18 +01:00
echo "dh dh.pem" >>/etc/openvpn/server/server.conf
2018-09-22 16:41:28 +02:00
fi
2018-09-22 14:07:51 +02:00
2018-09-22 22:34:10 +02:00
case $TLS_SIG in
2020-04-27 14:59:19 +02:00
1)
2025-12-13 14:32:38 +01:00
echo "tls-crypt-v2 tls-crypt-v2.key" >>/etc/openvpn/server/server.conf
2018-09-22 22:34:10 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-13 14:32:38 +01:00
echo "tls-crypt tls-crypt.key" >>/etc/openvpn/server/server.conf
;;
3)
2025-12-12 22:09:18 +01:00
echo "tls-auth tls-auth.key 0" >>/etc/openvpn/server/server.conf
2018-09-22 22:34:10 +02:00
;;
esac
2018-09-16 17:55:50 +02:00
echo "crl-verify crl.pem
2016-11-28 22:13:32 +01:00
ca ca.crt
2018-01-18 17:19:51 +01:00
cert $SERVER_NAME .crt
2019-06-30 23:06:33 +02:00
key $SERVER_NAME .key
2018-09-22 17:51:38 +02:00
auth $HMAC_ALG
2018-09-22 18:18:36 +02:00
cipher $CIPHER
2025-12-12 10:23:36 +01:00
ignore-unknown-option data-ciphers
data-ciphers $CIPHER
2018-09-22 18:18:36 +02:00
ncp-ciphers $CIPHER
2016-11-28 22:13:32 +01:00
tls-server
tls-version-min 1.2
2025-12-12 00:47:10 +01:00
remote-cert-tls client
2018-09-22 15:11:15 +02:00
tls-cipher $CC_CIPHER
2025-12-12 22:09:18 +01:00
client-config-dir ccd
2018-08-22 22:11:36 +02:00
status /var/log/openvpn/status.log
2025-12-12 22:09:18 +01:00
verb 3" >>/etc/openvpn/server/server.conf
2016-11-24 20:28:49 +01:00
2020-04-10 15:49:07 +00:00
# Create client-config-dir dir
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating client config directory" mkdir -p /etc/openvpn/server/ccd
2018-09-16 22:45:04 +02:00
# Create log dir
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating log directory" mkdir -p /var/log/openvpn
2017-11-12 22:51:54 +01:00
2025-12-12 22:09:18 +01:00
# On distros that use a dedicated OpenVPN user (not "nobody"), e.g., Fedora, RHEL, Arch,
# set ownership so OpenVPN can read config/certs and write to log directory
if [[ $OPENVPN_USER != "nobody" ]] ; then
log_info "Setting ownership for OpenVPN user..."
chown -R " $OPENVPN_USER : $OPENVPN_GROUP " /etc/openvpn/server
chown " $OPENVPN_USER : $OPENVPN_GROUP " /var/log/openvpn
fi
2018-09-16 17:55:50 +02:00
# Enable routing
2025-12-09 15:52:37 +01:00
log_info "Enabling IP forwarding..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating sysctl.d directory" mkdir -p /etc/sysctl.d
2020-10-20 15:44:52 -06:00
echo 'net.ipv4.ip_forward=1' >/etc/sysctl.d/99-openvpn.conf
2020-04-27 14:59:19 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2020-10-20 15:44:52 -06:00
echo 'net.ipv6.conf.all.forwarding=1' >>/etc/sysctl.d/99-openvpn.conf
2017-11-12 22:51:54 +01:00
fi
2019-08-20 17:58:51 +02:00
# Apply sysctl rules
2025-12-09 15:52:37 +01:00
run_cmd "Applying sysctl rules" sysctl --system
2018-07-15 15:25:59 +06:00
2017-11-12 22:51:54 +01:00
# If SELinux is enabled and a custom port was selected, we need this
if hash sestatus 2>/dev/null; then
if sestatus | grep "Current mode" | grep -qs "enforcing" ; then
2020-04-27 14:59:19 +02:00
if [[ $PORT != '1194' ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Configuring SELinux port" semanage port -a -t openvpn_port_t -p " $PROTOCOL " " $PORT "
2017-11-12 22:51:54 +01:00
fi
fi
fi
2018-07-15 15:25:59 +06:00
2018-09-18 14:55:00 +02:00
# Finally, restart and enable OpenVPN
2025-12-12 22:09:18 +01:00
# OpenVPN 2.4+ uses openvpn-server@.service with config in /etc/openvpn/server/
2025-12-09 15:52:37 +01:00
log_info "Configuring OpenVPN service..."
2018-09-24 14:33:08 +02:00
2025-12-12 22:09:18 +01:00
# Find the service file (location and name vary by distro)
# Modern distros: openvpn-server@.service in /usr/lib/systemd/system/ or /lib/systemd/system/
# openSUSE: openvpn@.service (old-style) that we need to adapt
if [[ -f /usr/lib/systemd/system/openvpn-server@.service ]] ; then
SERVICE_SOURCE = "/usr/lib/systemd/system/openvpn-server@.service"
elif [[ -f /lib/systemd/system/openvpn-server@.service ]] ; then
SERVICE_SOURCE = "/lib/systemd/system/openvpn-server@.service"
elif [[ -f /usr/lib/systemd/system/openvpn@.service ]] ; then
# openSUSE uses old-style service, we'll create our own openvpn-server@.service
SERVICE_SOURCE = "/usr/lib/systemd/system/openvpn@.service"
elif [[ -f /lib/systemd/system/openvpn@.service ]] ; then
SERVICE_SOURCE = "/lib/systemd/system/openvpn@.service"
2017-11-12 22:51:54 +01:00
else
2025-12-12 22:09:18 +01:00
log_fatal "Could not find openvpn-server@.service or openvpn@.service file"
fi
2019-06-30 23:06:33 +02:00
2025-12-12 22:09:18 +01:00
# Don't modify package-provided service, copy to /etc/systemd/system/
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Copying OpenVPN service file" cp " $SERVICE_SOURCE " /etc/systemd/system/openvpn-server@.service
2019-06-30 23:06:33 +02:00
2025-12-12 22:09:18 +01:00
# Workaround to fix OpenVPN service on OpenVZ
run_cmd "Patching service file (LimitNPROC)" sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn-server@.service
# Ensure the service uses /etc/openvpn/server/ as working directory
# This is needed for openSUSE which uses old-style paths by default
if grep -q "cd /etc/openvpn/" /etc/systemd/system/openvpn-server@.service; then
run_cmd "Patching service file (paths)" sed -i 's|/etc/openvpn/|/etc/openvpn/server/|g' /etc/systemd/system/openvpn-server@.service
2017-11-12 22:51:54 +01:00
fi
2018-07-15 15:25:59 +06:00
2025-12-12 22:09:18 +01:00
run_cmd "Reloading systemd" systemctl daemon-reload
run_cmd "Enabling OpenVPN service" systemctl enable openvpn-server@server
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
2020-04-27 14:59:19 +02:00
if [[ $DNS == 2 ]] ; then
2018-09-20 00:05:02 +02:00
installUnbound
fi
# Add iptables rules in two scripts
2025-12-09 15:52:37 +01:00
log_info "Configuring firewall rules..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
2018-09-20 00:05:02 +02:00
# Script to add rules
echo "#!/bin/sh
2019-08-20 11:55:43 +02:00
iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -o $NIC -j MASQUERADE
iptables -I INPUT 1 -i tun0 -j ACCEPT
iptables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
iptables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
2020-04-27 14:59:19 +02:00
iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/add-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
2020-04-27 14:59:19 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2019-08-20 11:55:43 +02:00
echo "ip6tables -t nat -I POSTROUTING 1 -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
ip6tables -I INPUT 1 -i tun0 -j ACCEPT
ip6tables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT
2020-04-30 23:42:09 +02:00
ip6tables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT
ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
fi
# Script to remove rules
echo "#!/bin/sh
iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o $NIC -j MASQUERADE
iptables -D INPUT -i tun0 -j ACCEPT
2019-08-20 11:20:24 +02:00
iptables -D FORWARD -i $NIC -o tun0 -j ACCEPT
iptables -D FORWARD -i tun0 -o $NIC -j ACCEPT
2020-04-27 14:59:19 +02:00
iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/rm-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
2020-04-27 14:59:19 +02:00
if [[ $IPV6_SUPPORT == 'y' ]] ; then
2018-09-20 00:05:02 +02:00
echo "ip6tables -t nat -D POSTROUTING -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE
ip6tables -D INPUT -i tun0 -j ACCEPT
2019-08-20 11:20:24 +02:00
ip6tables -D FORWARD -i $NIC -o tun0 -j ACCEPT
2020-04-30 23:42:09 +02:00
ip6tables -D FORWARD -i tun0 -o $NIC -j ACCEPT
ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
fi
2025-12-09 15:52:37 +01:00
run_cmd "Making add-openvpn-rules.sh executable" chmod +x /etc/iptables/add-openvpn-rules.sh
run_cmd "Making rm-openvpn-rules.sh executable" chmod +x /etc/iptables/rm-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
# Handle the rules via a systemd script
echo "[Unit]
Description=iptables rules for OpenVPN
2025-12-13 13:31:54 +01:00
Before=network-online.target
2018-10-08 21:11:30 +02:00
Wants=network-online.target
2018-09-20 00:05:02 +02:00
[Service]
Type=oneshot
ExecStart=/etc/iptables/add-openvpn-rules.sh
ExecStop=/etc/iptables/rm-openvpn-rules.sh
RemainAfterExit=yes
[Install]
2020-04-27 14:59:19 +02:00
WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
2018-09-20 00:05:02 +02:00
# Enable service and apply rules
2025-12-09 15:52:37 +01:00
run_cmd "Reloading systemd" systemctl daemon-reload
run_cmd "Enabling iptables service" systemctl enable iptables-openvpn
run_cmd "Starting iptables service" systemctl start iptables-openvpn
2018-09-20 00:05:02 +02:00
# If the server is behind a NAT, use the correct IP address for the clients to connect to
2020-04-27 14:59:19 +02:00
if [[ $ENDPOINT != "" ]] ; then
2019-02-25 21:30:46 +01:00
IP = $ENDPOINT
2017-11-12 22:51:54 +01:00
fi
2018-09-22 14:21:20 +02:00
2017-11-12 22:51:54 +01:00
# client-template.txt is created so we have a template to add further users later
2025-12-09 15:52:37 +01:00
log_info "Creating client template..."
2025-12-12 22:09:18 +01:00
echo "client" >/etc/openvpn/server/client-template.txt
2020-04-27 14:59:19 +02:00
if [[ $PROTOCOL == 'udp' ]] ; then
2025-12-12 22:09:18 +01:00
echo "proto udp" >>/etc/openvpn/server/client-template.txt
echo "explicit-exit-notify" >>/etc/openvpn/server/client-template.txt
2020-04-27 14:59:19 +02:00
elif [[ $PROTOCOL == 'tcp' ]] ; then
2025-12-12 22:09:18 +01:00
echo "proto tcp-client" >>/etc/openvpn/server/client-template.txt
2017-11-12 22:51:54 +01:00
fi
echo "remote $IP $PORT
2015-09-12 21:48:08 +02:00
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
2018-01-18 17:36:31 +01:00
verify-x509-name $SERVER_NAME name
2018-09-22 17:51:38 +02:00
auth $HMAC_ALG
2017-08-27 13:59:08 -05:00
auth-nocache
2018-09-22 18:18:36 +02:00
cipher $CIPHER
2025-12-12 10:23:36 +01:00
ignore-unknown-option data-ciphers
data-ciphers $CIPHER
ncp-ciphers $CIPHER
2016-11-28 22:13:32 +01:00
tls-client
2016-04-10 18:53:29 +02:00
tls-version-min 1.2
2018-09-22 15:11:15 +02:00
tls-cipher $CC_CIPHER
2020-04-27 12:19:25 +00:00
ignore-unknown-option block-outside-dns
2018-09-20 17:16:04 +02:00
setenv opt block-outside-dns # Prevent Windows 10 DNS leak
2025-12-12 22:09:18 +01:00
verb 3" >>/etc/openvpn/server/client-template.txt
2016-11-28 22:13:32 +01:00
2020-04-27 14:59:19 +02:00
if [[ $COMPRESSION_ENABLED == "y" ]] ; then
2025-12-12 22:09:18 +01:00
echo "compress $COMPRESSION_ALG " >>/etc/openvpn/server/client-template.txt
2020-04-27 14:59:19 +02:00
fi
2018-09-22 14:07:51 +02:00
2017-11-12 22:51:54 +01:00
# Generate the custom client.ovpn
2025-12-13 15:57:02 -03:00
if [[ $NEW_CLIENT == "n" ]] ; then
log_info "No clients added. To add clients, simply run the script again."
else
log_info "Generating first client certificate..."
newClient
log_success "If you want to add more clients, you simply need to run this script another time!"
fi
2018-09-20 00:05:02 +02:00
}
2025-12-09 21:49:19 +01:00
# Helper function to get the home directory for storing client configs
function getHomeDir() {
local client = " $1 "
if [ -e "/home/ ${ client } " ] ; then
echo "/home/ ${ client } "
elif [ " ${ SUDO_USER } " ] ; then
if [ " ${ SUDO_USER } " == "root" ] ; then
echo "/root"
else
echo "/home/ ${ SUDO_USER } "
fi
else
echo "/root"
fi
}
# Helper function to regenerate the CRL after certificate changes
function regenerateCRL() {
2025-12-09 23:33:57 +01:00
export EASYRSA_CRL_DAYS = $DEFAULT_CRL_VALIDITY_DURATION_DAYS
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Regenerating CRL" ./easyrsa gen-crl
2025-12-12 22:09:18 +01:00
run_cmd "Removing old CRL" rm -f /etc/openvpn/server/crl.pem
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Copying new CRL" cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem
2025-12-12 22:09:18 +01:00
run_cmd "Setting CRL permissions" chmod 644 /etc/openvpn/server/crl.pem
2025-12-09 21:49:19 +01:00
}
# Helper function to generate .ovpn client config file
function generateClientConfig() {
local client = " $1 "
local home_dir = " $2 "
2025-12-13 14:32:38 +01:00
# Determine if we use tls-crypt-v2, tls-crypt, or tls-auth
2025-12-09 21:49:19 +01:00
local tls_sig = ""
2025-12-13 14:32:38 +01:00
if grep -qs "^tls-crypt-v2" /etc/openvpn/server/server.conf; then
2025-12-09 21:49:19 +01:00
tls_sig = "1"
2025-12-13 14:32:38 +01:00
elif grep -qs "^tls-crypt" /etc/openvpn/server/server.conf; then
2025-12-09 21:49:19 +01:00
tls_sig = "2"
2025-12-13 14:32:38 +01:00
elif grep -qs "^tls-auth" /etc/openvpn/server/server.conf; then
tls_sig = "3"
2025-12-09 21:49:19 +01:00
fi
# Generate the custom client.ovpn
2025-12-12 22:09:18 +01:00
run_cmd "Creating client config" cp /etc/openvpn/server/client-template.txt " $home_dir / $client .ovpn"
2025-12-09 21:49:19 +01:00
{
echo "<ca>"
2025-12-12 22:09:18 +01:00
cat "/etc/openvpn/server/easy-rsa/pki/ca.crt"
2025-12-09 21:49:19 +01:00
echo "</ca>"
echo "<cert>"
2025-12-12 22:09:18 +01:00
awk '/BEGIN/,/END CERTIFICATE/' "/etc/openvpn/server/easy-rsa/pki/issued/ $client .crt"
2025-12-09 21:49:19 +01:00
echo "</cert>"
echo "<key>"
2025-12-12 22:09:18 +01:00
cat "/etc/openvpn/server/easy-rsa/pki/private/ $client .key"
2025-12-09 21:49:19 +01:00
echo "</key>"
case $tls_sig in
1)
2025-12-13 14:32:38 +01:00
# Generate per-client tls-crypt-v2 key using secure temp file
tls_crypt_v2_tmpfile = $( mktemp)
if ! openvpn --tls-crypt-v2 /etc/openvpn/server/tls-crypt-v2.key \
--genkey tls-crypt-v2-client " $tls_crypt_v2_tmpfile " ; then
rm -f " $tls_crypt_v2_tmpfile "
log_error "Failed to generate tls-crypt-v2 client key"
exit 1
fi
echo "<tls-crypt-v2>"
cat " $tls_crypt_v2_tmpfile "
echo "</tls-crypt-v2>"
rm -f " $tls_crypt_v2_tmpfile "
;;
2)
2025-12-09 21:49:19 +01:00
echo "<tls-crypt>"
2025-12-12 22:09:18 +01:00
cat /etc/openvpn/server/tls-crypt.key
2025-12-09 21:49:19 +01:00
echo "</tls-crypt>"
;;
2025-12-13 14:32:38 +01:00
3)
2025-12-09 21:49:19 +01:00
echo "key-direction 1"
echo "<tls-auth>"
2025-12-12 22:09:18 +01:00
cat /etc/openvpn/server/tls-auth.key
2025-12-09 21:49:19 +01:00
echo "</tls-auth>"
;;
esac
} >>" $home_dir / $client .ovpn"
}
# Helper function to list valid clients and select one
# Arguments: show_expiry (optional, "true" to show expiry info)
# Sets global variables:
# CLIENT - the selected client name
# CLIENTNUMBER - the selected client number (1-based index)
# NUMBEROFCLIENTS - total count of valid clients
function selectClient() {
local show_expiry = " ${ 1 :- false } "
local client_number
2025-12-12 22:09:18 +01:00
NUMBEROFCLIENTS = $( tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V" )
2025-12-09 21:49:19 +01:00
if [[ $NUMBEROFCLIENTS == '0' ]] ; then
log_fatal "You have no existing clients!"
fi
if [[ $show_expiry == "true" ]] ; then
local i = 1
while read -r client; do
2025-12-12 22:09:18 +01:00
local client_cert = "/etc/openvpn/server/easy-rsa/pki/issued/ $client .crt"
2025-12-09 21:49:19 +01:00
local days
days = $( getDaysUntilExpiry " $client_cert " )
local expiry
expiry = $( formatExpiry " $days " )
echo " $i ) $client $expiry "
(( i++))
2025-12-12 22:09:18 +01:00
done < <( tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2)
2025-12-09 21:49:19 +01:00
else
2025-12-12 22:09:18 +01:00
tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
2025-12-09 21:49:19 +01:00
fi
until [[ ${ CLIENTNUMBER :- $client_number } -ge 1 && ${ CLIENTNUMBER :- $client_number } -le $NUMBEROFCLIENTS ]] ; do
if [[ $NUMBEROFCLIENTS == '1' ]] ; then
read -rp "Select one client [1]: " client_number
else
read -rp "Select one client [1- $NUMBEROFCLIENTS ]: " client_number
fi
done
CLIENTNUMBER = " ${ CLIENTNUMBER :- $client_number } "
2025-12-12 22:09:18 +01:00
CLIENT = $( tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n " $CLIENTNUMBER " p)
2025-12-09 21:49:19 +01:00
}
2025-12-13 19:17:30 +01:00
function listClients() {
log_header "Client Certificates"
local index_file = "/etc/openvpn/server/easy-rsa/pki/index.txt"
local number_of_clients
# Exclude server certificates (CN starting with server_)
number_of_clients = $( tail -n +2 " $index_file " | grep "^[VR]" | grep -cv "/CN=server_" )
if [[ $number_of_clients == '0' ]] ; then
log_warn "You have no existing client certificates!"
return
fi
log_info "Found $number_of_clients client certificate(s)"
log_menu ""
printf " %-25s %-10s %-12s %s\n" "Name" "Status" "Expiry" "Remaining"
printf " %-25s %-10s %-12s %s\n" "----" "------" "------" "---------"
local cert_dir = "/etc/openvpn/server/easy-rsa/pki/issued"
# Parse index.txt and sort by expiry date (oldest first)
# Exclude server certificates (CN starting with server_)
{
while read -r line; do
local status = " ${ line : 0 : 1 } "
local client_name
client_name = $( echo " $line " | sed 's/.*\/CN=//' )
# Format status
local status_text
if [[ " $status " == "V" ]] ; then
status_text = "Valid"
elif [[ " $status " == "R" ]] ; then
status_text = "Revoked"
else
status_text = "Unknown"
fi
# Get expiry date from certificate file
local cert_file = " $cert_dir / $client_name .crt"
local expiry_date = "unknown"
local relative = "unknown"
if [[ -f " $cert_file " ]] ; then
# Get expiry from certificate (format: notAfter=Mon DD HH:MM:SS YYYY GMT)
local enddate
enddate = $( openssl x509 -enddate -noout -in " $cert_file " 2>/dev/null | cut -d= -f2)
if [[ -n " $enddate " ]] ; then
# Parse date and convert to epoch
local expiry_epoch
expiry_epoch = $( date -d " $enddate " +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" " $enddate " +%s 2>/dev/null)
if [[ -n " $expiry_epoch " ]] ; then
# Format as YYYY-MM-DD
expiry_date = $( date -d "@ $expiry_epoch " +%Y-%m-%d 2>/dev/null || date -r " $expiry_epoch " +%Y-%m-%d 2>/dev/null)
# Calculate days remaining
local now_epoch days_remaining
now_epoch = $( date +%s)
days_remaining = $(( ( expiry_epoch - now_epoch) / 86400 ))
if [[ $days_remaining -lt 0 ]] ; then
relative = " $(( - days_remaining)) days ago"
elif [[ $days_remaining -eq 0 ]] ; then
relative = "today"
elif [[ $days_remaining -eq 1 ]] ; then
relative = "1 day"
else
relative = " $days_remaining days"
fi
fi
fi
fi
printf " %-25s %-10s %-12s %s\n" " $client_name " " $status_text " " $expiry_date " " $relative "
done < <( tail -n +2 " $index_file " | grep "^[VR]" | grep -v "/CN=server_" | sort -t$'\t' -k2)
}
log_menu ""
}
2020-04-27 14:59:19 +02:00
function newClient() {
2025-12-09 15:52:37 +01:00
log_header "New Client Setup"
log_prompt "Tell me a name for the client."
log_prompt "The name must consist of alphanumeric character. It may also include an underscore or a dash."
2018-09-20 00:05:02 +02:00
2020-07-17 22:10:31 +03:00
until [[ $CLIENT = ~ ^[ a-zA-Z0-9_-] +$ ]] ; do
2018-09-20 00:05:02 +02:00
read -rp "Client name: " -e CLIENT
done
2025-12-09 23:33:57 +01:00
if [[ -z $CLIENT_CERT_DURATION_DAYS ]] || ! [[ $CLIENT_CERT_DURATION_DAYS = ~ ^[ 0-9] +$ ]] || [[ $CLIENT_CERT_DURATION_DAYS -lt 1 ]] ; then
2025-12-09 22:34:29 +03:30
log_menu ""
log_prompt "How many days should the client certificate be valid for?"
2025-12-09 23:33:57 +01:00
until [[ $CLIENT_CERT_DURATION_DAYS = ~ ^[ 0-9] +$ ]] && [[ $CLIENT_CERT_DURATION_DAYS -ge 1 ]] ; do
read -rp "Certificate validity (days): " -e -i $DEFAULT_CERT_VALIDITY_DURATION_DAYS CLIENT_CERT_DURATION_DAYS
2025-12-09 21:49:19 +01:00
done
2025-12-09 22:34:29 +03:30
fi
2025-12-09 15:52:37 +01:00
log_menu ""
log_prompt "Do you want to protect the configuration file with a password?"
log_prompt "(e.g. encrypt the private key with a password)"
log_menu " 1) Add a passwordless client"
log_menu " 2) Use a password for the client"
2018-09-20 00:05:02 +02:00
2020-04-27 14:59:19 +02:00
until [[ $PASS = ~ ^[ 1-2] $ ]] ; do
2018-09-20 00:05:02 +02:00
read -rp "Select an option [1-2]: " -e -i 1 PASS
done
2025-12-12 22:09:18 +01:00
CLIENTEXISTS = $( tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -E "^V" | grep -c -E "/CN= $CLIENT \$" )
2025-12-11 11:15:34 -05:00
if [[ $CLIENTEXISTS != '0' ]] ; then
2025-12-09 15:52:37 +01:00
log_error "The specified client CN was already found in easy-rsa, please choose another name."
2020-04-27 17:45:58 +02:00
exit
2020-04-27 04:56:34 -07:00
else
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2025-12-09 15:52:37 +01:00
log_info "Generating client certificate..."
2025-12-09 23:33:57 +01:00
export EASYRSA_CERT_EXPIRE = $CLIENT_CERT_DURATION_DAYS
2020-04-27 04:56:34 -07:00
case $PASS in
2020-04-27 14:59:19 +02:00
1)
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Building client certificate" ./easyrsa --batch build-client-full " $CLIENT " nopass
2020-04-27 04:56:34 -07:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-13 15:42:43 +01:00
if [[ -z " $PASSPHRASE " ]] ; then
log_warn "You will be asked for the client password below"
# Run directly (not via run_cmd) so password prompt is visible to user
if ! ./easyrsa --batch build-client-full " $CLIENT " ; then
log_fatal "Building client certificate failed"
fi
else
log_info "Using provided passphrase for client certificate"
# Use env var to avoid exposing passphrase in install log
export EASYRSA_PASSPHRASE = " $PASSPHRASE "
run_cmd_fatal "Building client certificate" ./easyrsa --batch --passin= env:EASYRSA_PASSPHRASE --passout= env:EASYRSA_PASSPHRASE build-client-full " $CLIENT "
unset EASYRSA_PASSPHRASE
2025-12-13 13:31:54 +01:00
fi
2020-04-27 04:56:34 -07:00
;;
esac
2025-12-09 23:33:57 +01:00
log_success "Client $CLIENT added and is valid for $CLIENT_CERT_DURATION_DAYS days."
2020-04-27 04:56:34 -07:00
fi
2018-09-20 00:05:02 +02:00
2025-12-09 21:49:19 +01:00
# Generate the .ovpn config file
homeDir = $( getHomeDir " $CLIENT " )
generateClientConfig " $CLIENT " " $homeDir "
2018-09-20 00:05:02 +02:00
2025-12-09 15:52:37 +01:00
log_menu ""
log_success "The configuration file has been written to $homeDir / $CLIENT .ovpn."
log_info "Download the .ovpn file and import it in your OpenVPN client."
2019-02-25 21:54:36 +01:00
exit 0
2018-09-20 00:05:02 +02:00
}
2020-04-27 14:59:19 +02:00
function revokeClient() {
2025-12-09 15:52:37 +01:00
log_header "Revoke Client"
log_prompt "Select the existing client certificate you want to revoke"
2025-12-09 21:49:19 +01:00
selectClient
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2025-12-09 15:52:37 +01:00
log_info "Revoking certificate for $CLIENT ..."
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Revoking certificate" ./easyrsa --batch revoke-issued " $CLIENT "
2025-12-09 21:49:19 +01:00
regenerateCRL
2025-12-09 15:52:37 +01:00
run_cmd "Removing client config from /home" find /home/ -maxdepth 2 -name " $CLIENT .ovpn" -delete
run_cmd "Removing client config from /root" rm -f "/root/ $CLIENT .ovpn"
2025-12-12 22:09:18 +01:00
run_cmd "Removing IP assignment" sed -i "/^ $CLIENT ,.*/d" /etc/openvpn/server/ipp.txt
run_cmd "Backing up index" cp /etc/openvpn/server/easy-rsa/pki/index.txt{ ,.bk}
2025-12-09 15:52:37 +01:00
log_success "Certificate for client $CLIENT revoked."
2018-09-20 00:05:02 +02:00
}
2025-12-09 21:49:19 +01:00
function renewClient() {
2025-12-09 23:33:57 +01:00
local homeDir client_cert_duration_days
2025-12-09 21:49:19 +01:00
log_header "Renew Client Certificate"
log_prompt "Select the existing client certificate you want to renew"
selectClient "true"
2025-12-09 23:33:57 +01:00
# Allow user to specify renewal duration (use CLIENT_CERT_DURATION_DAYS env var for headless mode)
if [[ -z $CLIENT_CERT_DURATION_DAYS ]] || ! [[ $CLIENT_CERT_DURATION_DAYS = ~ ^[ 0-9] +$ ]] || [[ $CLIENT_CERT_DURATION_DAYS -lt 1 ]] ; then
2025-12-09 21:49:19 +01:00
log_menu ""
log_prompt "How many days should the renewed certificate be valid for?"
2025-12-09 23:33:57 +01:00
until [[ $client_cert_duration_days = ~ ^[ 0-9] +$ ]] && [[ $client_cert_duration_days -ge 1 ]] ; do
read -rp "Certificate validity (days): " -e -i $DEFAULT_CERT_VALIDITY_DURATION_DAYS client_cert_duration_days
2025-12-09 21:49:19 +01:00
done
else
2025-12-09 23:33:57 +01:00
client_cert_duration_days = $CLIENT_CERT_DURATION_DAYS
2025-12-09 21:49:19 +01:00
fi
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2025-12-09 21:49:19 +01:00
log_info "Renewing certificate for $CLIENT ..."
# Backup the old certificate before renewal
2025-12-12 22:09:18 +01:00
run_cmd "Backing up old certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/ $CLIENT .crt" "/etc/openvpn/server/easy-rsa/pki/issued/ $CLIENT .crt.bak"
2025-12-09 21:49:19 +01:00
# Renew the certificate (keeps the same private key)
2025-12-09 23:33:57 +01:00
export EASYRSA_CERT_EXPIRE = $client_cert_duration_days
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Renewing certificate" ./easyrsa --batch renew " $CLIENT "
2025-12-09 21:49:19 +01:00
# Revoke the old certificate
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Revoking old certificate" ./easyrsa --batch revoke-renewed " $CLIENT "
2025-12-09 21:49:19 +01:00
# Regenerate the CRL
regenerateCRL
# Regenerate the .ovpn file with the new certificate
homeDir = $( getHomeDir " $CLIENT " )
generateClientConfig " $CLIENT " " $homeDir "
log_menu ""
2025-12-09 23:33:57 +01:00
log_success "Certificate for client $CLIENT renewed and is valid for $client_cert_duration_days days."
2025-12-09 21:49:19 +01:00
log_info "The new configuration file has been written to $homeDir / $CLIENT .ovpn."
log_info "Download the new .ovpn file and import it in your OpenVPN client."
}
function renewServer() {
2025-12-09 23:33:57 +01:00
local server_name server_cert_duration_days
2025-12-09 21:49:19 +01:00
log_header "Renew Server Certificate"
2025-12-12 22:09:18 +01:00
# Get the server name from the config (extract basename since path may be relative)
server_name = $( basename " $( grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2) " .crt)
2025-12-09 21:49:19 +01:00
if [[ -z " $server_name " ]] ; then
2025-12-12 22:09:18 +01:00
log_fatal "Could not determine server certificate name from /etc/openvpn/server/server.conf"
2025-12-09 21:49:19 +01:00
fi
log_prompt "This will renew the server certificate: $server_name "
log_warn "The OpenVPN service will be restarted after renewal."
if [[ -z $CONTINUE ]] ; then
read -rp "Do you want to continue? [y/n]: " -e -i n CONTINUE
fi
if [[ $CONTINUE != "y" ]] ; then
log_info "Renewal aborted."
return
fi
2025-12-09 23:33:57 +01:00
# Allow user to specify renewal duration (use SERVER_CERT_DURATION_DAYS env var for headless mode)
if [[ -z $SERVER_CERT_DURATION_DAYS ]] || ! [[ $SERVER_CERT_DURATION_DAYS = ~ ^[ 0-9] +$ ]] || [[ $SERVER_CERT_DURATION_DAYS -lt 1 ]] ; then
2025-12-09 21:49:19 +01:00
log_menu ""
log_prompt "How many days should the renewed certificate be valid for?"
2025-12-09 23:33:57 +01:00
until [[ $server_cert_duration_days = ~ ^[ 0-9] +$ ]] && [[ $server_cert_duration_days -ge 1 ]] ; do
read -rp "Certificate validity (days): " -e -i $DEFAULT_CERT_VALIDITY_DURATION_DAYS server_cert_duration_days
2025-12-09 21:49:19 +01:00
done
else
2025-12-09 23:33:57 +01:00
server_cert_duration_days = $SERVER_CERT_DURATION_DAYS
2025-12-09 21:49:19 +01:00
fi
2025-12-12 22:09:18 +01:00
cd /etc/openvpn/server/easy-rsa/ || return
2025-12-09 21:49:19 +01:00
log_info "Renewing server certificate..."
# Backup the old certificate before renewal
2025-12-12 22:09:18 +01:00
run_cmd "Backing up old certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/ $server_name .crt" "/etc/openvpn/server/easy-rsa/pki/issued/ $server_name .crt.bak"
2025-12-09 21:49:19 +01:00
# Renew the certificate (keeps the same private key)
2025-12-09 23:33:57 +01:00
export EASYRSA_CERT_EXPIRE = $server_cert_duration_days
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Renewing certificate" ./easyrsa --batch renew " $server_name "
2025-12-09 21:49:19 +01:00
# Revoke the old certificate
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Revoking old certificate" ./easyrsa --batch revoke-renewed " $server_name "
2025-12-09 21:49:19 +01:00
# Regenerate the CRL
regenerateCRL
2025-12-12 22:09:18 +01:00
# Copy the new certificate to /etc/openvpn/server/
2025-12-13 13:31:54 +01:00
run_cmd_fatal "Copying new certificate" cp "/etc/openvpn/server/easy-rsa/pki/issued/ $server_name .crt" /etc/openvpn/server/
2025-12-09 21:49:19 +01:00
# Restart OpenVPN
log_info "Restarting OpenVPN service..."
2025-12-12 22:09:18 +01:00
run_cmd "Restarting OpenVPN" systemctl restart openvpn-server@server
2025-12-09 21:49:19 +01:00
2025-12-09 23:33:57 +01:00
log_success "Server certificate renewed successfully and is valid for $server_cert_duration_days days."
2025-12-09 21:49:19 +01:00
}
function getDaysUntilExpiry() {
local cert_file = " $1 "
if [[ -f " $cert_file " ]] ; then
local expiry_date
expiry_date = $( openssl x509 -in " $cert_file " -noout -enddate | cut -d= -f2)
local expiry_epoch
expiry_epoch = $( date -d " $expiry_date " +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" " $expiry_date " +%s 2>/dev/null)
if [[ -z " $expiry_epoch " ]] ; then
echo "?"
return
fi
local now_epoch
now_epoch = $( date +%s)
echo $(( ( expiry_epoch - now_epoch) / 86400 ))
else
echo "?"
fi
}
function formatExpiry() {
local days = " $1 "
if [[ " $days " == "?" ]] ; then
echo "(unknown expiry)"
elif [[ $days -lt 0 ]] ; then
echo "(EXPIRED $(( - days)) days ago)"
elif [[ $days -eq 0 ]] ; then
echo "(expires today)"
elif [[ $days -eq 1 ]] ; then
echo "(expires in 1 day)"
else
echo "(expires in $days days)"
fi
}
function renewMenu() {
local server_name server_cert server_days server_expiry renew_option
log_header "Certificate Renewal"
2025-12-12 22:09:18 +01:00
# Get server certificate expiry for menu display (extract basename since path may be relative)
server_name = $( basename " $( grep '^cert ' /etc/openvpn/server/server.conf | cut -d ' ' -f 2) " .crt)
2025-12-09 21:49:19 +01:00
if [[ -z " $server_name " ]] ; then
server_expiry = "(unknown expiry)"
else
2025-12-12 22:09:18 +01:00
server_cert = "/etc/openvpn/server/easy-rsa/pki/issued/ $server_name .crt"
2025-12-09 21:49:19 +01:00
server_days = $( getDaysUntilExpiry " $server_cert " )
server_expiry = $( formatExpiry " $server_days " )
fi
log_menu ""
log_prompt "What do you want to renew?"
log_menu " 1) Renew a client certificate"
log_menu " 2) Renew the server certificate $server_expiry "
log_menu " 3) Back to main menu"
until [[ ${ RENEW_OPTION :- $renew_option } = ~ ^[ 1-3] $ ]] ; do
read -rp "Select an option [1-3]: " renew_option
done
renew_option = " ${ RENEW_OPTION :- $renew_option } "
case $renew_option in
1)
renewClient
;;
2)
renewServer
;;
3)
manageMenu
;;
esac
}
2020-04-27 14:59:19 +02:00
function removeUnbound() {
2025-12-11 13:14:56 +01:00
run_cmd "Removing OpenVPN Unbound config" rm -f /etc/unbound/unbound.conf.d/openvpn.conf
# Clean up include directive if conf.d directory is now empty
if [[ -d /etc/unbound/unbound.conf.d ]] && [[ -z " $( ls -A /etc/unbound/unbound.conf.d) " ]] ; then
run_cmd "Cleaning up Unbound include directive" \
sed -i '/^include: "\/etc\/unbound\/unbound\.conf\.d\/\*\.conf"$/d' /etc/unbound/unbound.conf
fi
2018-09-20 00:05:02 +02:00
2018-09-22 15:23:01 +02:00
until [[ $REMOVE_UNBOUND = ~ ( y| n) ]] ; do
2025-12-09 15:52:37 +01:00
log_info "If you were already using Unbound before installing OpenVPN, I removed the configuration related to OpenVPN."
2018-09-20 00:05:02 +02:00
read -rp "Do you want to completely remove Unbound? [y/n]: " -e REMOVE_UNBOUND
done
2020-04-27 14:59:19 +02:00
if [[ $REMOVE_UNBOUND == 'y' ]] ; then
2025-12-09 15:52:37 +01:00
log_info "Removing Unbound..."
run_cmd "Stopping Unbound" systemctl stop unbound
2018-09-20 00:05:02 +02:00
2020-04-27 14:59:19 +02:00
if [[ $OS = ~ ( debian| ubuntu) ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing Unbound" apt-get remove --purge -y unbound
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'arch' ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing Unbound" pacman --noconfirm -R unbound
2025-12-10 17:54:00 +01:00
elif [[ $OS = ~ ( centos| oracle) ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing Unbound" yum remove -y unbound
2025-12-10 17:54:00 +01:00
elif [[ $OS = ~ ( fedora| amzn2023) ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing Unbound" dnf remove -y unbound
2025-12-12 04:22:12 +08:00
elif [[ $OS == 'opensuse' ]] ; then
run_cmd "Removing Unbound" zypper remove -y unbound
2018-09-20 00:05:02 +02:00
fi
2025-12-09 15:52:37 +01:00
run_cmd "Removing Unbound config" rm -rf /etc/unbound/
log_success "Unbound removed!"
2018-09-20 00:05:02 +02:00
else
2025-12-09 15:52:37 +01:00
run_cmd "Restarting Unbound" systemctl restart unbound
log_info "Unbound wasn't removed."
2018-09-20 00:05:02 +02:00
fi
}
2020-04-27 14:59:19 +02:00
function removeOpenVPN() {
2025-12-09 15:52:37 +01:00
log_header "Remove OpenVPN"
2018-09-20 00:05:02 +02:00
read -rp "Do you really want to remove OpenVPN? [y/n]: " -e -i n REMOVE
2020-04-27 14:59:19 +02:00
if [[ $REMOVE == 'y' ]] ; then
2018-09-20 00:05:02 +02:00
# Get OpenVPN port from the configuration
2025-12-12 22:09:18 +01:00
PORT = $( grep '^port ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
PROTOCOL = $( grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
2018-09-20 00:05:02 +02:00
# Stop OpenVPN
2025-12-09 15:52:37 +01:00
log_info "Stopping OpenVPN service..."
2025-12-12 22:09:18 +01:00
run_cmd "Disabling OpenVPN service" systemctl disable openvpn-server@server
run_cmd "Stopping OpenVPN service" systemctl stop openvpn-server@server
# Remove customised service
run_cmd "Removing service file" rm -f /etc/systemd/system/openvpn-server@.service
2018-09-20 00:05:02 +02:00
# Remove the iptables rules related to the script
2025-12-09 15:52:37 +01:00
log_info "Removing iptables rules..."
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
2018-09-20 00:05:02 +02:00
# Cleanup
2025-12-09 15:52:37 +01:00
run_cmd "Disabling iptables service" systemctl disable iptables-openvpn
run_cmd "Removing iptables service file" rm /etc/systemd/system/iptables-openvpn.service
run_cmd "Reloading systemd" systemctl daemon-reload
run_cmd "Removing iptables add script" rm /etc/iptables/add-openvpn-rules.sh
run_cmd "Removing iptables rm script" rm /etc/iptables/rm-openvpn-rules.sh
2018-09-20 00:05:02 +02:00
# SELinux
if hash sestatus 2>/dev/null; then
if sestatus | grep "Current mode" | grep -qs "enforcing" ; then
2020-04-27 16:05:51 +02:00
if [[ $PORT != '1194' ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing SELinux port" semanage port -d -t openvpn_port_t -p " $PROTOCOL " " $PORT "
2018-09-20 00:05:02 +02:00
fi
fi
fi
2025-12-09 15:52:37 +01:00
log_info "Removing OpenVPN package..."
2020-04-27 14:59:19 +02:00
if [[ $OS = ~ ( debian| ubuntu) ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing OpenVPN" apt-get remove --purge -y openvpn
2025-12-09 19:45:56 +01:00
# Remove OpenVPN official repository and GPG key
if [[ -e /etc/apt/sources.list.d/openvpn-aptrepo.list ]] ; then
run_cmd "Removing OpenVPN repo" rm /etc/apt/sources.list.d/openvpn-aptrepo.list
fi
if [[ -e /etc/apt/keyrings/openvpn-repo-public.asc ]] ; then
run_cmd "Removing OpenVPN GPG key" rm /etc/apt/keyrings/openvpn-repo-public.asc
2018-09-22 11:41:26 +02:00
fi
2025-12-09 19:45:56 +01:00
run_cmd "Updating package lists" apt-get update
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'arch' ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing OpenVPN" pacman --noconfirm -R openvpn
2025-12-09 19:45:56 +01:00
elif [[ $OS = ~ ( centos| oracle) ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing OpenVPN" yum remove -y openvpn
2025-12-09 19:45:56 +01:00
# Disable Copr repo if it was enabled
if command -v dnf & >/dev/null; then
run_cmd "Disabling OpenVPN Copr repo" dnf copr disable -y @OpenVPN/openvpn-release-2.6 2>/dev/null || true
else
run_cmd "Disabling OpenVPN Copr repo" yum copr disable -y @OpenVPN/openvpn-release-2.6 2>/dev/null || true
fi
2025-12-10 17:54:00 +01:00
elif [[ $OS == 'amzn2023' ]] ; then
run_cmd "Removing OpenVPN" dnf remove -y openvpn
2020-04-27 14:59:19 +02:00
elif [[ $OS == 'fedora' ]] ; then
2025-12-09 15:52:37 +01:00
run_cmd "Removing OpenVPN" dnf remove -y openvpn
2025-12-12 04:22:12 +08:00
elif [[ $OS == 'opensuse' ]] ; then
run_cmd "Removing OpenVPN" zypper remove -y openvpn
2018-09-20 00:05:02 +02:00
fi
# Cleanup
2025-12-09 15:52:37 +01:00
run_cmd "Removing client configs from /home" find /home/ -maxdepth 2 -name "*.ovpn" -delete
run_cmd "Removing client configs from /root" find /root/ -maxdepth 1 -name "*.ovpn" -delete
run_cmd "Removing /etc/openvpn" rm -rf /etc/openvpn
run_cmd "Removing OpenVPN docs" rm -rf /usr/share/doc/openvpn*
run_cmd "Removing sysctl config" rm -f /etc/sysctl.d/99-openvpn.conf
run_cmd "Removing OpenVPN logs" rm -rf /var/log/openvpn
2018-09-20 00:05:02 +02:00
# Unbound
2025-12-11 13:14:56 +01:00
if [[ -e /etc/unbound/unbound.conf.d/openvpn.conf ]] ; then
2018-09-20 00:05:02 +02:00
removeUnbound
fi
2025-12-09 15:52:37 +01:00
log_success "OpenVPN removed!"
2018-09-20 00:05:02 +02:00
else
2025-12-09 15:52:37 +01:00
log_info "Removal aborted!"
2018-09-20 00:05:02 +02:00
fi
}
2020-04-27 14:59:19 +02:00
function manageMenu() {
2025-12-09 21:49:19 +01:00
local menu_option
2025-12-09 15:52:37 +01:00
log_header "OpenVPN Management"
log_prompt "The git repository is available at: https://github.com/angristan/openvpn-install"
log_success "OpenVPN is already installed."
log_menu ""
log_prompt "What do you want to do?"
log_menu " 1) Add a new user"
2025-12-13 19:17:30 +01:00
log_menu " 2) List client certificates"
log_menu " 3) Revoke existing user"
log_menu " 4) Renew certificate"
log_menu " 5) Remove OpenVPN"
log_menu " 6) Exit"
until [[ ${ MENU_OPTION :- $menu_option } = ~ ^[ 1-6] $ ]] ; do
read -rp "Select an option [1-6]: " menu_option
2018-09-20 00:05:02 +02:00
done
2025-12-09 21:49:19 +01:00
menu_option = " ${ MENU_OPTION :- $menu_option } "
2018-09-20 00:05:02 +02:00
2025-12-09 21:49:19 +01:00
case $menu_option in
2020-04-27 14:59:19 +02:00
1)
newClient
2018-09-20 00:05:02 +02:00
;;
2020-04-27 14:59:19 +02:00
2)
2025-12-13 19:17:30 +01:00
listClients
2018-09-20 00:05:02 +02:00
;;
2020-04-27 14:59:19 +02:00
3)
2025-12-13 19:17:30 +01:00
revokeClient
2018-09-20 00:05:02 +02:00
;;
2020-04-27 14:59:19 +02:00
4)
2025-12-13 19:17:30 +01:00
renewMenu
2025-12-09 21:49:19 +01:00
;;
5)
2025-12-13 19:17:30 +01:00
removeOpenVPN
;;
6)
2020-04-27 14:59:19 +02:00
exit 0
2018-09-20 00:05:02 +02:00
;;
esac
}
# Check for root, TUN, OS...
initialCheck
# Check if OpenVPN is already installed
2025-12-12 22:09:18 +01:00
if [[ -e /etc/openvpn/server/server.conf ]] ; then
2018-09-20 00:05:02 +02:00
manageMenu
else
installOpenVPN
2013-08-05 00:58:43 +02:00
fi