feat(openvpn): add interactive subnet prompt in firewall config

Introduce an interactive prompt for specifying allowed subnets for VPN clients (10.8.0.0/24). The script now builds custom iptables rules based on user input and persists them via a systemd service, while retaining the original EasyRSA extraction and configuration.
This commit is contained in:
@bl4ckarch 2025-03-26 14:56:09 +01:00
parent e30c8a38e7
commit 0b89cec77a
No known key found for this signature in database

View File

@ -3,7 +3,7 @@
# https://github.com/Nyr/openvpn-install # https://github.com/Nyr/openvpn-install
# #
# Copyright (c) 2013 Nyr. Released under the MIT License. # Copyright (c) 2013 Nyr. Released under the MIT License.
#
# Detect Debian users running the script with "sh" instead of bash # Detect Debian users running the script with "sh" instead of bash
if readlink /proc/$$/exe | grep -q "dash"; then if readlink /proc/$$/exe | grep -q "dash"; then
@ -14,8 +14,7 @@ fi
# Discard stdin. Needed when running from a one-liner which includes a newline # Discard stdin. Needed when running from a one-liner which includes a newline
read -N 999999 -t 0.001 read -N 999999 -t 0.001
# Detect OS # Detect OS (with some variables kept for convenience)
# $os_version variables aren't always in use, but are kept here for convenience
if grep -qs "ubuntu" /etc/os-release; then if grep -qs "ubuntu" /etc/os-release; then
os="ubuntu" os="ubuntu"
os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.') os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')
@ -103,7 +102,7 @@ new_client () {
} }
if [[ ! -e /etc/openvpn/server/server.conf ]]; then if [[ ! -e /etc/openvpn/server/server.conf ]]; then
# Detect some Debian minimal setups where neither wget nor curl are installed # Detect minimal setups where neither wget nor curl are installed
if ! hash wget 2>/dev/null && ! hash curl 2>/dev/null; then if ! hash wget 2>/dev/null && ! hash curl 2>/dev/null; then
echo "Wget is required to use this installer." echo "Wget is required to use this installer."
read -n1 -r -p "Press any key to install Wget and continue..." read -n1 -r -p "Press any key to install Wget and continue..."
@ -112,7 +111,7 @@ if [[ ! -e /etc/openvpn/server/server.conf ]]; then
fi fi
clear clear
echo 'Welcome to this OpenVPN road warrior installer!' echo 'Welcome to this OpenVPN road warrior installer!'
# If system has a single IPv4, it is selected automatically. Else, ask the user # Choose IPv4 address (if only one exists, it is selected automatically)
if [[ $(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}') -eq 1 ]]; then if [[ $(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}') -eq 1 ]]; then
ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}') ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}')
else else
@ -128,25 +127,22 @@ if [[ ! -e /etc/openvpn/server/server.conf ]]; then
[[ -z "$ip_number" ]] && ip_number="1" [[ -z "$ip_number" ]] && ip_number="1"
ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | sed -n "$ip_number"p) ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | sed -n "$ip_number"p)
fi fi
# If $ip is a private IP address, the server must be behind NAT # If $ip is private, ask for public IP or hostname
if echo "$ip" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then if echo "$ip" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then
echo echo
echo "This server is behind NAT. What is the public IPv4 address or hostname?" echo "This server is behind NAT. What is the public IPv4 address or hostname?"
# Get public IP and sanitize with grep
get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "http://ip1.dynupdate.no-ip.com/" || curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/")") get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "http://ip1.dynupdate.no-ip.com/" || curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/")")
read -p "Public IPv4 address / hostname [$get_public_ip]: " public_ip read -p "Public IPv4 address / hostname [$get_public_ip]: " public_ip
# If the checkip service is unavailable and user didn't provide input, ask again
until [[ -n "$get_public_ip" || -n "$public_ip" ]]; do until [[ -n "$get_public_ip" || -n "$public_ip" ]]; do
echo "Invalid input." echo "Invalid input."
read -p "Public IPv4 address / hostname: " public_ip read -p "Public IPv4 address / hostname: " public_ip
done done
[[ -z "$public_ip" ]] && public_ip="$get_public_ip" [[ -z "$public_ip" ]] && public_ip="$get_public_ip"
fi fi
# If system has a single IPv6, it is selected automatically # Determine IPv6 address if available
if [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then if [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then
ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}') ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}')
fi fi
# If system has multiple IPv6, ask the user to select one
if [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then if [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then
number_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]') number_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]')
echo echo
@ -199,15 +195,12 @@ if [[ ! -e /etc/openvpn/server/server.conf ]]; then
echo "$dns: invalid selection." echo "$dns: invalid selection."
read -p "DNS server [1]: " dns read -p "DNS server [1]: " dns
done done
# If the user selected custom resolvers, we deal with that here
if [[ "$dns" = "7" ]]; then if [[ "$dns" = "7" ]]; then
echo echo
until [[ -n "$custom_dns" ]]; do until [[ -n "$custom_dns" ]]; do
echo "Enter DNS servers (one or more IPv4 addresses, separated by commas or spaces):" echo "Enter DNS servers (one or more IPv4 addresses, separated by commas or spaces):"
read -p "DNS servers: " dns_input read -p "DNS servers: " dns_input
# Convert comma delimited to space delimited
dns_input=$(echo "$dns_input" | tr ',' ' ') dns_input=$(echo "$dns_input" | tr ',' ' ')
# Validate and build custom DNS IP list
for dns_ip in $dns_input; do for dns_ip in $dns_input; do
if [[ "$dns_ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then if [[ "$dns_ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
if [[ -z "$custom_dns" ]]; then if [[ -z "$custom_dns" ]]; then
@ -225,30 +218,30 @@ if [[ ! -e /etc/openvpn/server/server.conf ]]; then
echo echo
echo "Enter a name for the first client:" echo "Enter a name for the first client:"
read -p "Name [client]: " unsanitized_client read -p "Name [client]: " unsanitized_client
# Allow a limited set of characters to avoid conflicts
client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client") client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
[[ -z "$client" ]] && client="client" [[ -z "$client" ]] && client="client"
echo echo
echo "OpenVPN installation is ready to begin." echo "OpenVPN installation is ready to begin."
# Install a firewall if firewalld or iptables are not already available
# Install firewall package if necessary
if ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>/dev/null; then if ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>/dev/null; then
if [[ "$os" == "centos" || "$os" == "fedora" ]]; then if [[ "$os" == "centos" || "$os" == "fedora" ]]; then
firewall="firewalld" firewall="firewalld"
# We don't want to silently enable firewalld, so we give a subtle warning
# If the user continues, firewalld will be installed and enabled during setup
echo "firewalld, which is required to manage routing tables, will also be installed." echo "firewalld, which is required to manage routing tables, will also be installed."
elif [[ "$os" == "debian" || "$os" == "ubuntu" ]]; then elif [[ "$os" == "debian" || "$os" == "ubuntu" ]]; then
# iptables is way less invasive than firewalld so no warning is given
firewall="iptables" firewall="iptables"
fi fi
fi fi
read -n1 -r -p "Press any key to continue..." read -n1 -r -p "Press any key to continue..."
# If running inside a container, disable LimitNPROC to prevent conflicts # If running inside a container, disable LimitNPROC to prevent conflicts
if systemd-detect-virt -cq; then if systemd-detect-virt -cq; then
mkdir /etc/systemd/system/openvpn-server@server.service.d/ 2>/dev/null mkdir /etc/systemd/system/openvpn-server@server.service.d/ 2>/dev/null
echo "[Service] echo "[Service]
LimitNPROC=infinity" > /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf LimitNPROC=infinity" > /etc/systemd/system/openvpn-server@server.service.d/disable-limitnproc.conf
fi fi
# Install OpenVPN and prerequisites
if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then if [[ "$os" = "debian" || "$os" = "ubuntu" ]]; then
apt-get update apt-get update
apt-get install -y --no-install-recommends openvpn openssl ca-certificates $firewall apt-get install -y --no-install-recommends openvpn openssl ca-certificates $firewall
@ -256,34 +249,36 @@ LimitNPROC=infinity" > /etc/systemd/system/openvpn-server@server.service.d/disab
dnf install -y epel-release dnf install -y epel-release
dnf install -y openvpn openssl ca-certificates tar $firewall dnf install -y openvpn openssl ca-certificates tar $firewall
else else
# Else, OS must be Fedora
dnf install -y openvpn openssl ca-certificates tar $firewall dnf install -y openvpn openssl ca-certificates tar $firewall
fi fi
# If firewalld was just installed, enable it
if [[ "$firewall" == "firewalld" ]]; then if [[ "$firewall" == "firewalld" ]]; then
systemctl enable --now firewalld.service systemctl enable --now firewalld.service
fi fi
# Get easy-rsa
# Get EasyRSA: Create the directory and extract EasyRSA files
easy_rsa_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.2.2/EasyRSA-3.2.2.tgz' easy_rsa_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.2.2/EasyRSA-3.2.2.tgz'
mkdir -p /etc/openvpn/server/easy-rsa/ mkdir -p /etc/openvpn/server/easy-rsa/
{ wget -qO- "$easy_rsa_url" 2>/dev/null || curl -sL "$easy_rsa_url" ; } | tar xz -C /etc/openvpn/server/easy-rsa/ --strip-components 1 { wget -qO- "$easy_rsa_url" 2>/dev/null || curl -sL "$easy_rsa_url" ; } | tar xz -C /etc/openvpn/server/easy-rsa/ --strip-components=1
chown -R root:root /etc/openvpn/server/easy-rsa/ chown -R root:root /etc/openvpn/server/easy-rsa/
cd /etc/openvpn/server/easy-rsa/ cd /etc/openvpn/server/easy-rsa/
# Create the PKI, set up the CA and the server and client certificates
# Create the PKI, set up the CA, and generate server and client certificates
./easyrsa --batch init-pki ./easyrsa --batch init-pki
./easyrsa --batch build-ca nopass ./easyrsa --batch build-ca nopass
./easyrsa --batch --days=3650 build-server-full server nopass ./easyrsa --batch --days=3650 build-server-full server nopass
./easyrsa --batch --days=3650 build-client-full "$client" nopass ./easyrsa --batch --days=3650 build-client-full "$client" nopass
./easyrsa --batch --days=3650 gen-crl ./easyrsa --batch --days=3650 gen-crl
# Move the stuff we need
# Move necessary files to /etc/openvpn/server
cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server
# CRL is read with each client connection, while OpenVPN is dropped to nobody
chown nobody:"$group_name" /etc/openvpn/server/crl.pem chown nobody:"$group_name" /etc/openvpn/server/crl.pem
# Without +x in the directory, OpenVPN can't run a stat() on the CRL file
chmod o+x /etc/openvpn/server/ chmod o+x /etc/openvpn/server/
# Generate key for tls-crypt # Generate key for tls-crypt
openvpn --genkey secret /etc/openvpn/server/tc.key openvpn --genkey secret /etc/openvpn/server/tc.key
# Create the DH parameters file using the predefined ffdhe2048 group
# Create the DH parameters file using ffdhe2048
echo '-----BEGIN DH PARAMETERS----- echo '-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
@ -292,6 +287,7 @@ YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----' > /etc/openvpn/server/dh.pem -----END DH PARAMETERS-----' > /etc/openvpn/server/dh.pem
# Generate server.conf # Generate server.conf
echo "local $ip echo "local $ip
port $port port $port
@ -305,7 +301,6 @@ auth SHA512
tls-crypt tc.key tls-crypt tc.key
topology subnet topology subnet
server 10.8.0.0 255.255.255.0" > /etc/openvpn/server/server.conf server 10.8.0.0 255.255.255.0" > /etc/openvpn/server/server.conf
# IPv6
if [[ -z "$ip6" ]]; then if [[ -z "$ip6" ]]; then
echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server/server.conf echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server/server.conf
else else
@ -313,17 +308,14 @@ server 10.8.0.0 255.255.255.0" > /etc/openvpn/server/server.conf
echo 'push "redirect-gateway def1 ipv6 bypass-dhcp"' >> /etc/openvpn/server/server.conf echo 'push "redirect-gateway def1 ipv6 bypass-dhcp"' >> /etc/openvpn/server/server.conf
fi fi
echo 'ifconfig-pool-persist ipp.txt' >> /etc/openvpn/server/server.conf echo 'ifconfig-pool-persist ipp.txt' >> /etc/openvpn/server/server.conf
# DNS # DNS configuration
case "$dns" in case "$dns" in
1|"") 1|"")
# Locate the proper resolv.conf
# Needed for systems running systemd-resolved
if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then
resolv_conf="/etc/resolv.conf" resolv_conf="/etc/resolv.conf"
else else
resolv_conf="/run/systemd/resolve/resolv.conf" resolv_conf="/run/systemd/resolve/resolv.conf"
fi fi
# Obtain the resolvers from resolv.conf and use them for OpenVPN
grep -v '^#\|^;' "$resolv_conf" | grep '^nameserver' | grep -v '127.0.0.53' | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | while read line; do grep -v '^#\|^;' "$resolv_conf" | grep '^nameserver' | grep -v '127.0.0.53' | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | while read line; do
echo "push \"dhcp-option DNS $line\"" >> /etc/openvpn/server/server.conf echo "push \"dhcp-option DNS $line\"" >> /etc/openvpn/server/server.conf
done done
@ -365,26 +357,17 @@ crl-verify crl.pem" >> /etc/openvpn/server/server.conf
if [[ "$protocol" = "udp" ]]; then if [[ "$protocol" = "udp" ]]; then
echo "explicit-exit-notify" >> /etc/openvpn/server/server.conf echo "explicit-exit-notify" >> /etc/openvpn/server/server.conf
fi fi
# Enable net.ipv4.ip_forward for the system
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-openvpn-forward.conf echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-openvpn-forward.conf
# Enable without waiting for a reboot or service restart
echo 1 > /proc/sys/net/ipv4/ip_forward echo 1 > /proc/sys/net/ipv4/ip_forward
if [[ -n "$ip6" ]]; then if [[ -n "$ip6" ]]; then
# Enable net.ipv6.conf.all.forwarding for the system
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.d/99-openvpn-forward.conf echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.d/99-openvpn-forward.conf
# Enable without waiting for a reboot or service restart
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
fi fi
if systemctl is-active --quiet firewalld.service; then if systemctl is-active --quiet firewalld.service; then
# Using both permanent and not permanent rules to avoid a firewalld
# reload.
# We don't use --add-service=openvpn because that would only work with
# the default port and protocol.
firewall-cmd --add-port="$port"/"$protocol" firewall-cmd --add-port="$port"/"$protocol"
firewall-cmd --zone=trusted --add-source=10.8.0.0/24 firewall-cmd --zone=trusted --add-source=10.8.0.0/24
firewall-cmd --permanent --add-port="$port"/"$protocol" firewall-cmd --permanent --add-port="$port"/"$protocol"
firewall-cmd --permanent --zone=trusted --add-source=10.8.0.0/24 firewall-cmd --permanent --zone=trusted --add-source=10.8.0.0/24
# Set NAT for the VPN subnet
firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip" firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip" firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
if [[ -n "$ip6" ]]; then if [[ -n "$ip6" ]]; then
@ -394,52 +377,70 @@ crl-verify crl.pem" >> /etc/openvpn/server/server.conf
firewall-cmd --permanent --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6" firewall-cmd --permanent --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to "$ip6"
fi fi
else else
# Create a service to set up persistent iptables rules
iptables_path=$(command -v iptables) iptables_path=$(command -v iptables)
ip6tables_path=$(command -v ip6tables) ip6tables_path=$(command -v ip6tables)
# nf_tables is not available as standard in OVZ kernels. So use iptables-legacy
# if we are in OVZ, with a nf_tables backend and iptables-legacy is available.
if [[ $(systemd-detect-virt) == "openvz" ]] && readlink -f "$(command -v iptables)" | grep -q "nft" && hash iptables-legacy 2>/dev/null; then if [[ $(systemd-detect-virt) == "openvz" ]] && readlink -f "$(command -v iptables)" | grep -q "nft" && hash iptables-legacy 2>/dev/null; then
iptables_path=$(command -v iptables-legacy) iptables_path=$(command -v iptables-legacy)
ip6tables_path=$(command -v ip6tables-legacy) ip6tables_path=$(command -v ip6tables-legacy)
fi fi
echo "[Unit] read -p "Enter allowed subnets for VPN access (separated by spaces or commas): " allowed_input
allowed_subnets=$(echo "$allowed_input" | tr ',' ' ' | xargs)
echo "Allowed subnets: $allowed_subnets"
custom_rules=""
for subnet in $allowed_subnets; do
custom_rules="${custom_rules}\nExecStart=${iptables_path} -w 5 -A VPN_FILTER -d ${subnet} -j ACCEPT"
done
cat <<EOF > /etc/systemd/system/openvpn-iptables.service
[Unit]
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=$iptables_path -w 5 -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip RemainAfterExit=yes
ExecStart=$iptables_path -w 5 -I INPUT -p $protocol --dport $port -j ACCEPT ExecStart=${iptables_path} -w 5 -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to ${ip}
ExecStart=$iptables_path -w 5 -I FORWARD -s 10.8.0.0/24 -j ACCEPT ExecStart=${iptables_path} -w 5 -I INPUT -p ${protocol} --dport ${port} -j ACCEPT
ExecStart=$iptables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT ExecStart=${iptables_path} -w 5 -I FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStop=$iptables_path -w 5 -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $ip ExecStart=${iptables_path} -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$iptables_path -w 5 -D INPUT -p $protocol --dport $port -j ACCEPT ExecStart=-${iptables_path} -w 5 -N VPN_FILTER
ExecStop=$iptables_path -w 5 -D FORWARD -s 10.8.0.0/24 -j ACCEPT ExecStart=${iptables_path} -w 5 -A VPN_FILTER -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$iptables_path -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" > /etc/systemd/system/openvpn-iptables.service ExecStart=${iptables_path} -w 5 -A VPN_FILTER -p tcp -m multiport --dports 22,80,443,8000,9000,8001,8002 -j ACCEPT
ExecStart=${iptables_path} -w 5 -A VPN_FILTER -p udp --dport 1195 -j ACCEPT
$custom_rules
ExecStart=${iptables_path} -w 5 -I FORWARD -s 10.8.0.0/24 -j VPN_FILTER
ExecStart=${iptables_path} -w 5 -A VPN_FILTER -j DROP
ExecStop=${iptables_path} -w 5 -t nat -D POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to ${ip}
ExecStop=${iptables_path} -w 5 -D INPUT -p ${protocol} --dport ${port} -j ACCEPT
ExecStop=${iptables_path} -w 5 -D FORWARD -s 10.8.0.0/24 -j ACCEPT
ExecStop=${iptables_path} -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=${iptables_path} -w 5 -F VPN_FILTER
ExecStop=${iptables_path} -w 5 -X VPN_FILTER
EOF
if [[ -n "$ip6" ]]; then if [[ -n "$ip6" ]]; then
echo "ExecStart=$ip6tables_path -w 5 -t nat -A POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6 cat <<EOF >> /etc/systemd/system/openvpn-iptables.service
ExecStart=$ip6tables_path -w 5 -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT ExecStart=${ip6tables_path} -w 5 -t nat -A POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to ${ip6}
ExecStart=$ip6tables_path -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT ExecStart=${ip6tables_path} -w 5 -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
ExecStop=$ip6tables_path -w 5 -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to $ip6 ExecStart=${ip6tables_path} -w 5 -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$ip6tables_path -w 5 -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT ExecStop=${ip6tables_path} -w 5 -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to ${ip6}
ExecStop=$ip6tables_path -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" >> /etc/systemd/system/openvpn-iptables.service ExecStop=${ip6tables_path} -w 5 -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
ExecStop=${ip6tables_path} -w 5 -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
EOF
fi fi
echo "RemainAfterExit=yes cat <<EOF >> /etc/systemd/system/openvpn-iptables.service
[Install] [Install]
WantedBy=multi-user.target" >> /etc/systemd/system/openvpn-iptables.service WantedBy=multi-user.target
EOF
systemctl enable --now openvpn-iptables.service systemctl enable --now openvpn-iptables.service
fi fi
# If SELinux is enabled and a custom port was selected, we need this
if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then if sestatus 2>/dev/null | grep "Current mode" | grep -q "enforcing" && [[ "$port" != 1194 ]]; then
# Install semanage if not already present
if ! hash semanage 2>/dev/null; then if ! hash semanage 2>/dev/null; then
dnf install -y policycoreutils-python-utils dnf install -y policycoreutils-python-utils
fi fi
semanage port -a -t openvpn_port_t -p "$protocol" "$port" semanage port -a -t openvpn_port_t -p "$protocol" "$port"
fi fi
# If the server is behind NAT, use the correct IP address
[[ -n "$public_ip" ]] && ip="$public_ip" [[ -n "$public_ip" ]] && ip="$public_ip"
# client-common.txt is created so we have a template to add further users later
echo "client echo "client
dev tun dev tun
proto $protocol proto $protocol
@ -452,9 +453,7 @@ remote-cert-tls server
auth SHA512 auth SHA512
ignore-unknown-option block-outside-dns ignore-unknown-option block-outside-dns
verb 3" > /etc/openvpn/server/client-common.txt verb 3" > /etc/openvpn/server/client-common.txt
# Enable and start the OpenVPN service
systemctl enable --now openvpn-server@server.service systemctl enable --now openvpn-server@server.service
# Generates the custom client.ovpn
new_client new_client
echo echo
echo "Finished!" echo "Finished!"
@ -488,15 +487,12 @@ else
done done
cd /etc/openvpn/server/easy-rsa/ cd /etc/openvpn/server/easy-rsa/
./easyrsa --batch --days=3650 build-client-full "$client" nopass ./easyrsa --batch --days=3650 build-client-full "$client" nopass
# Generates the custom client.ovpn
new_client new_client
echo echo
echo "$client added. Configuration available in:" "$script_dir"/"$client.ovpn" echo "$client added. Configuration available in:" "$script_dir"/"$client.ovpn"
exit exit
;; ;;
2) 2)
# This option could be documented a bit better and maybe even be simplified
# ...but what can I say, I want some sleep too
number_of_clients=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V") number_of_clients=$(tail -n +2 /etc/openvpn/server/easy-rsa/pki/index.txt | grep -c "^V")
if [[ "$number_of_clients" = 0 ]]; then if [[ "$number_of_clients" = 0 ]]; then
echo echo
@ -526,7 +522,6 @@ else
rm -f /etc/openvpn/server/easy-rsa/pki/reqs/"$client".req rm -f /etc/openvpn/server/easy-rsa/pki/reqs/"$client".req
rm -f /etc/openvpn/server/easy-rsa/pki/private/"$client".key rm -f /etc/openvpn/server/easy-rsa/pki/private/"$client".key
cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem cp /etc/openvpn/server/easy-rsa/pki/crl.pem /etc/openvpn/server/crl.pem
# CRL is read with each client connection, when OpenVPN is dropped to nobody
chown nobody:"$group_name" /etc/openvpn/server/crl.pem chown nobody:"$group_name" /etc/openvpn/server/crl.pem
echo echo
echo "$client revoked!" echo "$client revoked!"
@ -548,7 +543,6 @@ else
protocol=$(grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2) protocol=$(grep '^proto ' /etc/openvpn/server/server.conf | cut -d " " -f 2)
if systemctl is-active --quiet firewalld.service; then if systemctl is-active --quiet firewalld.service; then
ip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\-s 10.8.0.0/24 '"'"'!'"'"' -d 10.8.0.0/24' | grep -oE '[^ ]+$') ip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\-s 10.8.0.0/24 '"'"'!'"'"' -d 10.8.0.0/24' | grep -oE '[^ ]+$')
# Using both permanent and not permanent rules to avoid a firewalld reload.
firewall-cmd --remove-port="$port"/"$protocol" firewall-cmd --remove-port="$port"/"$protocol"
firewall-cmd --zone=trusted --remove-source=10.8.0.0/24 firewall-cmd --zone=trusted --remove-source=10.8.0.0/24
firewall-cmd --permanent --remove-port="$port"/"$protocol" firewall-cmd --permanent --remove-port="$port"/"$protocol"
@ -576,7 +570,6 @@ else
rm -rf /etc/openvpn/server rm -rf /etc/openvpn/server
apt-get remove --purge -y openvpn apt-get remove --purge -y openvpn
else else
# Else, OS must be CentOS or Fedora
dnf remove -y openvpn dnf remove -y openvpn
rm -rf /etc/openvpn/server rm -rf /etc/openvpn/server
fi fi