mirror of
https://github.com/angristan/openvpn-install.git
synced 2026-08-02 20:58:13 +02:00
Add configurable VPN access policies
This commit is contained in:
@@ -129,6 +129,17 @@ jobs:
|
||||
name: tls-crypt-v2
|
||||
sig: crypt-v2
|
||||
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 }}
|
||||
steps:
|
||||
@@ -178,6 +189,9 @@ jobs:
|
||||
-e TLS_KEY_FILE=${{ matrix.tls.key_file }} \
|
||||
-e CLIENT_IPV6=${{ matrix.os.client_ipv6 && 'y' || 'n' }} \
|
||||
-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
|
||||
|
||||
- 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?
|
||||
|
||||
**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`
|
||||
|
||||
@@ -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?
|
||||
|
||||
**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?
|
||||
|
||||
**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`:
|
||||
```bash
|
||||
./openvpn-install.sh install --local-network 192.168.1.0/24
|
||||
```
|
||||
|
||||
```
|
||||
push "route 192.168.1.0 255.255.255.0"
|
||||
```
|
||||
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.
|
||||
|
||||
Replace `192.168.1.0/24` with your actual LAN subnet.
|
||||
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.
|
||||
|
||||
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
|
||||
# iptables
|
||||
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.
|
||||
|
||||
Restart OpenVPN after making changes: `systemctl restart openvpn-server@server`
|
||||
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
|
||||
route-nopull
|
||||
route 10.0.0.0 255.0.0.0
|
||||
```bash
|
||||
./openvpn-install.sh install \
|
||||
--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 \
|
||||
--local-network 10.0.0.0/8 \
|
||||
--local-network 192.168.1.0/24
|
||||
```
|
||||
|
||||
```
|
||||
#push "redirect-gateway def1 bypass-dhcp"
|
||||
```
|
||||
|
||||
2. Add routes for the networks you want to tunnel:
|
||||
|
||||
```
|
||||
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`
|
||||
The installer does not push internet routes, leak-blocking directives, or VPN DNS in this mode.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
graph LR
|
||||
@@ -44,6 +44,7 @@ That said, OpenVPN still makes sense when you need:
|
||||
- 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
|
||||
- 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 tunnel MTU (default: `1500`)
|
||||
- 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
|
||||
./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
|
||||
./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
|
||||
- `--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::`)
|
||||
- `--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-random` - Use random port (49152-65535)
|
||||
- `--protocol <udp|tcp>` - Protocol (default: `udp`)
|
||||
- `--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 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-primary <ip>` - Custom primary DNS (requires `--dns custom`)
|
||||
- `--dns-secondary <ip>` - Custom secondary DNS (requires `--dns custom`)
|
||||
|
||||
@@ -14,6 +14,10 @@ services:
|
||||
cgroupns: host
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
ROUTE_INTERNET: ${ROUTE_INTERNET:-y}
|
||||
CLIENT_TO_CLIENT: ${CLIENT_TO_CLIENT:-n}
|
||||
LOCAL_NETWORKS: ${LOCAL_NETWORKS:-}
|
||||
sysctls:
|
||||
- net.ipv4.ip_forward=1
|
||||
volumes:
|
||||
|
||||
+677
-154
File diff suppressed because it is too large
Load Diff
@@ -80,7 +80,7 @@ RUN printf '%s\n' \
|
||||
'[Service]' \
|
||||
'Type=oneshot' \
|
||||
'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' \
|
||||
'ExecStart=/entrypoint.sh' \
|
||||
'RemainAfterExit=yes' \
|
||||
|
||||
@@ -220,13 +220,42 @@ if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test 2: Ping VPN gateway (IPv4)
|
||||
echo "Test 2: Pinging VPN gateway (IPv4) ($VPN_GATEWAY)..."
|
||||
# Test 2: Verify pushed routes match the access policy.
|
||||
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)"
|
||||
|
||||
# Test 2b: Ping VPN gateway (IPv6, if enabled)
|
||||
# Test 3b: Ping VPN gateway (IPv6, if enabled)
|
||||
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
|
||||
echo "PASS: Can ping VPN gateway (IPv6)"
|
||||
else
|
||||
@@ -235,8 +264,12 @@ if [ "${CLIENT_IPV6:-n}" = "y" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test 3: DNS resolution through Unbound
|
||||
test_dns_resolution "Test 3"
|
||||
# Test 4: DNS resolution through Unbound in full-tunnel mode.
|
||||
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 "=== Initial connectivity tests PASSED ==="
|
||||
@@ -269,7 +302,9 @@ sleep 5
|
||||
echo "Test: Pinging VPN gateway after renewal ($VPN_GATEWAY)..."
|
||||
wait_for_gateway_ping "VPN gateway after renewal"
|
||||
|
||||
test_dns_resolution "Test: Post-renewal DNS"
|
||||
if [ "${ROUTE_INTERNET:-y}" = "y" ]; then
|
||||
test_dns_resolution "Test: Post-renewal DNS"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Post-renewal connectivity tests PASSED ==="
|
||||
|
||||
+167
-48
@@ -57,10 +57,27 @@ if grep -q $'\033' "$NO_COLOR_OUTPUT"; then
|
||||
fi
|
||||
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)
|
||||
VPN_GATEWAY="${VPN_SUBNET_IPV4%.*}.1"
|
||||
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)
|
||||
# CLIENT_IPV6: y/n to enable IPv6 for VPN clients
|
||||
# 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+=(--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
|
||||
if [ "$CLIENT_IPV6" = "y" ]; then
|
||||
INSTALL_CMD+=(--client-ipv6)
|
||||
@@ -197,6 +226,65 @@ fi
|
||||
|
||||
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
|
||||
# =====================================================
|
||||
@@ -264,6 +352,9 @@ echo "Client config copied to /shared/client.ovpn"
|
||||
echo "VPN_GATEWAY=$VPN_GATEWAY"
|
||||
echo "CLIENT_IPV6=$CLIENT_IPV6"
|
||||
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
|
||||
echo "VPN_SUBNET_IPV6=$VPN_SUBNET_IPV6"
|
||||
echo "VPN_GATEWAY_IPV6=$VPN_GATEWAY_IPV6"
|
||||
@@ -599,7 +690,8 @@ echo "Post-renewal client tests passed"
|
||||
# =====================================================
|
||||
# Verify Unbound DNS resolver (started by systemd via install script)
|
||||
# =====================================================
|
||||
echo "=== Verifying Unbound DNS Resolver ==="
|
||||
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||
echo "=== Verifying Unbound DNS Resolver ==="
|
||||
|
||||
if [ -f /etc/unbound/unbound.conf ]; then
|
||||
# Verify Unbound is running (started by systemctl in install script)
|
||||
@@ -655,8 +747,15 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Unbound Installation Verified ==="
|
||||
echo ""
|
||||
echo "=== Unbound Installation Verified ==="
|
||||
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)
|
||||
echo "Verifying OpenVPN server..."
|
||||
@@ -664,19 +763,18 @@ echo "Verifying OpenVPN server..."
|
||||
# Verify firewall rules exist
|
||||
echo "Verifying firewall rules..."
|
||||
if systemctl is-active --quiet firewalld; then
|
||||
# firewalld is active - verify masquerade is enabled
|
||||
echo "firewalld detected, checking masquerade..."
|
||||
for _ in $(seq 1 10); do
|
||||
if firewall-cmd --query-masquerade 2>/dev/null; then
|
||||
echo "PASS: firewalld masquerade is enabled"
|
||||
break
|
||||
echo "firewalld detected, checking scoped policy rules..."
|
||||
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||
if firewall-cmd --list-rich-rules | grep -q "source address=\"$VPN_SUBNET_IPV4/24\" masquerade"; then
|
||||
echo "PASS: firewalld has source-scoped internet NAT"
|
||||
else
|
||||
echo "FAIL: firewalld source-scoped internet NAT is missing"
|
||||
firewall-cmd --list-rich-rules
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if ! firewall-cmd --query-masquerade 2>/dev/null; then
|
||||
echo "FAIL: firewalld masquerade is not enabled"
|
||||
echo "Current firewalld config:"
|
||||
firewall-cmd --list-all 2>&1 || true
|
||||
fi
|
||||
if firewall-cmd --query-masquerade 2>/dev/null; then
|
||||
echo "FAIL: firewalld zone-wide masquerade should not be enabled"
|
||||
exit 1
|
||||
fi
|
||||
# Verify port is open
|
||||
@@ -687,13 +785,17 @@ if systemctl is-active --quiet firewalld; then
|
||||
firewall-cmd --list-ports
|
||||
exit 1
|
||||
fi
|
||||
# Verify VPN subnet rich rule exists (source-based rules work reliably across firewalld backends)
|
||||
if firewall-cmd --list-rich-rules | grep -q "source address=\"$VPN_SUBNET_IPV4/24\""; then
|
||||
echo "PASS: VPN subnet rich rule is configured"
|
||||
else
|
||||
echo "FAIL: VPN subnet rich rule not found in firewalld"
|
||||
echo "Current rich rules:"
|
||||
firewall-cmd --list-rich-rules
|
||||
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||
# Private destinations, including the VPN pool, stay isolated unless explicitly allowed.
|
||||
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
|
||||
echo "FAIL: firewalld private-network isolation is missing"
|
||||
firewall-cmd --list-rich-rules
|
||||
exit 1
|
||||
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
|
||||
@@ -712,20 +814,25 @@ elif systemctl is-active --quiet nftables; then
|
||||
nft list ruleset 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
# Verify NAT table exists
|
||||
if nft list table ip openvpn-nat >/dev/null 2>&1; then
|
||||
echo "PASS: nftables 'ip openvpn-nat' table exists"
|
||||
else
|
||||
echo "FAIL: nftables 'ip openvpn-nat' table not found"
|
||||
nft list ruleset 2>&1 || true
|
||||
exit 1
|
||||
if [ "$ROUTE_INTERNET" = "y" ] || [ -n "$LOCAL_NETWORKS" ]; 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 scoped NAT is configured"
|
||||
else
|
||||
echo "FAIL: nftables scoped NAT is missing"
|
||||
nft list ruleset 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# Verify masquerade rule exists
|
||||
if nft list table ip openvpn-nat | grep -q "masquerade"; then
|
||||
echo "PASS: nftables masquerade rule exists"
|
||||
else
|
||||
echo "FAIL: nftables masquerade rule not found"
|
||||
nft list table ip openvpn-nat 2>&1 || true
|
||||
if [ "$ROUTE_INTERNET" = "y" ]; then
|
||||
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
|
||||
echo "FAIL: nftables private-network isolation is missing"
|
||||
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
|
||||
fi
|
||||
# Verify include in nftables.conf
|
||||
@@ -737,20 +844,32 @@ elif systemctl is-active --quiet nftables; then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# iptables mode - verify NAT rules
|
||||
echo "iptables mode, checking NAT rules..."
|
||||
for _ in $(seq 1 10); do
|
||||
if iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4"; then
|
||||
echo "PASS: NAT POSTROUTING rule for $VPN_SUBNET_IPV4/24 exists"
|
||||
break
|
||||
echo "iptables mode, checking policy rules..."
|
||||
if [ "$ROUTE_INTERNET" = "y" ] || [ -n "$LOCAL_NETWORKS" ]; then
|
||||
for _ in $(seq 1 10); do
|
||||
iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4" && break
|
||||
sleep 1
|
||||
done
|
||||
if ! iptables -t nat -L POSTROUTING -n | grep -q "$VPN_SUBNET_IPV4"; then
|
||||
echo "FAIL: Expected scoped NAT rule was not found"
|
||||
iptables -t nat -L POSTROUTING -n -v
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
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 "Current NAT rules:"
|
||||
iptables -t nat -L POSTROUTING -n -v
|
||||
systemctl status iptables-openvpn 2>&1 || true
|
||||
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
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user