mirror of
https://github.com/angristan/openvpn-install.git
synced 2026-08-03 05:08:13 +02:00
Add configurable VPN access policies
This commit is contained in:
@@ -129,6 +129,17 @@ jobs:
|
|||||||
name: tls-crypt-v2
|
name: tls-crypt-v2
|
||||||
sig: crypt-v2
|
sig: crypt-v2
|
||||||
key_file: tls-crypt-v2.key
|
key_file: tls-crypt-v2.key
|
||||||
|
# Test split tunnel with peer and home-LAN access enabled
|
||||||
|
- os:
|
||||||
|
name: ubuntu-24.04-access-policy
|
||||||
|
image: ubuntu:24.04
|
||||||
|
route_internet: n
|
||||||
|
client_to_client: y
|
||||||
|
local_networks: 10.55.0.0/24
|
||||||
|
tls:
|
||||||
|
name: tls-crypt-v2
|
||||||
|
sig: crypt-v2
|
||||||
|
key_file: tls-crypt-v2.key
|
||||||
|
|
||||||
name: ${{ matrix.os.name }}
|
name: ${{ matrix.os.name }}
|
||||||
steps:
|
steps:
|
||||||
@@ -178,6 +189,9 @@ jobs:
|
|||||||
-e TLS_KEY_FILE=${{ matrix.tls.key_file }} \
|
-e TLS_KEY_FILE=${{ matrix.tls.key_file }} \
|
||||||
-e CLIENT_IPV6=${{ matrix.os.client_ipv6 && 'y' || 'n' }} \
|
-e CLIENT_IPV6=${{ matrix.os.client_ipv6 && 'y' || 'n' }} \
|
||||||
-e AUTH_MODE=${{ matrix.os.auth_mode || 'pki' }} \
|
-e AUTH_MODE=${{ matrix.os.auth_mode || 'pki' }} \
|
||||||
|
-e ROUTE_INTERNET=${{ matrix.os.route_internet || 'y' }} \
|
||||||
|
-e CLIENT_TO_CLIENT=${{ matrix.os.client_to_client || 'n' }} \
|
||||||
|
-e LOCAL_NETWORKS=${{ matrix.os.local_networks || '' }} \
|
||||||
openvpn-server
|
openvpn-server
|
||||||
|
|
||||||
- name: Wait for server installation and startup
|
- name: Wait for server installation and startup
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ down /usr/share/openvpn/contrib/pull-resolv-conf/client.down
|
|||||||
|
|
||||||
**Q:** What sysctl and firewall changes are made by the script?
|
**Q:** What sysctl and firewall changes are made by the script?
|
||||||
|
|
||||||
**A:** If firewalld is active, the script uses `firewall-cmd --permanent` to configure port, masquerade, and rich rules. Otherwise, iptables rules are saved at `/etc/iptables/add-openvpn-rules.sh` and `/etc/iptables/rm-openvpn-rules.sh`, managed by `/etc/systemd/system/iptables-openvpn.service`.
|
**A:** If firewalld is active, the script uses `firewall-cmd --permanent` to configure scoped rich rules. If nftables is active, it creates `/etc/nftables/openvpn.nft`. Otherwise, iptables rules are saved at `/etc/iptables/add-openvpn-rules.sh` and `/etc/iptables/rm-openvpn-rules.sh`, managed by `/etc/systemd/system/iptables-openvpn.service`.
|
||||||
|
|
||||||
Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
|
Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
|
||||||
|
|
||||||
@@ -94,7 +94,13 @@ Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
|
|||||||
|
|
||||||
**Q:** How can I access other clients connected to the same OpenVPN server?
|
**Q:** How can I access other clients connected to the same OpenVPN server?
|
||||||
|
|
||||||
**A:** Add `client-to-client` to your `server.conf`
|
**A:** Enable client-to-client access during installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./openvpn-install.sh install --client-to-client
|
||||||
|
```
|
||||||
|
|
||||||
|
It is disabled by default. The installer configures both OpenVPN and the firewall so the policy also applies when Data Channel Offload (DCO) is active.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -110,36 +116,17 @@ Sysctl options are at `/etc/sysctl.d/99-openvpn.conf`
|
|||||||
|
|
||||||
**Q:** How can I access computers on the OpenVPN server's LAN?
|
**Q:** How can I access computers on the OpenVPN server's LAN?
|
||||||
|
|
||||||
**A:** Two steps are required:
|
**A:** Specify the LAN during installation:
|
||||||
|
|
||||||
1. **Push a route to clients** - Add the LAN subnet to `/etc/openvpn/server/server.conf`:
|
|
||||||
|
|
||||||
```
|
|
||||||
push "route 192.168.1.0 255.255.255.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `192.168.1.0/24` with your actual LAN subnet.
|
|
||||||
|
|
||||||
2. **Enable routing back to VPN clients** - Choose one of these options:
|
|
||||||
- **Option A: Add a static route on your router** (recommended when you can configure your router)
|
|
||||||
|
|
||||||
On your LAN router, add a route for the VPN subnet (default `10.8.0.0/24`) pointing to the OpenVPN server's LAN IP. This allows LAN devices to reply to VPN clients without NAT.
|
|
||||||
|
|
||||||
- **Option B: Masquerade VPN traffic to LAN**
|
|
||||||
|
|
||||||
If you can't modify your router, add a masquerade rule so VPN traffic appears to come from the server:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# iptables
|
./openvpn-install.sh install --local-network 192.168.1.0/24
|
||||||
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -d 192.168.1.0/24 -j MASQUERADE
|
|
||||||
|
|
||||||
# or nftables
|
|
||||||
nft add rule ip nat postrouting ip saddr 10.8.0.0/24 ip daddr 192.168.1.0/24 masquerade
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Make this persistent by adding it to your firewall scripts.
|
Repeat `--local-network` to expose more than one server-side network. This feature is mainly for OpenVPN servers installed at home. It is disabled by default so cloud VPC, container, and management networks are not exposed automatically.
|
||||||
|
|
||||||
Restart OpenVPN after making changes: `systemctl restart openvpn-server@server`
|
The installer pushes the route, permits only the selected destination, and adds destination-scoped NAT. LAN computers therefore see the connection as coming from the OpenVPN server and do not need a return route to the VPN subnet.
|
||||||
|
|
||||||
|
Do not use a LAN CIDR that overlaps the VPN subnet. An overlap with the client's current LAN can also prevent the route from working.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -180,56 +167,32 @@ To add password-protected clients:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Q:** For my clients - I want to set my internal network to pass through the VPN and the rest to go through my internet?
|
**Q:** For my clients, how can I route only an internal network through the VPN?
|
||||||
|
|
||||||
**A:** You would need to edit the `.ovpn` file. You can edit the template out of which those files are created by editing `/etc/openvpn/server/client-template.txt` file and adding
|
**A:** Disable internet routing and specify the server-side network during installation:
|
||||||
|
|
||||||
```sh
|
```bash
|
||||||
route-nopull
|
./openvpn-install.sh install \
|
||||||
route 10.0.0.0 255.0.0.0
|
--no-route-internet \
|
||||||
|
--local-network 10.0.0.0/8
|
||||||
```
|
```
|
||||||
|
|
||||||
So for example - here it would route all traffic of `10.0.0.0/8` to the VPN. And the rest through the internet.
|
The client's normal internet route and DNS remain unchanged.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Q:** How do I configure split-tunnel mode on the server (route only specific networks through VPN for all clients)?
|
**Q:** How do I configure split-tunnel mode on the server?
|
||||||
|
|
||||||
**A:** By default, the script configures full-tunnel mode where all client traffic goes through the VPN. To configure split-tunnel (only specific networks routed through VPN), edit `/etc/openvpn/server/server.conf`:
|
**A:** Use `--no-route-internet`. Add each server-side network that should use the tunnel:
|
||||||
|
|
||||||
1. Remove or comment out the redirect-gateway line:
|
```bash
|
||||||
|
./openvpn-install.sh install \
|
||||||
```
|
--no-route-internet \
|
||||||
#push "redirect-gateway def1 bypass-dhcp"
|
--local-network 10.0.0.0/8 \
|
||||||
|
--local-network 192.168.1.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add routes for the networks you want to tunnel:
|
The installer does not push internet routes, leak-blocking directives, or VPN DNS in this mode.
|
||||||
|
|
||||||
```
|
|
||||||
push "route 10.0.0.0 255.0.0.0"
|
|
||||||
push "route 192.168.1.0 255.255.255.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Optionally remove DNS push directives if you don't want VPN DNS:
|
|
||||||
|
|
||||||
```
|
|
||||||
#push "dhcp-option DNS 1.1.1.1"
|
|
||||||
```
|
|
||||||
|
|
||||||
4. For IPv6, remove or comment out:
|
|
||||||
|
|
||||||
```
|
|
||||||
#push "route-ipv6 2000::/3"
|
|
||||||
#push "redirect-gateway ipv6"
|
|
||||||
```
|
|
||||||
|
|
||||||
Or add specific IPv6 routes:
|
|
||||||
|
|
||||||
```
|
|
||||||
push "route-ipv6 2001:db8::/32"
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Restart OpenVPN: `systemctl restart openvpn-server@server`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ This script is meant to be run on your own server, whether it's a VPS or a dedic
|
|||||||
|
|
||||||
Once set up, you will be able to generate client configuration files for every device you want to connect.
|
Once set up, you will be able to generate client configuration files for every device you want to connect.
|
||||||
|
|
||||||
Each client will be able to route its internet traffic through the server, fully encrypted.
|
By default, each client routes its internet traffic through the server, fully encrypted. You can instead keep internet traffic outside the VPN and allow only selected server-side networks.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
@@ -44,6 +44,7 @@ That said, OpenVPN still makes sense when you need:
|
|||||||
- Immediate client disconnect on certificate revocation (via management interface)
|
- Immediate client disconnect on certificate revocation (via management interface)
|
||||||
- Uses [official OpenVPN repositories](https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos) when possible for the latest stable releases
|
- Uses [official OpenVPN repositories](https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos) when possible for the latest stable releases
|
||||||
- Firewall rules and forwarding managed seamlessly (native firewalld and nftables support, iptables fallback)
|
- Firewall rules and forwarding managed seamlessly (native firewalld and nftables support, iptables fallback)
|
||||||
|
- Independent access policies for internet routing, communication between VPN clients, and selected server-side networks
|
||||||
- Configurable VPN subnets (IPv4: default `10.8.0.0/24`, IPv6: default `fd42:42:42:42::/112`)
|
- Configurable VPN subnets (IPv4: default `10.8.0.0/24`, IPv6: default `fd42:42:42:42::/112`)
|
||||||
- Configurable tunnel MTU (default: `1500`)
|
- Configurable tunnel MTU (default: `1500`)
|
||||||
- If needed, the script can cleanly remove OpenVPN, including configuration and firewall rules
|
- If needed, the script can cleanly remove OpenVPN, including configuration and firewall rules
|
||||||
@@ -263,6 +264,12 @@ The `install` command supports many options for customization:
|
|||||||
# Custom VPN subnet
|
# Custom VPN subnet
|
||||||
./openvpn-install.sh install --subnet-ipv4 10.9.0.0
|
./openvpn-install.sh install --subnet-ipv4 10.9.0.0
|
||||||
|
|
||||||
|
# Home VPN: access the home LAN without routing internet through the VPN
|
||||||
|
./openvpn-install.sh install --no-route-internet --local-network 192.168.1.0/24
|
||||||
|
|
||||||
|
# Allow VPN clients to access each other
|
||||||
|
./openvpn-install.sh install --client-to-client
|
||||||
|
|
||||||
# Enable dual-stack (IPv4 + IPv6) for clients
|
# Enable dual-stack (IPv4 + IPv6) for clients
|
||||||
./openvpn-install.sh install --client-ipv4 --client-ipv6
|
./openvpn-install.sh install --client-ipv4 --client-ipv6
|
||||||
|
|
||||||
@@ -299,13 +306,22 @@ The `install` command supports many options for customization:
|
|||||||
- `--no-client-ipv6` - Disable IPv6 for VPN clients
|
- `--no-client-ipv6` - Disable IPv6 for VPN clients
|
||||||
- `--subnet-ipv4 <x.x.x.0>` - IPv4 VPN subnet (default: `10.8.0.0`)
|
- `--subnet-ipv4 <x.x.x.0>` - IPv4 VPN subnet (default: `10.8.0.0`)
|
||||||
- `--subnet-ipv6 <prefix>` - IPv6 VPN subnet (default: `fd42:42:42:42::`)
|
- `--subnet-ipv6 <prefix>` - IPv6 VPN subnet (default: `fd42:42:42:42::`)
|
||||||
|
- `--route-internet` / `--no-route-internet` - Enable or disable routing client internet traffic through the VPN (default: enabled)
|
||||||
|
- `--client-to-client` / `--no-client-to-client` - Allow or isolate communication between VPN clients (default: isolated)
|
||||||
|
- `--local-network <CIDR>` - Allow access to a server-side network and add destination-scoped NAT. Repeat for multiple networks (default: none)
|
||||||
- `--port <num>` - OpenVPN port (default: `1194`)
|
- `--port <num>` - OpenVPN port (default: `1194`)
|
||||||
- `--port-random` - Use random port (49152-65535)
|
- `--port-random` - Use random port (49152-65535)
|
||||||
- `--protocol <udp|tcp>` - Protocol (default: `udp`)
|
- `--protocol <udp|tcp>` - Protocol (default: `udp`)
|
||||||
- `--mtu <size>` - Tunnel MTU (default: `1500`)
|
- `--mtu <size>` - Tunnel MTU (default: `1500`)
|
||||||
|
|
||||||
|
Server-side network access is mainly intended for VPN servers installed at home. Specify each LAN explicitly. The installer does not automatically expose connected cloud, container, or management networks. LAN devices see connections as coming from the VPN server because destination-scoped NAT is enabled.
|
||||||
|
|
||||||
|
Local networks must use network-aligned IPv4 or IPv6 CIDRs and must not overlap the VPN pools. Client-side and server-side LANs that overlap can still cause routing conflicts.
|
||||||
|
|
||||||
**DNS Options:**
|
**DNS Options:**
|
||||||
|
|
||||||
|
DNS settings are pushed only when internet routing through the VPN is enabled.
|
||||||
|
|
||||||
- `--dns <provider>` - DNS provider (default: `cloudflare`). Options: `system`, `unbound`, `cloudflare`, `quad9`, `quad9-uncensored`, `fdn`, `dnswatch`, `opendns`, `google`, `yandex`, `adguard`, `nextdns`, `custom`
|
- `--dns <provider>` - DNS provider (default: `cloudflare`). Options: `system`, `unbound`, `cloudflare`, `quad9`, `quad9-uncensored`, `fdn`, `dnswatch`, `opendns`, `google`, `yandex`, `adguard`, `nextdns`, `custom`
|
||||||
- `--dns-primary <ip>` - Custom primary DNS (requires `--dns custom`)
|
- `--dns-primary <ip>` - Custom primary DNS (requires `--dns custom`)
|
||||||
- `--dns-secondary <ip>` - Custom secondary DNS (requires `--dns custom`)
|
- `--dns-secondary <ip>` - Custom secondary DNS (requires `--dns custom`)
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ services:
|
|||||||
cgroupns: host
|
cgroupns: host
|
||||||
devices:
|
devices:
|
||||||
- /dev/net/tun:/dev/net/tun
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
environment:
|
||||||
|
ROUTE_INTERNET: ${ROUTE_INTERNET:-y}
|
||||||
|
CLIENT_TO_CLIENT: ${CLIENT_TO_CLIENT:-n}
|
||||||
|
LOCAL_NETWORKS: ${LOCAL_NETWORKS:-}
|
||||||
sysctls:
|
sysctls:
|
||||||
- net.ipv4.ip_forward=1
|
- net.ipv4.ip_forward=1
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+619
-96
@@ -213,6 +213,11 @@ show_install_help() {
|
|||||||
--no-client-ipv6 Disable IPv6 for VPN clients (default)
|
--no-client-ipv6 Disable IPv6 for VPN clients (default)
|
||||||
--subnet-ipv4 <x.x.x.0> IPv4 VPN subnet (default: 10.8.0.0)
|
--subnet-ipv4 <x.x.x.0> IPv4 VPN subnet (default: 10.8.0.0)
|
||||||
--subnet-ipv6 <prefix> IPv6 VPN subnet (default: fd42:42:42:42::)
|
--subnet-ipv6 <prefix> IPv6 VPN subnet (default: fd42:42:42:42::)
|
||||||
|
--route-internet Route client internet traffic through VPN (default)
|
||||||
|
--no-route-internet Keep client internet traffic outside VPN
|
||||||
|
--client-to-client Allow VPN clients to access each other
|
||||||
|
--no-client-to-client Isolate VPN clients from each other (default)
|
||||||
|
--local-network <CIDR> Allow access to a server-side network (repeatable)
|
||||||
--port <num> OpenVPN port (default: 1194)
|
--port <num> OpenVPN port (default: 1194)
|
||||||
--port-random Use random port (49152-65535)
|
--port-random Use random port (49152-65535)
|
||||||
--protocol <proto> Protocol: udp or tcp (default: udp)
|
--protocol <proto> Protocol: udp or tcp (default: udp)
|
||||||
@@ -486,6 +491,11 @@ readonly AUTH_MODES=("pki" "fingerprint")
|
|||||||
# HMAC algorithms
|
# HMAC algorithms
|
||||||
readonly HMAC_ALGS=("SHA256" "SHA384" "SHA512")
|
readonly HMAC_ALGS=("SHA256" "SHA384" "SHA512")
|
||||||
|
|
||||||
|
# Networks that internet access must not implicitly expose. Explicit local
|
||||||
|
# networks are allowed before these deny rules are evaluated.
|
||||||
|
readonly PROTECTED_IPV4_NETWORKS=("10.0.0.0/8" "100.64.0.0/10" "127.0.0.0/8" "169.254.0.0/16" "172.16.0.0/12" "192.168.0.0/16")
|
||||||
|
readonly PROTECTED_IPV6_NETWORKS=("::1/128" "fc00::/7" "fe80::/10")
|
||||||
|
|
||||||
# TLS 1.3 cipher suite options
|
# TLS 1.3 cipher suite options
|
||||||
readonly TLS13_OPTIONS=("all" "aes-256-only" "aes-128-only" "chacha20-only")
|
readonly TLS13_OPTIONS=("all" "aes-256-only" "aes-128-only" "chacha20-only")
|
||||||
|
|
||||||
@@ -503,6 +513,9 @@ set_installation_defaults() {
|
|||||||
CLIENT_IPV6="${CLIENT_IPV6:-n}"
|
CLIENT_IPV6="${CLIENT_IPV6:-n}"
|
||||||
VPN_SUBNET_IPV4="${VPN_SUBNET_IPV4:-10.8.0.0}"
|
VPN_SUBNET_IPV4="${VPN_SUBNET_IPV4:-10.8.0.0}"
|
||||||
VPN_SUBNET_IPV6="${VPN_SUBNET_IPV6:-fd42:42:42:42::}"
|
VPN_SUBNET_IPV6="${VPN_SUBNET_IPV6:-fd42:42:42:42::}"
|
||||||
|
ROUTE_INTERNET="${ROUTE_INTERNET:-y}"
|
||||||
|
CLIENT_TO_CLIENT="${CLIENT_TO_CLIENT:-n}"
|
||||||
|
LOCAL_NETWORKS="${LOCAL_NETWORKS:-}"
|
||||||
PORT="${PORT:-1194}"
|
PORT="${PORT:-1194}"
|
||||||
PROTOCOL="${PROTOCOL:-udp}"
|
PROTOCOL="${PROTOCOL:-udp}"
|
||||||
|
|
||||||
@@ -597,6 +610,196 @@ validate_subnet_ipv6() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_valid_ipv4_cidr() {
|
||||||
|
local cidr="$1" address prefix extra
|
||||||
|
local -a octets
|
||||||
|
|
||||||
|
[[ $cidr == */* ]] || return 1
|
||||||
|
address="${cidr%/*}"
|
||||||
|
prefix="${cidr##*/}"
|
||||||
|
[[ $prefix =~ ^(0|[1-9][0-9]?)$ ]] || return 1
|
||||||
|
prefix=$((10#$prefix))
|
||||||
|
((prefix >= 1 && prefix <= 32)) || return 1
|
||||||
|
|
||||||
|
IFS='.' read -r -a octets <<<"$address"
|
||||||
|
[[ ${#octets[@]} -eq 4 ]] || return 1
|
||||||
|
for extra in "${octets[@]}"; do
|
||||||
|
[[ $extra =~ ^(0|[1-9][0-9]{0,2})$ ]] || return 1
|
||||||
|
extra=$((10#$extra))
|
||||||
|
((extra >= 0 && extra <= 255)) || return 1
|
||||||
|
done
|
||||||
|
|
||||||
|
local ip mask
|
||||||
|
ip=$(((10#${octets[0]} << 24) | (10#${octets[1]} << 16) | (10#${octets[2]} << 8) | 10#${octets[3]}))
|
||||||
|
if ((prefix == 0)); then
|
||||||
|
mask=0
|
||||||
|
else
|
||||||
|
mask=$(((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF))
|
||||||
|
fi
|
||||||
|
(( (ip & mask) == ip ))
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_ipv6_address() {
|
||||||
|
local address="$1"
|
||||||
|
local -n result_ref="$2"
|
||||||
|
local left right remainder part
|
||||||
|
local -a left_parts=() right_parts=()
|
||||||
|
|
||||||
|
[[ $address == *:* ]] || return 1
|
||||||
|
[[ $address =~ ^[0-9a-fA-F:]+$ ]] || return 1
|
||||||
|
|
||||||
|
if [[ $address == *::* ]]; then
|
||||||
|
remainder="${address#*::}"
|
||||||
|
[[ $remainder != *::* ]] || return 1
|
||||||
|
left="${address%%::*}"
|
||||||
|
right="${address#*::}"
|
||||||
|
[[ -z $left ]] || IFS=':' read -r -a left_parts <<<"$left"
|
||||||
|
[[ -z $right ]] || IFS=':' read -r -a right_parts <<<"$right"
|
||||||
|
((${#left_parts[@]} + ${#right_parts[@]} < 8)) || return 1
|
||||||
|
else
|
||||||
|
IFS=':' read -r -a left_parts <<<"$address"
|
||||||
|
[[ ${#left_parts[@]} -eq 8 ]] || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for part in "${left_parts[@]}" "${right_parts[@]}"; do
|
||||||
|
[[ $part =~ ^[0-9a-fA-F]{1,4}$ ]] || return 1
|
||||||
|
done
|
||||||
|
|
||||||
|
result_ref=()
|
||||||
|
for part in "${left_parts[@]}"; do
|
||||||
|
result_ref+=("$((16#$part))")
|
||||||
|
done
|
||||||
|
while ((${#result_ref[@]} + ${#right_parts[@]} < 8)); do
|
||||||
|
result_ref+=(0)
|
||||||
|
done
|
||||||
|
for part in "${right_parts[@]}"; do
|
||||||
|
result_ref+=("$((16#$part))")
|
||||||
|
done
|
||||||
|
[[ ${#result_ref[@]} -eq 8 ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid_ipv6_cidr() {
|
||||||
|
local cidr="$1" address prefix_text prefix index remaining host_mask
|
||||||
|
local -a hextets
|
||||||
|
|
||||||
|
[[ $cidr == */* ]] || return 1
|
||||||
|
address="${cidr%/*}"
|
||||||
|
prefix_text="${cidr##*/}"
|
||||||
|
[[ $prefix_text =~ ^(0|[1-9][0-9]{0,2})$ ]] || return 1
|
||||||
|
prefix=$((10#$prefix_text))
|
||||||
|
((prefix >= 1 && prefix <= 128)) || return 1
|
||||||
|
expand_ipv6_address "$address" hextets || return 1
|
||||||
|
|
||||||
|
remaining=$prefix
|
||||||
|
for index in "${!hextets[@]}"; do
|
||||||
|
if ((remaining >= 16)); then
|
||||||
|
remaining=$((remaining - 16))
|
||||||
|
elif ((remaining <= 0)); then
|
||||||
|
((hextets[index] == 0)) || return 1
|
||||||
|
else
|
||||||
|
host_mask=$(((1 << (16 - remaining)) - 1))
|
||||||
|
(( (hextets[index] & host_mask) == 0 )) || return 1
|
||||||
|
remaining=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid_local_network() {
|
||||||
|
is_valid_ipv4_cidr "$1" || is_valid_ipv6_cidr "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_local_network() {
|
||||||
|
local network="${1//[[:space:]]/}"
|
||||||
|
is_valid_local_network "$network" || log_fatal "Invalid local network: $1. Use a network CIDR such as 192.168.1.0/24 or fd00:1::/64."
|
||||||
|
|
||||||
|
if [[ -z $LOCAL_NETWORKS ]]; then
|
||||||
|
LOCAL_NETWORKS="$network"
|
||||||
|
elif [[ ",$LOCAL_NETWORKS," != *",$network,"* ]]; then
|
||||||
|
LOCAL_NETWORKS+=",$network"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_local_networks() {
|
||||||
|
local configured="${LOCAL_NETWORKS//[[:space:]]/}" network
|
||||||
|
LOCAL_NETWORKS=""
|
||||||
|
[[ -z $configured ]] && return
|
||||||
|
|
||||||
|
while IFS= read -r network; do
|
||||||
|
add_local_network "$network"
|
||||||
|
done < <(tr ',' '\n' <<<"$configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
local_networks_for_family() {
|
||||||
|
local family="$1" network
|
||||||
|
[[ -z $LOCAL_NETWORKS ]] && return
|
||||||
|
|
||||||
|
while IFS= read -r network; do
|
||||||
|
if [[ $family == "4" && $network == *.* ]] || [[ $family == "6" && $network == *:* ]]; then
|
||||||
|
echo "$network"
|
||||||
|
fi
|
||||||
|
done < <(tr ',' '\n' <<<"$LOCAL_NETWORKS")
|
||||||
|
}
|
||||||
|
|
||||||
|
has_local_network_family() {
|
||||||
|
[[ -n $(local_networks_for_family "$1") ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4_prefix_to_netmask() {
|
||||||
|
local prefix="$1" mask
|
||||||
|
if ((prefix == 0)); then
|
||||||
|
mask=0
|
||||||
|
else
|
||||||
|
mask=$(((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF))
|
||||||
|
fi
|
||||||
|
printf '%d.%d.%d.%d\n' \
|
||||||
|
$(((mask >> 24) & 255)) \
|
||||||
|
$(((mask >> 16) & 255)) \
|
||||||
|
$(((mask >> 8) & 255)) \
|
||||||
|
$((mask & 255))
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4_cidrs_overlap() {
|
||||||
|
local first="$1" second="$2" first_address second_address first_prefix second_prefix prefix mask
|
||||||
|
local -a first_octets second_octets
|
||||||
|
first_address="${first%/*}"
|
||||||
|
second_address="${second%/*}"
|
||||||
|
first_prefix=$((10#${first##*/}))
|
||||||
|
second_prefix=$((10#${second##*/}))
|
||||||
|
prefix=$first_prefix
|
||||||
|
((second_prefix < prefix)) && prefix=$second_prefix
|
||||||
|
IFS='.' read -r -a first_octets <<<"$first_address"
|
||||||
|
IFS='.' read -r -a second_octets <<<"$second_address"
|
||||||
|
mask=$(((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF))
|
||||||
|
local first_ip=$(((10#${first_octets[0]} << 24) | (10#${first_octets[1]} << 16) | (10#${first_octets[2]} << 8) | 10#${first_octets[3]}))
|
||||||
|
local second_ip=$(((10#${second_octets[0]} << 24) | (10#${second_octets[1]} << 16) | (10#${second_octets[2]} << 8) | 10#${second_octets[3]}))
|
||||||
|
(( (first_ip & mask) == (second_ip & mask) ))
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6_cidrs_overlap() {
|
||||||
|
local first="$1" second="$2" first_prefix second_prefix prefix index remaining mask
|
||||||
|
local -a first_hextets second_hextets
|
||||||
|
first_prefix=$((10#${first##*/}))
|
||||||
|
second_prefix=$((10#${second##*/}))
|
||||||
|
prefix=$first_prefix
|
||||||
|
((second_prefix < prefix)) && prefix=$second_prefix
|
||||||
|
expand_ipv6_address "${first%/*}" first_hextets || return 1
|
||||||
|
expand_ipv6_address "${second%/*}" second_hextets || return 1
|
||||||
|
|
||||||
|
remaining=$prefix
|
||||||
|
for index in "${!first_hextets[@]}"; do
|
||||||
|
((remaining <= 0)) && return 0
|
||||||
|
if ((remaining >= 16)); then
|
||||||
|
((first_hextets[index] == second_hextets[index])) || return 1
|
||||||
|
remaining=$((remaining - 16))
|
||||||
|
else
|
||||||
|
mask=$(((0xFFFF << (16 - remaining)) & 0xFFFF))
|
||||||
|
(( (first_hextets[index] & mask) == (second_hextets[index] & mask) ))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
validate_positive_int() {
|
validate_positive_int() {
|
||||||
local value="$1"
|
local value="$1"
|
||||||
local name="$2"
|
local name="$2"
|
||||||
@@ -643,9 +846,12 @@ validate_configuration() {
|
|||||||
*) log_fatal "Invalid protocol: $PROTOCOL. Must be 'udp' or 'tcp'." ;;
|
*) log_fatal "Invalid protocol: $PROTOCOL. Must be 'udp' or 'tcp'." ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Validate DNS
|
# Validate DNS. Split-tunnel installs do not push a DNS server.
|
||||||
case "$DNS" in
|
case "$DNS" in
|
||||||
system | unbound | cloudflare | quad9 | quad9-uncensored | fdn | dnswatch | opendns | google | yandex | adguard | nextdns | custom) ;;
|
system | unbound | cloudflare | quad9 | quad9-uncensored | fdn | dnswatch | opendns | google | yandex | adguard | nextdns | custom) ;;
|
||||||
|
"")
|
||||||
|
[[ $ROUTE_INTERNET == "n" ]] || log_fatal "A DNS provider is required when internet routing is enabled."
|
||||||
|
;;
|
||||||
*) log_fatal "Invalid DNS provider: $DNS. Valid providers: system, unbound, cloudflare, quad9, quad9-uncensored, fdn, dnswatch, opendns, google, yandex, adguard, nextdns, custom" ;;
|
*) log_fatal "Invalid DNS provider: $DNS. Valid providers: system, unbound, cloudflare, quad9, quad9-uncensored, fdn, dnswatch, opendns, google, yandex, adguard, nextdns, custom" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -686,6 +892,31 @@ validate_configuration() {
|
|||||||
log_fatal "At least one of CLIENT_IPV4 or CLIENT_IPV6 must be 'y'"
|
log_fatal "At least one of CLIENT_IPV4 or CLIENT_IPV6 must be 'y'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
case "$ROUTE_INTERNET" in
|
||||||
|
y | n) ;;
|
||||||
|
*) log_fatal "Invalid ROUTE_INTERNET value: $ROUTE_INTERNET. Must be 'y' or 'n'." ;;
|
||||||
|
esac
|
||||||
|
case "$CLIENT_TO_CLIENT" in
|
||||||
|
y | n) ;;
|
||||||
|
*) log_fatal "Invalid CLIENT_TO_CLIENT value: $CLIENT_TO_CLIENT. Must be 'y' or 'n'." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
normalize_local_networks
|
||||||
|
if has_local_network_family 4 && [[ $CLIENT_IPV4 != "y" ]]; then
|
||||||
|
log_fatal "IPv4 local networks require IPv4 for VPN clients. Use --client-ipv4 or remove the IPv4 local network."
|
||||||
|
fi
|
||||||
|
if has_local_network_family 6 && [[ $CLIENT_IPV6 != "y" ]]; then
|
||||||
|
log_fatal "IPv6 local networks require IPv6 for VPN clients. Use --client-ipv6 or remove the IPv6 local network."
|
||||||
|
fi
|
||||||
|
local local_network
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
if [[ $local_network == *.* ]] && ipv4_cidrs_overlap "$local_network" "$VPN_SUBNET_IPV4/24"; then
|
||||||
|
log_fatal "Local network $local_network overlaps the IPv4 VPN subnet $VPN_SUBNET_IPV4/24."
|
||||||
|
elif [[ $local_network == *:* ]] && ipv6_cidrs_overlap "$local_network" "${VPN_SUBNET_IPV6}/112"; then
|
||||||
|
log_fatal "Local network $local_network overlaps the IPv6 VPN subnet ${VPN_SUBNET_IPV6}/112."
|
||||||
|
fi
|
||||||
|
done < <(tr ',' '\n' <<<"$LOCAL_NETWORKS")
|
||||||
|
|
||||||
# Validate ENDPOINT_TYPE
|
# Validate ENDPOINT_TYPE
|
||||||
case "$ENDPOINT_TYPE" in
|
case "$ENDPOINT_TYPE" in
|
||||||
4 | 6) ;;
|
4 | 6) ;;
|
||||||
@@ -950,6 +1181,27 @@ cmd_install() {
|
|||||||
VPN_SUBNET_IPV4="$2"
|
VPN_SUBNET_IPV4="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--route-internet)
|
||||||
|
ROUTE_INTERNET=y
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--no-route-internet)
|
||||||
|
ROUTE_INTERNET=n
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--client-to-client)
|
||||||
|
CLIENT_TO_CLIENT=y
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--no-client-to-client)
|
||||||
|
CLIENT_TO_CLIENT=n
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--local-network)
|
||||||
|
[[ -z "${2:-}" ]] && log_fatal "--local-network requires an argument"
|
||||||
|
add_local_network "$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
--port)
|
--port)
|
||||||
[[ -z "${2:-}" ]] && log_fatal "--port requires an argument"
|
[[ -z "${2:-}" ]] && log_fatal "--port requires an argument"
|
||||||
validate_port "$2"
|
validate_port "$2"
|
||||||
@@ -1157,13 +1409,18 @@ cmd_install() {
|
|||||||
# Set all defaults for any unset values
|
# Set all defaults for any unset values
|
||||||
set_installation_defaults
|
set_installation_defaults
|
||||||
|
|
||||||
# Validate configuration values (catches invalid env vars)
|
|
||||||
validate_configuration
|
|
||||||
|
|
||||||
# Detect IPs and set up network config (interactive mode does this in installQuestions)
|
# Detect IPs and set up network config (interactive mode does this in installQuestions)
|
||||||
detect_server_ips
|
detect_server_ips
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Split-tunnel installs leave the client's DNS configuration unchanged.
|
||||||
|
if [[ $ROUTE_INTERNET == "n" ]]; then
|
||||||
|
DNS=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate both CLI and interactive configuration.
|
||||||
|
validate_configuration
|
||||||
|
|
||||||
# Prepare derived network configuration (gateways, etc.)
|
# Prepare derived network configuration (gateways, etc.)
|
||||||
prepare_network_config
|
prepare_network_config
|
||||||
|
|
||||||
@@ -2307,6 +2564,39 @@ function installQuestions() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# Step 7: Client access policy
|
||||||
|
# ==========================================================================
|
||||||
|
log_menu ""
|
||||||
|
log_prompt "What should VPN clients be allowed to access?"
|
||||||
|
prompt_yes_no "Route client internet traffic through the VPN?" "y" ROUTE_INTERNET
|
||||||
|
prompt_yes_no "Allow VPN clients to access each other?" "n" CLIENT_TO_CLIENT
|
||||||
|
|
||||||
|
local local_network_access
|
||||||
|
prompt_yes_no "Allow VPN clients to access the server's local network? (mainly for home servers)" "n" local_network_access
|
||||||
|
if [[ $local_network_access == "y" ]]; then
|
||||||
|
log_prompt "Enter the server-side networks clients may access."
|
||||||
|
log_prompt "Use comma-separated CIDRs, for example: 192.168.1.0/24,fd00:1::/64"
|
||||||
|
until [[ -n $LOCAL_NETWORKS ]]; do
|
||||||
|
local configured_networks network networks_valid=true
|
||||||
|
read -rp "Local networks: " -e configured_networks
|
||||||
|
while IFS= read -r network; do
|
||||||
|
network="${network//[[:space:]]/}"
|
||||||
|
if [[ -z $network ]] || ! is_valid_local_network "$network"; then
|
||||||
|
log_warn "Invalid network CIDR: ${network:-<empty>}"
|
||||||
|
networks_valid=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < <(tr ',' '\n' <<<"$configured_networks")
|
||||||
|
if [[ $networks_valid == true ]]; then
|
||||||
|
LOCAL_NETWORKS="$configured_networks"
|
||||||
|
normalize_local_networks
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
LOCAL_NETWORKS=""
|
||||||
|
fi
|
||||||
|
|
||||||
log_menu ""
|
log_menu ""
|
||||||
log_prompt "What port do you want OpenVPN to listen to?"
|
log_prompt "What port do you want OpenVPN to listen to?"
|
||||||
log_menu " 1) Default: 1194"
|
log_menu " 1) Default: 1194"
|
||||||
@@ -2346,6 +2636,7 @@ function installQuestions() {
|
|||||||
PROTOCOL="tcp"
|
PROTOCOL="tcp"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
if [[ $ROUTE_INTERNET == "y" ]]; then
|
||||||
log_menu ""
|
log_menu ""
|
||||||
log_prompt "What DNS resolvers do you want to use with the VPN?"
|
log_prompt "What DNS resolvers do you want to use with the VPN?"
|
||||||
local dns_labels=("Current system resolvers (from /etc/resolv.conf)" "Self-hosted DNS Resolver (Unbound)" "Cloudflare (Anycast: worldwide)" "Quad9 (Anycast: worldwide)" "Quad9 uncensored (Anycast: worldwide)" "FDN (France)" "DNS.WATCH (Germany)" "OpenDNS (Anycast: worldwide)" "Google (Anycast: worldwide)" "Yandex Basic (Russia)" "AdGuard DNS (Anycast: worldwide)" "NextDNS (Anycast: worldwide)" "Custom")
|
local dns_labels=("Current system resolvers (from /etc/resolv.conf)" "Self-hosted DNS Resolver (Unbound)" "Cloudflare (Anycast: worldwide)" "Quad9 (Anycast: worldwide)" "Quad9 uncensored (Anycast: worldwide)" "FDN (France)" "DNS.WATCH (Germany)" "OpenDNS (Anycast: worldwide)" "Google (Anycast: worldwide)" "Yandex Basic (Russia)" "AdGuard DNS (Anycast: worldwide)" "NextDNS (Anycast: worldwide)" "Custom")
|
||||||
@@ -2384,6 +2675,10 @@ function installQuestions() {
|
|||||||
dns_valid=true
|
dns_valid=true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
DNS=""
|
||||||
|
log_info "VPN DNS is not configured because internet routing is disabled."
|
||||||
|
fi
|
||||||
log_menu ""
|
log_menu ""
|
||||||
log_prompt "Do you want to allow a single .ovpn profile to be used on multiple devices simultaneously?"
|
log_prompt "Do you want to allow a single .ovpn profile to be used on multiple devices simultaneously?"
|
||||||
log_prompt "Note: Enabling this disables persistent IP addresses for clients."
|
log_prompt "Note: Enabling this disables persistent IP addresses for clients."
|
||||||
@@ -2629,6 +2924,9 @@ function installOpenVPN() {
|
|||||||
log_info " CLIENT_IPV6=$CLIENT_IPV6"
|
log_info " CLIENT_IPV6=$CLIENT_IPV6"
|
||||||
log_info " VPN_SUBNET_IPV4=$VPN_SUBNET_IPV4"
|
log_info " VPN_SUBNET_IPV4=$VPN_SUBNET_IPV4"
|
||||||
log_info " VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
log_info " VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
||||||
|
log_info " ROUTE_INTERNET=$ROUTE_INTERNET"
|
||||||
|
log_info " CLIENT_TO_CLIENT=$CLIENT_TO_CLIENT"
|
||||||
|
log_info " LOCAL_NETWORKS=${LOCAL_NETWORKS:-none}"
|
||||||
log_info " PORT=$PORT"
|
log_info " PORT=$PORT"
|
||||||
log_info " PROTOCOL=$PROTOCOL"
|
log_info " PROTOCOL=$PROTOCOL"
|
||||||
log_info " DNS=$DNS"
|
log_info " DNS=$DNS"
|
||||||
@@ -2862,6 +3160,9 @@ function installOpenVPN() {
|
|||||||
if [[ $MULTI_CLIENT == "y" ]]; then
|
if [[ $MULTI_CLIENT == "y" ]]; then
|
||||||
echo "duplicate-cn" >>/etc/openvpn/server/server.conf
|
echo "duplicate-cn" >>/etc/openvpn/server/server.conf
|
||||||
fi
|
fi
|
||||||
|
if [[ $CLIENT_TO_CLIENT == "y" ]]; then
|
||||||
|
echo "client-to-client" >>/etc/openvpn/server/server.conf
|
||||||
|
fi
|
||||||
|
|
||||||
echo "dev tun" >>/etc/openvpn/server/server.conf
|
echo "dev tun" >>/etc/openvpn/server/server.conf
|
||||||
# Only add user/group if systemd doesn't handle it (avoids double privilege drop)
|
# Only add user/group if systemd doesn't handle it (avoids double privilege drop)
|
||||||
@@ -2892,7 +3193,8 @@ topology subnet" >>/etc/openvpn/server/server.conf
|
|||||||
echo "ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server/server.conf
|
echo "ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server/server.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# DNS resolvers
|
# DNS resolvers are only pushed when the VPN carries internet traffic.
|
||||||
|
if [[ $ROUTE_INTERNET == "y" ]]; then
|
||||||
case $DNS in
|
case $DNS in
|
||||||
system)
|
system)
|
||||||
# Locate the proper resolv.conf
|
# Locate the proper resolv.conf
|
||||||
@@ -3027,18 +3329,33 @@ topology subnet" >>/etc/openvpn/server/server.conf
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
# Redirect gateway settings - always redirect both IPv4 and IPv6 to prevent leaks
|
# Push explicit routes for server-side networks. These routes are independent
|
||||||
# For IPv4: redirect-gateway def1 routes all IPv4 through VPN (or drops it if IPv4 not configured)
|
# from internet routing and are protected by matching firewall rules.
|
||||||
# For IPv6: route-ipv6 + redirect-gateway ipv6 routes all IPv6, or block-ipv6 drops it
|
local local_network address prefix netmask
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
address="${local_network%/*}"
|
||||||
|
prefix="${local_network##*/}"
|
||||||
|
netmask=$(ipv4_prefix_to_netmask "$prefix")
|
||||||
|
echo "push \"route $address $netmask\"" >>/etc/openvpn/server/server.conf
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo "push \"route-ipv6 $local_network\"" >>/etc/openvpn/server/server.conf
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
|
||||||
|
# Full-tunnel mode redirects enabled address families and blocks leaks from
|
||||||
|
# disabled families. Split-tunnel mode leaves normal client internet routes intact.
|
||||||
|
if [[ $ROUTE_INTERNET == "y" ]]; 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
|
||||||
if [[ $CLIENT_IPV6 == "y" ]]; then
|
if [[ $CLIENT_IPV6 == "y" ]]; then
|
||||||
echo 'push "route-ipv6 2000::/3"' >>/etc/openvpn/server/server.conf
|
echo 'push "route-ipv6 2000::/3"' >>/etc/openvpn/server/server.conf
|
||||||
echo 'push "redirect-gateway ipv6"' >>/etc/openvpn/server/server.conf
|
echo 'push "redirect-gateway ipv6"' >>/etc/openvpn/server/server.conf
|
||||||
else
|
else
|
||||||
# Block IPv6 on clients to prevent IPv6 leaks when VPN only handles IPv4
|
# Block IPv6 on clients to prevent IPv6 leaks when VPN only handles IPv4.
|
||||||
echo 'push "block-ipv6"' >>/etc/openvpn/server/server.conf
|
echo 'push "block-ipv6"' >>/etc/openvpn/server/server.conf
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n $MTU ]]; then
|
if [[ -n $MTU ]]; then
|
||||||
echo "tun-mtu $MTU" >>/etc/openvpn/server/server.conf
|
echo "tun-mtu $MTU" >>/etc/openvpn/server/server.conf
|
||||||
@@ -3084,6 +3401,24 @@ management /var/run/openvpn-server/server.sock unix
|
|||||||
verb 3"
|
verb 3"
|
||||||
} >>/etc/openvpn/server/server.conf
|
} >>/etc/openvpn/server/server.conf
|
||||||
|
|
||||||
|
# Record installer-owned policy so firewall rules can be removed exactly.
|
||||||
|
if systemctl is-active --quiet firewalld; then
|
||||||
|
FIREWALL_BACKEND=firewalld
|
||||||
|
elif systemctl is-active --quiet nftables; then
|
||||||
|
FIREWALL_BACKEND=nftables
|
||||||
|
else
|
||||||
|
FIREWALL_BACKEND=iptables
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "FIREWALL_BACKEND=$FIREWALL_BACKEND"
|
||||||
|
echo "ROUTE_INTERNET=$ROUTE_INTERNET"
|
||||||
|
echo "CLIENT_TO_CLIENT=$CLIENT_TO_CLIENT"
|
||||||
|
echo "LOCAL_NETWORKS=$LOCAL_NETWORKS"
|
||||||
|
echo "CLIENT_IPV4=$CLIENT_IPV4"
|
||||||
|
echo "CLIENT_IPV6=$CLIENT_IPV6"
|
||||||
|
} >/etc/openvpn/server/openvpn-install.conf
|
||||||
|
chmod 600 /etc/openvpn/server/openvpn-install.conf
|
||||||
|
|
||||||
# Create client-config-dir dir
|
# Create client-config-dir dir
|
||||||
run_cmd_fatal "Creating client config directory" mkdir -p /etc/openvpn/server/ccd
|
run_cmd_fatal "Creating client config directory" mkdir -p /etc/openvpn/server/ccd
|
||||||
# Create log dir
|
# Create log dir
|
||||||
@@ -3096,19 +3431,22 @@ verb 3"
|
|||||||
chown -R "$OPENVPN_USER:$OPENVPN_GROUP" /etc/openvpn/server
|
chown -R "$OPENVPN_USER:$OPENVPN_GROUP" /etc/openvpn/server
|
||||||
chown "$OPENVPN_USER:$OPENVPN_GROUP" /var/log/openvpn
|
chown "$OPENVPN_USER:$OPENVPN_GROUP" /var/log/openvpn
|
||||||
fi
|
fi
|
||||||
|
chown root:root /etc/openvpn/server/openvpn-install.conf
|
||||||
|
chmod 600 /etc/openvpn/server/openvpn-install.conf
|
||||||
|
|
||||||
# Enable routing
|
# Enable routing
|
||||||
log_info "Enabling IP forwarding..."
|
log_info "Enabling IP forwarding..."
|
||||||
run_cmd_fatal "Creating sysctl.d directory" mkdir -p /etc/sysctl.d
|
run_cmd_fatal "Creating sysctl.d directory" mkdir -p /etc/sysctl.d
|
||||||
|
|
||||||
# Enable IPv4 forwarding if clients get IPv4
|
# Forwarding is needed for internet or server-side network access. OpenVPN
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
# handles non-DCO client-to-client traffic internally, while DCO traffic is
|
||||||
|
# still constrained by the firewall rules below.
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' ]] && { [[ $ROUTE_INTERNET == 'y' ]] || has_local_network_family 4 || [[ $CLIENT_TO_CLIENT == 'y' ]]; }; then
|
||||||
echo 'net.ipv4.ip_forward=1' >/etc/sysctl.d/99-openvpn.conf
|
echo 'net.ipv4.ip_forward=1' >/etc/sysctl.d/99-openvpn.conf
|
||||||
else
|
else
|
||||||
echo '# IPv4 forwarding not needed (no IPv4 clients)' >/etc/sysctl.d/99-openvpn.conf
|
echo '# IPv4 forwarding not required by the selected access policy' >/etc/sysctl.d/99-openvpn.conf
|
||||||
fi
|
fi
|
||||||
# Enable IPv6 forwarding if clients get IPv6
|
if [[ $CLIENT_IPV6 == 'y' ]] && { [[ $ROUTE_INTERNET == 'y' ]] || has_local_network_family 6 || [[ $CLIENT_TO_CLIENT == 'y' ]]; }; then
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
|
||||||
echo 'net.ipv6.conf.all.forwarding=1' >>/etc/sysctl.d/99-openvpn.conf
|
echo 'net.ipv6.conf.all.forwarding=1' >>/etc/sysctl.d/99-openvpn.conf
|
||||||
fi
|
fi
|
||||||
# Apply sysctl rules
|
# Apply sysctl rules
|
||||||
@@ -3186,7 +3524,7 @@ verb 3"
|
|||||||
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
|
run_cmd "Starting OpenVPN service" systemctl restart openvpn-server@server
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $DNS == "unbound" ]]; then
|
if [[ $ROUTE_INTERNET == "y" && $DNS == "unbound" ]]; then
|
||||||
installUnbound
|
installUnbound
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -3194,34 +3532,62 @@ verb 3"
|
|||||||
# Use source-based rules for VPN traffic (works reliably regardless of which tun interface OpenVPN uses)
|
# Use source-based rules for VPN traffic (works reliably regardless of which tun interface OpenVPN uses)
|
||||||
log_info "Configuring firewall rules..."
|
log_info "Configuring firewall rules..."
|
||||||
|
|
||||||
if systemctl is-active --quiet firewalld; then
|
if [[ $FIREWALL_BACKEND == 'firewalld' ]]; then
|
||||||
# Use firewalld native commands for systems with firewalld active
|
# Rich-rule priorities make explicit local and peer access win before the
|
||||||
|
# private-network deny rules, followed by the selected default policy.
|
||||||
log_info "firewalld detected, using firewall-cmd..."
|
log_info "firewalld detected, using firewall-cmd..."
|
||||||
run_cmd "Adding OpenVPN port to firewalld" firewall-cmd --permanent --add-port="$PORT/$PROTOCOL"
|
run_cmd_fatal "Adding OpenVPN port to firewalld" firewall-cmd --permanent --add-port="$PORT/$PROTOCOL"
|
||||||
run_cmd "Adding masquerade to firewalld" firewall-cmd --permanent --add-masquerade
|
|
||||||
|
|
||||||
# Add rich rules for VPN traffic (source-based only, as firewalld doesn't reliably
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
# support interface patterns with direct rules when using nftables backend)
|
run_cmd_fatal "Allowing the IPv4 VPN gateway" firewall-cmd --permanent --add-rich-rule="rule priority=\"-400\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$VPN_GATEWAY_IPV4/32\" accept"
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
while IFS= read -r local_network; do
|
||||||
run_cmd "Adding IPv4 VPN subnet rule" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" accept"
|
run_cmd_fatal "Allowing local IPv4 network $local_network" firewall-cmd --permanent --add-rich-rule="rule priority=\"-300\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$local_network\" accept"
|
||||||
|
run_cmd_fatal "Adding NAT for local IPv4 network $local_network" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$local_network\" masquerade"
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
run_cmd_fatal "Allowing IPv4 client-to-client traffic" firewall-cmd --permanent --add-rich-rule="rule priority=\"-300\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$VPN_SUBNET_IPV4/24\" accept"
|
||||||
|
fi
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV4_NETWORKS[@]}"; do
|
||||||
|
run_cmd_fatal "Protecting IPv4 network $protected_network" firewall-cmd --permanent --add-rich-rule="rule priority=\"-200\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$protected_network\" reject"
|
||||||
|
done
|
||||||
|
run_cmd_fatal "Allowing IPv4 internet access" firewall-cmd --permanent --add-rich-rule="rule priority=\"-100\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" accept"
|
||||||
|
run_cmd_fatal "Adding IPv4 internet NAT" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" masquerade"
|
||||||
|
else
|
||||||
|
run_cmd_fatal "Restricting other IPv4 access" firewall-cmd --permanent --add-rich-rule="rule priority=\"-100\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" reject"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
run_cmd "Adding IPv6 VPN subnet rule" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" accept"
|
run_cmd_fatal "Allowing the IPv6 VPN gateway" firewall-cmd --permanent --add-rich-rule="rule priority=\"-400\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$VPN_GATEWAY_IPV6/128\" accept"
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
run_cmd_fatal "Allowing local IPv6 network $local_network" firewall-cmd --permanent --add-rich-rule="rule priority=\"-300\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$local_network\" accept"
|
||||||
|
run_cmd_fatal "Adding NAT for local IPv6 network $local_network" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$local_network\" masquerade"
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
if [[ $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
run_cmd_fatal "Allowing IPv6 client-to-client traffic" firewall-cmd --permanent --add-rich-rule="rule priority=\"-300\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"${VPN_SUBNET_IPV6}/112\" accept"
|
||||||
|
fi
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV6_NETWORKS[@]}"; do
|
||||||
|
run_cmd_fatal "Protecting IPv6 network $protected_network" firewall-cmd --permanent --add-rich-rule="rule priority=\"-200\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$protected_network\" reject"
|
||||||
|
done
|
||||||
|
run_cmd_fatal "Allowing IPv6 internet access" firewall-cmd --permanent --add-rich-rule="rule priority=\"-100\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" accept"
|
||||||
|
run_cmd_fatal "Adding IPv6 internet NAT" firewall-cmd --permanent --add-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" masquerade"
|
||||||
|
else
|
||||||
|
run_cmd_fatal "Restricting other IPv6 access" firewall-cmd --permanent --add-rich-rule="rule priority=\"-100\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" reject"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_cmd "Reloading firewalld" firewall-cmd --reload
|
run_cmd_fatal "Reloading firewalld" firewall-cmd --reload
|
||||||
elif systemctl is-active --quiet nftables; then
|
elif [[ $FIREWALL_BACKEND == 'nftables' ]]; then
|
||||||
# Use nftables native rules for systems with nftables active
|
|
||||||
log_info "nftables detected, configuring nftables rules..."
|
log_info "nftables detected, configuring nftables rules..."
|
||||||
run_cmd_fatal "Creating nftables directory" mkdir -p /etc/nftables
|
run_cmd_fatal "Creating nftables directory" mkdir -p /etc/nftables
|
||||||
|
|
||||||
# Create nftables rules file
|
|
||||||
{
|
{
|
||||||
echo "table inet openvpn {"
|
echo "table inet openvpn {"
|
||||||
echo " chain input {"
|
echo " chain input {"
|
||||||
echo " type filter hook input priority 0; policy accept;"
|
echo " type filter hook input priority 0; policy accept;"
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 accept"
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 accept"
|
||||||
fi
|
fi
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
@@ -3231,93 +3597,197 @@ verb 3"
|
|||||||
echo " }"
|
echo " }"
|
||||||
echo ""
|
echo ""
|
||||||
echo " chain forward {"
|
echo " chain forward {"
|
||||||
echo " type filter hook forward priority 0; policy accept;"
|
echo " type filter hook forward priority -10; policy accept;"
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
|
echo " oifname \"tun*\" ip daddr $VPN_SUBNET_IPV4/24 ct state established,related accept"
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 ip daddr $local_network accept"
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 ip daddr $VPN_SUBNET_IPV4/24 accept"
|
||||||
|
fi
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV4_NETWORKS[@]}"; do
|
||||||
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 ip daddr $protected_network drop"
|
||||||
|
done
|
||||||
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 accept"
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 accept"
|
||||||
echo " oifname \"tun*\" ip daddr $VPN_SUBNET_IPV4/24 accept"
|
else
|
||||||
|
echo " iifname \"tun*\" ip saddr $VPN_SUBNET_IPV4/24 drop"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
|
echo " oifname \"tun*\" ip6 daddr ${VPN_SUBNET_IPV6}/112 ct state established,related accept"
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 ip6 daddr $local_network accept"
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
if [[ $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 ip6 daddr ${VPN_SUBNET_IPV6}/112 accept"
|
||||||
|
fi
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV6_NETWORKS[@]}"; do
|
||||||
|
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 ip6 daddr $protected_network drop"
|
||||||
|
done
|
||||||
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 accept"
|
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 accept"
|
||||||
echo " oifname \"tun*\" ip6 daddr ${VPN_SUBNET_IPV6}/112 accept"
|
else
|
||||||
|
echo " iifname \"tun*\" ip6 saddr ${VPN_SUBNET_IPV6}/112 drop"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
echo " }"
|
echo " }"
|
||||||
echo "}"
|
echo "}"
|
||||||
} >/etc/nftables/openvpn.nft
|
} >/etc/nftables/openvpn.nft
|
||||||
|
|
||||||
# IPv4 NAT rules (only if clients get IPv4)
|
if [[ $CLIENT_IPV4 == 'y' ]] && { [[ $ROUTE_INTERNET == 'y' ]] || has_local_network_family 4; }; then
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
{
|
||||||
echo "
|
echo ""
|
||||||
table ip openvpn-nat {
|
echo "table ip openvpn-nat {"
|
||||||
chain postrouting {
|
echo " chain postrouting {"
|
||||||
type nat hook postrouting priority 100; policy accept;
|
echo " type nat hook postrouting priority 100; policy accept;"
|
||||||
ip saddr $VPN_SUBNET_IPV4/24 oifname \"$NIC\" masquerade
|
while IFS= read -r local_network; do
|
||||||
}
|
echo " ip saddr $VPN_SUBNET_IPV4/24 ip daddr $local_network masquerade"
|
||||||
}" >>/etc/nftables/openvpn.nft
|
done < <(local_networks_for_family 4)
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
echo " ip saddr $VPN_SUBNET_IPV4/24 oifname \"$NIC\" masquerade"
|
||||||
|
fi
|
||||||
|
echo " }"
|
||||||
|
echo "}"
|
||||||
|
} >>/etc/nftables/openvpn.nft
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# IPv6 NAT rules (only if clients get IPv6)
|
if [[ $CLIENT_IPV6 == 'y' ]] && { [[ $ROUTE_INTERNET == 'y' ]] || has_local_network_family 6; }; then
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
{
|
||||||
echo "
|
echo ""
|
||||||
table ip6 openvpn-nat {
|
echo "table ip6 openvpn-nat {"
|
||||||
chain postrouting {
|
echo " chain postrouting {"
|
||||||
type nat hook postrouting priority 100; policy accept;
|
echo " type nat hook postrouting priority 100; policy accept;"
|
||||||
ip6 saddr ${VPN_SUBNET_IPV6}/112 oifname \"$NIC\" masquerade
|
while IFS= read -r local_network; do
|
||||||
}
|
echo " ip6 saddr ${VPN_SUBNET_IPV6}/112 ip6 daddr $local_network masquerade"
|
||||||
}" >>/etc/nftables/openvpn.nft
|
done < <(local_networks_for_family 6)
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
echo " ip6 saddr ${VPN_SUBNET_IPV6}/112 oifname \"$NIC\" masquerade"
|
||||||
|
fi
|
||||||
|
echo " }"
|
||||||
|
echo "}"
|
||||||
|
} >>/etc/nftables/openvpn.nft
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add include to nftables.conf if not already present
|
|
||||||
if ! grep -q 'include.*/etc/nftables/openvpn.nft' /etc/nftables.conf; then
|
if ! grep -q 'include.*/etc/nftables/openvpn.nft' /etc/nftables.conf; then
|
||||||
run_cmd "Adding include to nftables.conf" sh -c 'echo "include \"/etc/nftables/openvpn.nft\"" >> /etc/nftables.conf'
|
run_cmd_fatal "Adding include to nftables.conf" sh -c 'echo "include \"/etc/nftables/openvpn.nft\"" >> /etc/nftables.conf'
|
||||||
fi
|
fi
|
||||||
|
run_cmd_fatal "Reloading nftables" systemctl reload nftables
|
||||||
# Reload nftables to apply rules
|
|
||||||
run_cmd "Reloading nftables" systemctl reload nftables
|
|
||||||
else
|
else
|
||||||
# Use iptables for systems without firewalld or nftables
|
# Use iptables for systems without firewalld or nftables
|
||||||
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
run_cmd_fatal "Creating iptables directory" mkdir -p /etc/iptables
|
||||||
|
|
||||||
# Script to add rules
|
# Dedicated chains enforce the same policy for userspace and DCO traffic.
|
||||||
echo "#!/bin/sh" >/etc/iptables/add-openvpn-rules.sh
|
{
|
||||||
|
echo "#!/bin/sh"
|
||||||
# IPv4 rules (only if clients get IPv4)
|
echo "set -eu"
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
echo "if iptables -nL OPENVPN_INSTALL_FORWARD >/dev/null 2>&1; then"
|
||||||
echo "iptables -t nat -I POSTROUTING 1 -s $VPN_SUBNET_IPV4/24 -o $NIC -j MASQUERADE
|
echo " echo 'iptables chain OPENVPN_INSTALL_FORWARD already exists' >&2"
|
||||||
iptables -I INPUT 1 -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT
|
echo " exit 1"
|
||||||
iptables -I FORWARD 1 -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT
|
echo "fi"
|
||||||
iptables -I FORWARD 1 -o tun+ -d $VPN_SUBNET_IPV4/24 -j ACCEPT
|
|
||||||
iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# IPv6 rules (only if clients get IPv6)
|
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
echo "ip6tables -t nat -I POSTROUTING 1 -s ${VPN_SUBNET_IPV6}/112 -o $NIC -j MASQUERADE
|
echo "if ip6tables -nL OPENVPN_INSTALL_FORWARD >/dev/null 2>&1; then"
|
||||||
ip6tables -I INPUT 1 -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo " echo 'ip6tables chain OPENVPN_INSTALL_FORWARD already exists' >&2"
|
||||||
ip6tables -I FORWARD 1 -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo " exit 1"
|
||||||
ip6tables -I FORWARD 1 -o tun+ -d ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo "fi"
|
||||||
ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
|
fi
|
||||||
|
echo "cleanup() { /etc/iptables/rm-openvpn-rules.sh >/dev/null 2>&1 || true; }"
|
||||||
|
echo "trap cleanup EXIT HUP INT TERM"
|
||||||
|
} >/etc/iptables/add-openvpn-rules.sh
|
||||||
|
{
|
||||||
|
echo "#!/bin/sh"
|
||||||
|
echo "set -u"
|
||||||
|
echo 'remove_rule() { "$@" 2>/dev/null || true; }'
|
||||||
|
} >/etc/iptables/rm-openvpn-rules.sh
|
||||||
|
|
||||||
|
if [[ $ENDPOINT_TYPE == '4' ]]; then
|
||||||
|
echo "iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
|
||||||
|
echo "remove_rule iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
|
||||||
|
else
|
||||||
|
echo "ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh
|
||||||
|
echo "remove_rule ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Script to remove rules
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
echo "#!/bin/sh" >/etc/iptables/rm-openvpn-rules.sh
|
{
|
||||||
|
echo "iptables -N OPENVPN_INSTALL_FORWARD"
|
||||||
|
echo "iptables -I INPUT 1 -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT"
|
||||||
|
echo "iptables -I FORWARD 1 -o tun+ -d $VPN_SUBNET_IPV4/24 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
|
||||||
|
echo "iptables -I FORWARD 1 -i tun+ -s $VPN_SUBNET_IPV4/24 -j OPENVPN_INSTALL_FORWARD"
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo "iptables -A OPENVPN_INSTALL_FORWARD -d $local_network -j ACCEPT"
|
||||||
|
echo "iptables -t nat -I POSTROUTING 1 -s $VPN_SUBNET_IPV4/24 -d $local_network -j MASQUERADE"
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
echo "iptables -A OPENVPN_INSTALL_FORWARD -d $VPN_SUBNET_IPV4/24 -j ACCEPT"
|
||||||
|
fi
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV4_NETWORKS[@]}"; do
|
||||||
|
echo "iptables -A OPENVPN_INSTALL_FORWARD -d $protected_network -j REJECT"
|
||||||
|
done
|
||||||
|
echo "iptables -A OPENVPN_INSTALL_FORWARD -j ACCEPT"
|
||||||
|
echo "iptables -t nat -I POSTROUTING 1 -s $VPN_SUBNET_IPV4/24 -o $NIC -j MASQUERADE"
|
||||||
|
else
|
||||||
|
echo "iptables -A OPENVPN_INSTALL_FORWARD -j REJECT"
|
||||||
|
fi
|
||||||
|
} >>/etc/iptables/add-openvpn-rules.sh
|
||||||
|
|
||||||
# IPv4 removal rules
|
{
|
||||||
if [[ $CLIENT_IPV4 == 'y' ]]; then
|
echo "remove_rule iptables -D FORWARD -i tun+ -s $VPN_SUBNET_IPV4/24 -j OPENVPN_INSTALL_FORWARD"
|
||||||
echo "iptables -t nat -D POSTROUTING -s $VPN_SUBNET_IPV4/24 -o $NIC -j MASQUERADE
|
echo "remove_rule iptables -D FORWARD -o tun+ -d $VPN_SUBNET_IPV4/24 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
|
||||||
iptables -D INPUT -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT
|
echo "remove_rule iptables -D INPUT -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT"
|
||||||
iptables -D FORWARD -i tun+ -s $VPN_SUBNET_IPV4/24 -j ACCEPT
|
if [[ $CLIENT_IPV4 == 'y' && $ROUTE_INTERNET == 'y' ]]; then
|
||||||
iptables -D FORWARD -o tun+ -d $VPN_SUBNET_IPV4/24 -j ACCEPT
|
echo "remove_rule iptables -t nat -D POSTROUTING -s $VPN_SUBNET_IPV4/24 -o $NIC -j MASQUERADE"
|
||||||
iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
|
fi
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo "remove_rule iptables -t nat -D POSTROUTING -s $VPN_SUBNET_IPV4/24 -d $local_network -j MASQUERADE"
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
echo "remove_rule iptables -F OPENVPN_INSTALL_FORWARD"
|
||||||
|
echo "remove_rule iptables -X OPENVPN_INSTALL_FORWARD"
|
||||||
|
} >>/etc/iptables/rm-openvpn-rules.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# IPv6 removal rules
|
|
||||||
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
echo "ip6tables -t nat -D POSTROUTING -s ${VPN_SUBNET_IPV6}/112 -o $NIC -j MASQUERADE
|
{
|
||||||
ip6tables -D INPUT -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo "ip6tables -N OPENVPN_INSTALL_FORWARD"
|
||||||
ip6tables -D FORWARD -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo "ip6tables -I INPUT 1 -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT"
|
||||||
ip6tables -D FORWARD -o tun+ -d ${VPN_SUBNET_IPV6}/112 -j ACCEPT
|
echo "ip6tables -I FORWARD 1 -o tun+ -d ${VPN_SUBNET_IPV6}/112 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
|
||||||
ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh
|
echo "ip6tables -I FORWARD 1 -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j OPENVPN_INSTALL_FORWARD"
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo "ip6tables -A OPENVPN_INSTALL_FORWARD -d $local_network -j ACCEPT"
|
||||||
|
echo "ip6tables -t nat -I POSTROUTING 1 -s ${VPN_SUBNET_IPV6}/112 -d $local_network -j MASQUERADE"
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
if [[ $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
echo "ip6tables -A OPENVPN_INSTALL_FORWARD -d ${VPN_SUBNET_IPV6}/112 -j ACCEPT"
|
||||||
fi
|
fi
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV6_NETWORKS[@]}"; do
|
||||||
|
echo "ip6tables -A OPENVPN_INSTALL_FORWARD -d $protected_network -j REJECT"
|
||||||
|
done
|
||||||
|
echo "ip6tables -A OPENVPN_INSTALL_FORWARD -j ACCEPT"
|
||||||
|
echo "ip6tables -t nat -I POSTROUTING 1 -s ${VPN_SUBNET_IPV6}/112 -o $NIC -j MASQUERADE"
|
||||||
|
else
|
||||||
|
echo "ip6tables -A OPENVPN_INSTALL_FORWARD -j REJECT"
|
||||||
|
fi
|
||||||
|
} >>/etc/iptables/add-openvpn-rules.sh
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "remove_rule ip6tables -D FORWARD -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j OPENVPN_INSTALL_FORWARD"
|
||||||
|
echo "remove_rule ip6tables -D FORWARD -o tun+ -d ${VPN_SUBNET_IPV6}/112 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
|
||||||
|
echo "remove_rule ip6tables -D INPUT -i tun+ -s ${VPN_SUBNET_IPV6}/112 -j ACCEPT"
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
echo "remove_rule ip6tables -t nat -D POSTROUTING -s ${VPN_SUBNET_IPV6}/112 -o $NIC -j MASQUERADE"
|
||||||
|
fi
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
echo "remove_rule ip6tables -t nat -D POSTROUTING -s ${VPN_SUBNET_IPV6}/112 -d $local_network -j MASQUERADE"
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
echo "remove_rule ip6tables -F OPENVPN_INSTALL_FORWARD"
|
||||||
|
echo "remove_rule ip6tables -X OPENVPN_INSTALL_FORWARD"
|
||||||
|
} >>/etc/iptables/rm-openvpn-rules.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "trap - EXIT HUP INT TERM" >>/etc/iptables/add-openvpn-rules.sh
|
||||||
|
|
||||||
run_cmd "Making add-openvpn-rules.sh executable" chmod +x /etc/iptables/add-openvpn-rules.sh
|
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
|
run_cmd "Making rm-openvpn-rules.sh executable" chmod +x /etc/iptables/rm-openvpn-rules.sh
|
||||||
@@ -3341,7 +3811,7 @@ WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service
|
|||||||
# Enable service and apply rules
|
# Enable service and apply rules
|
||||||
run_cmd "Reloading systemd" systemctl daemon-reload
|
run_cmd "Reloading systemd" systemctl daemon-reload
|
||||||
run_cmd "Enabling iptables service" systemctl enable iptables-openvpn
|
run_cmd "Enabling iptables service" systemctl enable iptables-openvpn
|
||||||
run_cmd "Starting iptables service" systemctl start iptables-openvpn
|
run_cmd_fatal "Starting iptables service" systemctl start iptables-openvpn
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If the server is behind a NAT, use the correct IP address for the clients to connect to
|
# If the server is behind a NAT, use the correct IP address for the clients to connect to
|
||||||
@@ -4446,6 +4916,20 @@ function removeOpenVPN() {
|
|||||||
# Extract IPv6 subnet (may be empty if IPv6 not enabled)
|
# Extract IPv6 subnet (may be empty if IPv6 not enabled)
|
||||||
VPN_SUBNET_IPV6=$(grep '^server-ipv6 ' /etc/openvpn/server/server.conf | cut -d " " -f 2 | sed 's|/.*||')
|
VPN_SUBNET_IPV6=$(grep '^server-ipv6 ' /etc/openvpn/server/server.conf | cut -d " " -f 2 | sed 's|/.*||')
|
||||||
|
|
||||||
|
local install_config=/etc/openvpn/server/openvpn-install.conf
|
||||||
|
local has_policy_manifest=n
|
||||||
|
if [[ -f $install_config ]]; then
|
||||||
|
has_policy_manifest=y
|
||||||
|
FIREWALL_BACKEND=$(grep '^FIREWALL_BACKEND=' "$install_config" | cut -d= -f2-)
|
||||||
|
ROUTE_INTERNET=$(grep '^ROUTE_INTERNET=' "$install_config" | cut -d= -f2-)
|
||||||
|
CLIENT_TO_CLIENT=$(grep '^CLIENT_TO_CLIENT=' "$install_config" | cut -d= -f2-)
|
||||||
|
LOCAL_NETWORKS=$(grep '^LOCAL_NETWORKS=' "$install_config" | cut -d= -f2-)
|
||||||
|
CLIENT_IPV4=$(grep '^CLIENT_IPV4=' "$install_config" | cut -d= -f2-)
|
||||||
|
CLIENT_IPV6=$(grep '^CLIENT_IPV6=' "$install_config" | cut -d= -f2-)
|
||||||
|
VPN_GATEWAY_IPV4="${VPN_SUBNET_IPV4%.*}.1"
|
||||||
|
VPN_GATEWAY_IPV6="${VPN_SUBNET_IPV6}1"
|
||||||
|
fi
|
||||||
|
|
||||||
# Stop OpenVPN
|
# Stop OpenVPN
|
||||||
log_info "Stopping OpenVPN service..."
|
log_info "Stopping OpenVPN service..."
|
||||||
run_cmd "Disabling OpenVPN service" systemctl disable openvpn-server@server
|
run_cmd "Disabling OpenVPN service" systemctl disable openvpn-server@server
|
||||||
@@ -4455,20 +4939,59 @@ function removeOpenVPN() {
|
|||||||
|
|
||||||
# Remove firewall rules
|
# Remove firewall rules
|
||||||
log_info "Removing firewall rules..."
|
log_info "Removing firewall rules..."
|
||||||
if systemctl is-active --quiet firewalld && firewall-cmd --list-ports | grep -q "$PORT/$PROTOCOL_BASE"; then
|
if systemctl is-active --quiet firewalld && { [[ $has_policy_manifest == 'y' && $FIREWALL_BACKEND == 'firewalld' ]] || { [[ $has_policy_manifest == 'n' ]] && firewall-cmd --list-ports | grep -q "$PORT/$PROTOCOL_BASE"; }; }; then
|
||||||
# firewalld was used
|
|
||||||
run_cmd "Removing OpenVPN port from firewalld" firewall-cmd --permanent --remove-port="$PORT/$PROTOCOL_BASE"
|
run_cmd "Removing OpenVPN port from firewalld" firewall-cmd --permanent --remove-port="$PORT/$PROTOCOL_BASE"
|
||||||
|
if [[ $has_policy_manifest == 'y' ]]; then
|
||||||
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-400\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$VPN_GATEWAY_IPV4/32\" accept" 2>/dev/null || true
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-300\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$local_network\" accept" 2>/dev/null || true
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$local_network\" masquerade" 2>/dev/null || true
|
||||||
|
done < <(local_networks_for_family 4)
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-300\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$VPN_SUBNET_IPV4/24\" accept" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ $CLIENT_IPV4 == 'y' && $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV4_NETWORKS[@]}"; do
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-200\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" destination address=\"$protected_network\" reject" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-100\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" accept" 2>/dev/null || true
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" masquerade" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-100\" family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" reject" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ $CLIENT_IPV6 == 'y' ]]; then
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-400\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$VPN_GATEWAY_IPV6/128\" accept" 2>/dev/null || true
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-300\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$local_network\" accept" 2>/dev/null || true
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$local_network\" masquerade" 2>/dev/null || true
|
||||||
|
done < <(local_networks_for_family 6)
|
||||||
|
if [[ $CLIENT_TO_CLIENT == 'y' ]]; then
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-300\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"${VPN_SUBNET_IPV6}/112\" accept" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ $ROUTE_INTERNET == 'y' ]]; then
|
||||||
|
for protected_network in "${PROTECTED_IPV6_NETWORKS[@]}"; do
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-200\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" destination address=\"$protected_network\" reject" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-100\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" accept" 2>/dev/null || true
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" masquerade" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
firewall-cmd --permanent --remove-rich-rule="rule priority=\"-100\" family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" reject" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Compatibility with installations created before policy manifests.
|
||||||
run_cmd "Removing masquerade from firewalld" firewall-cmd --permanent --remove-masquerade
|
run_cmd "Removing masquerade from firewalld" firewall-cmd --permanent --remove-masquerade
|
||||||
# Remove IPv4 rich rule if configured
|
|
||||||
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
if [[ -n $VPN_SUBNET_IPV4 ]]; then
|
||||||
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" accept" 2>/dev/null || true
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv4\" source address=\"$VPN_SUBNET_IPV4/24\" accept" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
# Remove IPv6 rich rule if configured
|
|
||||||
if [[ -n $VPN_SUBNET_IPV6 ]]; then
|
if [[ -n $VPN_SUBNET_IPV6 ]]; then
|
||||||
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" accept" 2>/dev/null || true
|
firewall-cmd --permanent --remove-rich-rule="rule family=\"ipv6\" source address=\"${VPN_SUBNET_IPV6}/112\" accept" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
run_cmd "Reloading firewalld" firewall-cmd --reload
|
run_cmd "Reloading firewalld" firewall-cmd --reload
|
||||||
elif [[ -f /etc/nftables/openvpn.nft ]]; then
|
elif [[ $has_policy_manifest == 'y' && $FIREWALL_BACKEND == 'nftables' && -f /etc/nftables/openvpn.nft ]] || [[ $has_policy_manifest == 'n' && -f /etc/nftables/openvpn.nft ]]; then
|
||||||
# nftables was used
|
# nftables was used
|
||||||
# Delete tables (suppress errors in case tables don't exist)
|
# Delete tables (suppress errors in case tables don't exist)
|
||||||
nft delete table inet openvpn 2>/dev/null || true
|
nft delete table inet openvpn 2>/dev/null || true
|
||||||
@@ -4476,7 +4999,7 @@ function removeOpenVPN() {
|
|||||||
nft delete table ip6 openvpn-nat 2>/dev/null || true
|
nft delete table ip6 openvpn-nat 2>/dev/null || true
|
||||||
run_cmd "Removing include from nftables.conf" sed -i '/include.*openvpn\.nft/d' /etc/nftables.conf
|
run_cmd "Removing include from nftables.conf" sed -i '/include.*openvpn\.nft/d' /etc/nftables.conf
|
||||||
run_cmd "Removing nftables rules file" rm -f /etc/nftables/openvpn.nft
|
run_cmd "Removing nftables rules file" rm -f /etc/nftables/openvpn.nft
|
||||||
elif [[ -f /etc/systemd/system/iptables-openvpn.service ]]; then
|
elif [[ $has_policy_manifest == 'y' && $FIREWALL_BACKEND == 'iptables' && -f /etc/systemd/system/iptables-openvpn.service ]] || [[ $has_policy_manifest == 'n' && -f /etc/systemd/system/iptables-openvpn.service ]]; then
|
||||||
# iptables was used
|
# iptables was used
|
||||||
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
run_cmd "Stopping iptables service" systemctl stop iptables-openvpn
|
||||||
run_cmd "Disabling iptables service" systemctl disable iptables-openvpn
|
run_cmd "Disabling iptables service" systemctl disable iptables-openvpn
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ RUN printf '%s\n' \
|
|||||||
'[Service]' \
|
'[Service]' \
|
||||||
'Type=oneshot' \
|
'Type=oneshot' \
|
||||||
'Environment=HOME=/root' \
|
'Environment=HOME=/root' \
|
||||||
'PassEnvironment=AUTH_MODE TLS_SIG TLS_KEY_FILE TLS_VERSION_MIN TLS13_CIPHERSUITES CLIENT_IPV6 VPN_SUBNET_IPV6 WAIT_TIMEOUT_SIGNAL WAIT_TIMEOUT_CONNECT WAIT_TIMEOUT_REVOKE' \
|
'PassEnvironment=AUTH_MODE TLS_SIG TLS_KEY_FILE TLS_VERSION_MIN TLS13_CIPHERSUITES CLIENT_IPV6 VPN_SUBNET_IPV6 ROUTE_INTERNET CLIENT_TO_CLIENT LOCAL_NETWORKS WAIT_TIMEOUT_SIGNAL WAIT_TIMEOUT_CONNECT WAIT_TIMEOUT_REVOKE' \
|
||||||
'WorkingDirectory=/root' \
|
'WorkingDirectory=/root' \
|
||||||
'ExecStart=/entrypoint.sh' \
|
'ExecStart=/entrypoint.sh' \
|
||||||
'RemainAfterExit=yes' \
|
'RemainAfterExit=yes' \
|
||||||
|
|||||||
@@ -220,13 +220,42 @@ if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 2: Ping VPN gateway (IPv4)
|
# Test 2: Verify pushed routes match the access policy.
|
||||||
echo "Test 2: Pinging VPN gateway (IPv4) ($VPN_GATEWAY)..."
|
echo "Test 2: Checking access policy routes..."
|
||||||
|
if [ "${ROUTE_INTERNET:-y}" = "y" ]; then
|
||||||
|
if ip route show | grep -q '^0.0.0.0/1 .* tun0' && ip route show | grep -q '^128.0.0.0/1 .* tun0'; then
|
||||||
|
echo "PASS: Internet routes use the VPN"
|
||||||
|
else
|
||||||
|
echo "FAIL: VPN internet routes are missing"
|
||||||
|
ip route show
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ip route show | grep -qE '^(0\.0\.0\.0/1|128\.0\.0\.0/1) .* tun0'; then
|
||||||
|
echo "FAIL: Internet route uses the VPN in split-tunnel mode"
|
||||||
|
ip route show
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Internet routes remain outside the VPN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${LOCAL_NETWORKS:-}" ]; then
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
if [[ $local_network == *.* ]] && ! ip route show "$local_network" | grep -q 'tun0'; then
|
||||||
|
echo "FAIL: Local network route is missing for $local_network"
|
||||||
|
ip route show
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done < <(tr ',' '\n' <<<"$LOCAL_NETWORKS")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 3: Ping VPN gateway (IPv4)
|
||||||
|
echo "Test 3: Pinging VPN gateway (IPv4) ($VPN_GATEWAY)..."
|
||||||
wait_for_gateway_ping "VPN gateway (IPv4)"
|
wait_for_gateway_ping "VPN gateway (IPv4)"
|
||||||
|
|
||||||
# Test 2b: Ping VPN gateway (IPv6, if enabled)
|
# Test 3b: Ping VPN gateway (IPv6, if enabled)
|
||||||
if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
||||||
echo "Test 2b: Pinging VPN gateway (IPv6) ($VPN_GATEWAY_IPV6)..."
|
echo "Test 3b: Pinging VPN gateway (IPv6) ($VPN_GATEWAY_IPV6)..."
|
||||||
if ping6 -c 5 "$VPN_GATEWAY_IPV6"; then
|
if ping6 -c 5 "$VPN_GATEWAY_IPV6"; then
|
||||||
echo "PASS: Can ping VPN gateway (IPv6)"
|
echo "PASS: Can ping VPN gateway (IPv6)"
|
||||||
else
|
else
|
||||||
@@ -235,8 +264,12 @@ if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 3: DNS resolution through Unbound
|
# Test 4: DNS resolution through Unbound in full-tunnel mode.
|
||||||
test_dns_resolution "Test 3"
|
if [ "${ROUTE_INTERNET:-y}" = "y" ]; then
|
||||||
|
test_dns_resolution "Test 4"
|
||||||
|
else
|
||||||
|
echo "Test 4: SKIP: VPN DNS is disabled in split-tunnel mode"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Initial connectivity tests PASSED ==="
|
echo "=== Initial connectivity tests PASSED ==="
|
||||||
@@ -269,7 +302,9 @@ sleep 5
|
|||||||
echo "Test: Pinging VPN gateway after renewal ($VPN_GATEWAY)..."
|
echo "Test: Pinging VPN gateway after renewal ($VPN_GATEWAY)..."
|
||||||
wait_for_gateway_ping "VPN gateway after renewal"
|
wait_for_gateway_ping "VPN gateway after renewal"
|
||||||
|
|
||||||
|
if [ "${ROUTE_INTERNET:-y}" = "y" ]; then
|
||||||
test_dns_resolution "Test: Post-renewal DNS"
|
test_dns_resolution "Test: Post-renewal DNS"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Post-renewal connectivity tests PASSED ==="
|
echo "=== Post-renewal connectivity tests PASSED ==="
|
||||||
|
|||||||
+154
-35
@@ -57,10 +57,27 @@ if grep -q $'\033' "$NO_COLOR_OUTPUT"; then
|
|||||||
fi
|
fi
|
||||||
echo "PASS: --no-color help output has no ANSI escape sequences"
|
echo "PASS: --no-color help output has no ANSI escape sequences"
|
||||||
|
|
||||||
|
INVALID_NETWORK_OUTPUT="/tmp/invalid-local-network.log"
|
||||||
|
if /opt/openvpn-install.sh install --local-network 192.168.1.1/24 >"$INVALID_NETWORK_OUTPUT" 2>&1; then
|
||||||
|
echo "FAIL: Host-address CIDR was accepted as a local network"
|
||||||
|
exit 1
|
||||||
|
elif grep -q "Invalid local network" "$INVALID_NETWORK_OUTPUT"; then
|
||||||
|
echo "PASS: Invalid local network CIDR is rejected"
|
||||||
|
else
|
||||||
|
echo "FAIL: Expected local network validation error"
|
||||||
|
cat "$INVALID_NETWORK_OUTPUT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Calculate VPN gateway from subnet (first usable IP)
|
# Calculate VPN gateway from subnet (first usable IP)
|
||||||
VPN_GATEWAY="${VPN_SUBNET_IPV4%.*}.1"
|
VPN_GATEWAY="${VPN_SUBNET_IPV4%.*}.1"
|
||||||
export VPN_GATEWAY
|
export VPN_GATEWAY
|
||||||
|
|
||||||
|
# Access policy configuration
|
||||||
|
ROUTE_INTERNET="${ROUTE_INTERNET:-y}"
|
||||||
|
CLIENT_TO_CLIENT="${CLIENT_TO_CLIENT:-n}"
|
||||||
|
LOCAL_NETWORKS="${LOCAL_NETWORKS:-}"
|
||||||
|
|
||||||
# IPv6 configuration (optional)
|
# IPv6 configuration (optional)
|
||||||
# CLIENT_IPV6: y/n to enable IPv6 for VPN clients
|
# CLIENT_IPV6: y/n to enable IPv6 for VPN clients
|
||||||
# VPN_SUBNET_IPV6: IPv6 subnet (ULA prefix, e.g., fd42:42:42:42::)
|
# VPN_SUBNET_IPV6: IPv6 subnet (ULA prefix, e.g., fd42:42:42:42::)
|
||||||
@@ -95,6 +112,18 @@ INSTALL_CMD+=(--subnet-ipv4 "$VPN_SUBNET_IPV4")
|
|||||||
INSTALL_CMD+=(--mtu 1400)
|
INSTALL_CMD+=(--mtu 1400)
|
||||||
INSTALL_CMD+=(--client testclient)
|
INSTALL_CMD+=(--client testclient)
|
||||||
|
|
||||||
|
if [ "$ROUTE_INTERNET" = "n" ]; then
|
||||||
|
INSTALL_CMD+=(--no-route-internet)
|
||||||
|
fi
|
||||||
|
if [ "$CLIENT_TO_CLIENT" = "y" ]; then
|
||||||
|
INSTALL_CMD+=(--client-to-client)
|
||||||
|
fi
|
||||||
|
if [ -n "$LOCAL_NETWORKS" ]; then
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
INSTALL_CMD+=(--local-network "$local_network")
|
||||||
|
done < <(tr ',' '\n' <<<"$LOCAL_NETWORKS")
|
||||||
|
fi
|
||||||
|
|
||||||
# Add IPv6 client support if enabled
|
# Add IPv6 client support if enabled
|
||||||
if [ "$CLIENT_IPV6" = "y" ]; then
|
if [ "$CLIENT_IPV6" = "y" ]; then
|
||||||
INSTALL_CMD+=(--client-ipv6)
|
INSTALL_CMD+=(--client-ipv6)
|
||||||
@@ -197,6 +226,65 @@ fi
|
|||||||
|
|
||||||
echo "All required files present"
|
echo "All required files present"
|
||||||
|
|
||||||
|
# =====================================================
|
||||||
|
# Verify access policy configuration
|
||||||
|
# =====================================================
|
||||||
|
echo ""
|
||||||
|
echo "=== Verifying Access Policy Configuration ==="
|
||||||
|
|
||||||
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
|
if grep -q '^push "redirect-gateway def1 bypass-dhcp"' /etc/openvpn/server/server.conf; then
|
||||||
|
echo "PASS: Internet default route is pushed"
|
||||||
|
else
|
||||||
|
echo "FAIL: Internet default route is missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if grep -q 'redirect-gateway\|block-ipv6' /etc/openvpn/server/server.conf; then
|
||||||
|
echo "FAIL: Internet or leak-blocking routes exist in split-tunnel mode"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: Client internet routes remain outside the VPN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CLIENT_TO_CLIENT" = "y" ]; then
|
||||||
|
grep -q '^client-to-client$' /etc/openvpn/server/server.conf || {
|
||||||
|
echo "FAIL: client-to-client directive is missing"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if grep -q '^client-to-client$' /etc/openvpn/server/server.conf; then
|
||||||
|
echo "FAIL: client-to-client is enabled by default"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$LOCAL_NETWORKS" ]; then
|
||||||
|
while IFS= read -r local_network; do
|
||||||
|
if [[ $local_network == *.* ]]; then
|
||||||
|
local_address="${local_network%/*}"
|
||||||
|
grep -q "^push \"route $local_address " /etc/openvpn/server/server.conf || {
|
||||||
|
echo "FAIL: Local IPv4 route for $local_network is missing"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
grep -q "^push \"route-ipv6 $local_network\"" /etc/openvpn/server/server.conf || {
|
||||||
|
echo "FAIL: Local IPv6 route for $local_network is missing"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
done < <(tr ',' '\n' <<<"$LOCAL_NETWORKS")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for setting in "ROUTE_INTERNET=$ROUTE_INTERNET" "CLIENT_TO_CLIENT=$CLIENT_TO_CLIENT" "LOCAL_NETWORKS=$LOCAL_NETWORKS"; do
|
||||||
|
grep -Fxq "$setting" /etc/openvpn/server/openvpn-install.conf || {
|
||||||
|
echo "FAIL: Policy manifest is missing $setting"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "PASS: Access policy configuration is correct"
|
||||||
|
|
||||||
# =====================================================
|
# =====================================================
|
||||||
# Verify management interface configuration
|
# Verify management interface configuration
|
||||||
# =====================================================
|
# =====================================================
|
||||||
@@ -264,6 +352,9 @@ echo "Client config copied to /shared/client.ovpn"
|
|||||||
echo "VPN_GATEWAY=$VPN_GATEWAY"
|
echo "VPN_GATEWAY=$VPN_GATEWAY"
|
||||||
echo "CLIENT_IPV6=$CLIENT_IPV6"
|
echo "CLIENT_IPV6=$CLIENT_IPV6"
|
||||||
echo "AUTH_MODE=$AUTH_MODE"
|
echo "AUTH_MODE=$AUTH_MODE"
|
||||||
|
echo "ROUTE_INTERNET=$ROUTE_INTERNET"
|
||||||
|
echo "CLIENT_TO_CLIENT=$CLIENT_TO_CLIENT"
|
||||||
|
echo "LOCAL_NETWORKS=$LOCAL_NETWORKS"
|
||||||
if [ "$CLIENT_IPV6" = "y" ]; then
|
if [ "$CLIENT_IPV6" = "y" ]; then
|
||||||
echo "VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
echo "VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
||||||
echo "VPN_GATEWAY_IPV6=$VPN_GATEWAY_IPV6"
|
echo "VPN_GATEWAY_IPV6=$VPN_GATEWAY_IPV6"
|
||||||
@@ -599,6 +690,7 @@ echo "Post-renewal client tests passed"
|
|||||||
# =====================================================
|
# =====================================================
|
||||||
# Verify Unbound DNS resolver (started by systemd via install script)
|
# Verify Unbound DNS resolver (started by systemd via install script)
|
||||||
# =====================================================
|
# =====================================================
|
||||||
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
echo "=== Verifying Unbound DNS Resolver ==="
|
echo "=== Verifying Unbound DNS Resolver ==="
|
||||||
|
|
||||||
if [ -f /etc/unbound/unbound.conf ]; then
|
if [ -f /etc/unbound/unbound.conf ]; then
|
||||||
@@ -657,6 +749,13 @@ fi
|
|||||||
|
|
||||||
echo "=== Unbound Installation Verified ==="
|
echo "=== Unbound Installation Verified ==="
|
||||||
echo ""
|
echo ""
|
||||||
|
else
|
||||||
|
if grep -q '^push "dhcp-option DNS ' /etc/openvpn/server/server.conf; then
|
||||||
|
echo "FAIL: DNS is pushed while internet routing is disabled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "PASS: VPN DNS setup is skipped in split-tunnel mode"
|
||||||
|
fi
|
||||||
|
|
||||||
# Verify OpenVPN server (started by systemd via install script)
|
# Verify OpenVPN server (started by systemd via install script)
|
||||||
echo "Verifying OpenVPN server..."
|
echo "Verifying OpenVPN server..."
|
||||||
@@ -664,19 +763,18 @@ echo "Verifying OpenVPN server..."
|
|||||||
# Verify firewall rules exist
|
# Verify firewall rules exist
|
||||||
echo "Verifying firewall rules..."
|
echo "Verifying firewall rules..."
|
||||||
if systemctl is-active --quiet firewalld; then
|
if systemctl is-active --quiet firewalld; then
|
||||||
# firewalld is active - verify masquerade is enabled
|
echo "firewalld detected, checking scoped policy rules..."
|
||||||
echo "firewalld detected, checking masquerade..."
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
for _ in $(seq 1 10); do
|
if firewall-cmd --list-rich-rules | grep -q "source address=\"$VPN_SUBNET_IPV4/24\" masquerade"; then
|
||||||
if firewall-cmd --query-masquerade 2>/dev/null; then
|
echo "PASS: firewalld has source-scoped internet NAT"
|
||||||
echo "PASS: firewalld masquerade is enabled"
|
else
|
||||||
break
|
echo "FAIL: firewalld source-scoped internet NAT is missing"
|
||||||
|
firewall-cmd --list-rich-rules
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
sleep 1
|
fi
|
||||||
done
|
if firewall-cmd --query-masquerade 2>/dev/null; then
|
||||||
if ! firewall-cmd --query-masquerade 2>/dev/null; then
|
echo "FAIL: firewalld zone-wide masquerade should not be enabled"
|
||||||
echo "FAIL: firewalld masquerade is not enabled"
|
|
||||||
echo "Current firewalld config:"
|
|
||||||
firewall-cmd --list-all 2>&1 || true
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Verify port is open
|
# Verify port is open
|
||||||
@@ -687,15 +785,19 @@ if systemctl is-active --quiet firewalld; then
|
|||||||
firewall-cmd --list-ports
|
firewall-cmd --list-ports
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Verify VPN subnet rich rule exists (source-based rules work reliably across firewalld backends)
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
if firewall-cmd --list-rich-rules | grep -q "source address=\"$VPN_SUBNET_IPV4/24\""; then
|
# Private destinations, including the VPN pool, stay isolated unless explicitly allowed.
|
||||||
echo "PASS: VPN subnet rich rule is configured"
|
if firewall-cmd --list-rich-rules | grep -q "destination address=\"10.0.0.0/8\" reject"; then
|
||||||
|
echo "PASS: firewalld private-network isolation is configured"
|
||||||
else
|
else
|
||||||
echo "FAIL: VPN subnet rich rule not found in firewalld"
|
echo "FAIL: firewalld private-network isolation is missing"
|
||||||
echo "Current rich rules:"
|
|
||||||
firewall-cmd --list-rich-rules
|
firewall-cmd --list-rich-rules
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
elif ! firewall-cmd --list-rich-rules | grep -q "source address=\"$VPN_SUBNET_IPV4/24\" reject"; then
|
||||||
|
echo "FAIL: firewalld split-tunnel default reject is missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
elif systemctl is-active --quiet nftables; then
|
elif systemctl is-active --quiet nftables; then
|
||||||
# nftables mode - verify OpenVPN tables exist
|
# nftables mode - verify OpenVPN tables exist
|
||||||
echo "nftables detected, checking OpenVPN tables..."
|
echo "nftables detected, checking OpenVPN tables..."
|
||||||
@@ -712,20 +814,25 @@ elif systemctl is-active --quiet nftables; then
|
|||||||
nft list ruleset 2>&1 || true
|
nft list ruleset 2>&1 || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Verify NAT table exists
|
if [ "$ROUTE_INTERNET" = "y" ] || [ -n "$LOCAL_NETWORKS" ]; then
|
||||||
if nft list table ip openvpn-nat >/dev/null 2>&1; then
|
if nft list table ip openvpn-nat >/dev/null 2>&1 && nft list table ip openvpn-nat | grep -q "masquerade"; then
|
||||||
echo "PASS: nftables 'ip openvpn-nat' table exists"
|
echo "PASS: nftables scoped NAT is configured"
|
||||||
else
|
else
|
||||||
echo "FAIL: nftables 'ip openvpn-nat' table not found"
|
echo "FAIL: nftables scoped NAT is missing"
|
||||||
nft list ruleset 2>&1 || true
|
nft list ruleset 2>&1 || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Verify masquerade rule exists
|
fi
|
||||||
if nft list table ip openvpn-nat | grep -q "masquerade"; then
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
echo "PASS: nftables masquerade rule exists"
|
if nft list table inet openvpn | grep -q "ip daddr 10.0.0.0/8 drop"; then
|
||||||
|
echo "PASS: nftables private-network isolation is configured"
|
||||||
else
|
else
|
||||||
echo "FAIL: nftables masquerade rule not found"
|
echo "FAIL: nftables private-network isolation is missing"
|
||||||
nft list table ip openvpn-nat 2>&1 || true
|
nft list table inet openvpn
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif ! nft list table inet openvpn | grep -q "ip saddr $VPN_SUBNET_IPV4/24 drop"; then
|
||||||
|
echo "FAIL: nftables split-tunnel default drop is missing"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Verify include in nftables.conf
|
# Verify include in nftables.conf
|
||||||
@@ -737,20 +844,32 @@ elif systemctl is-active --quiet nftables; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# iptables mode - verify NAT rules
|
echo "iptables mode, checking policy rules..."
|
||||||
echo "iptables mode, checking NAT rules..."
|
if [ "$ROUTE_INTERNET" = "y" ] || [ -n "$LOCAL_NETWORKS" ]; then
|
||||||
for _ in $(seq 1 10); do
|
for _ in $(seq 1 10); do
|
||||||
if iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4"; then
|
iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4" && break
|
||||||
echo "PASS: NAT POSTROUTING rule for $VPN_SUBNET_IPV4/24 exists"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
if ! iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4"; then
|
if ! iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4"; then
|
||||||
echo "FAIL: NAT POSTROUTING rule for $VPN_SUBNET_IPV4/24 not found"
|
echo "FAIL: Expected scoped NAT rule was not found"
|
||||||
echo "Current NAT rules:"
|
|
||||||
iptables -t nat -L POSTROUTING -n -v
|
iptables -t nat -L POSTROUTING -n -v
|
||||||
systemctl status iptables-openvpn 2>&1 || true
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||||
|
if iptables -S OPENVPN_INSTALL_FORWARD | grep -q -- "-d 10.0.0.0/8 -j REJECT"; then
|
||||||
|
echo "PASS: iptables private-network isolation is configured"
|
||||||
|
else
|
||||||
|
echo "FAIL: iptables private-network isolation is missing"
|
||||||
|
iptables -S OPENVPN_INSTALL_FORWARD
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif ! iptables -S OPENVPN_INSTALL_FORWARD | grep -q -- "-A OPENVPN_INSTALL_FORWARD -j REJECT"; then
|
||||||
|
echo "FAIL: iptables split-tunnel default reject is missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ "$CLIENT_TO_CLIENT" = "y" ] && ! iptables -S OPENVPN_INSTALL_FORWARD | grep -q -- "-d $VPN_SUBNET_IPV4/24 -j ACCEPT"; then
|
||||||
|
echo "FAIL: iptables client-to-client allow rule is missing"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user