mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-16 00:47:02 +01:00
Refactor Unbound setup and add E2E tests (#1340)
Refactor Unbound DNS installation to use modern `conf.d` pattern and add E2E testing. **Changes:** - Unified Unbound config across all distros using `/etc/unbound/unbound.conf.d/openvpn.conf` - Added startup validation with retry logic - Added `ip-freebind` to allow binding before tun interface exists - E2E tests now verify Unbound DNS resolution from VPN clients **Testing:** - Server: verifies config creation, interface binding, security options - Client: verifies DNS resolution through Unbound (10.8.0.1) --- Closes https://github.com/angristan/openvpn-install/issues/602 Closes https://github.com/angristan/openvpn-install/pull/604 Closes https://github.com/angristan/openvpn-install/issues/1189 Co-authored-by: Henry N <henrynmail-github@yahoo.de>
This commit is contained in:
@@ -5,11 +5,13 @@ FROM ubuntu:24.04
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install OpenVPN client and testing tools
|
||||
# dnsutils provides dig for DNS testing with Unbound
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
openvpn \
|
||||
iproute2 \
|
||||
iputils-ping \
|
||||
procps \
|
||||
dnsutils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create TUN device directory (device will be mounted at runtime)
|
||||
|
||||
@@ -8,21 +8,22 @@ ARG BASE_IMAGE
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install basic dependencies based on the OS
|
||||
# dnsutils/bind-utils provides dig for DNS testing with Unbound
|
||||
RUN if command -v apt-get >/dev/null; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
iproute2 iptables curl procps systemd systemd-sysv \
|
||||
iproute2 iptables curl procps systemd systemd-sysv dnsutils \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
elif command -v dnf >/dev/null; then \
|
||||
dnf install -y --allowerasing \
|
||||
iproute iptables curl procps-ng systemd tar gzip \
|
||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||
&& dnf clean all; \
|
||||
elif command -v yum >/dev/null; then \
|
||||
yum install -y \
|
||||
iproute iptables curl procps-ng systemd tar gzip \
|
||||
iproute iptables curl procps-ng systemd tar gzip bind-utils \
|
||||
&& yum clean all; \
|
||||
elif command -v pacman >/dev/null; then \
|
||||
pacman -Syu --noconfirm \
|
||||
iproute2 iptables curl procps-ng \
|
||||
iproute2 iptables curl procps-ng bind \
|
||||
&& pacman -Scc --noconfirm; \
|
||||
fi
|
||||
|
||||
|
||||
@@ -81,6 +81,28 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: DNS resolution through Unbound
|
||||
echo "Test 3: Testing DNS resolution via Unbound (10.8.0.1)..."
|
||||
DNS_SUCCESS=false
|
||||
for i in 1 2 3 4 5; do
|
||||
DIG_OUTPUT=$(dig @10.8.0.1 example.com +short +time=5 2>&1)
|
||||
if [ -n "$DIG_OUTPUT" ] && ! echo "$DIG_OUTPUT" | grep -qi "timed out\|SERVFAIL\|connection refused"; then
|
||||
DNS_SUCCESS=true
|
||||
break
|
||||
fi
|
||||
echo "DNS attempt $i failed:"
|
||||
echo "$DIG_OUTPUT"
|
||||
sleep 2
|
||||
done
|
||||
if [ "$DNS_SUCCESS" = true ]; then
|
||||
echo "PASS: DNS resolution through Unbound works"
|
||||
echo "Resolved example.com to: $(dig @10.8.0.1 example.com +short +time=5)"
|
||||
else
|
||||
echo "FAIL: DNS resolution through Unbound failed after 5 attempts"
|
||||
dig @10.8.0.1 example.com +time=5 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " ALL TESTS PASSED!"
|
||||
|
||||
@@ -20,7 +20,7 @@ export APPROVE_IP=y
|
||||
export IPV6_SUPPORT=n
|
||||
export PORT_CHOICE=1
|
||||
export PROTOCOL_CHOICE=1
|
||||
export DNS=9 # Google DNS (works in containers)
|
||||
export DNS=2 # Self-hosted Unbound DNS resolver
|
||||
export COMPRESSION_ENABLED=n
|
||||
export CUSTOMIZE_ENC=n
|
||||
export CLIENT=testclient
|
||||
@@ -29,8 +29,11 @@ export ENDPOINT=openvpn-server
|
||||
|
||||
# Prepare script for container environment:
|
||||
# - Replace systemctl calls with no-ops (systemd doesn't work in containers)
|
||||
# - Skip Unbound startup validation (we start Unbound manually later)
|
||||
# This ensures the script won't fail silently on systemctl commands
|
||||
sed 's/\bsystemctl /echo "[SKIPPED] systemctl " # /g' /opt/openvpn-install.sh >/tmp/openvpn-install.sh
|
||||
sed -e 's/\bsystemctl /echo "[SKIPPED] systemctl " # /g' \
|
||||
-e 's/log_fatal "Unbound failed to start/return 0 # [SKIPPED] /g' \
|
||||
/opt/openvpn-install.sh >/tmp/openvpn-install.sh
|
||||
chmod +x /tmp/openvpn-install.sh
|
||||
|
||||
echo "Running OpenVPN install script..."
|
||||
@@ -243,6 +246,80 @@ echo ""
|
||||
echo "=== All Certificate Renewal Tests PASSED ==="
|
||||
echo ""
|
||||
|
||||
# =====================================================
|
||||
# Start and verify Unbound DNS resolver
|
||||
# =====================================================
|
||||
echo "=== Starting Unbound DNS Resolver ==="
|
||||
|
||||
# Start Unbound manually (systemctl commands are no-ops in container)
|
||||
if [ -f /etc/unbound/unbound.conf ]; then
|
||||
echo "Starting Unbound DNS resolver..."
|
||||
|
||||
# Create root key for DNSSEC if it doesn't exist
|
||||
# Normally, unbound.service's ExecStartPre copies /usr/share/dns/root.key to /var/lib/unbound/root.key
|
||||
# In Docker, policy-rc.d blocks service starts during apt install, so this never happens
|
||||
if [ ! -f /var/lib/unbound/root.key ] && [ -f /usr/share/dns/root.key ]; then
|
||||
mkdir -p /var/lib/unbound
|
||||
cp /usr/share/dns/root.key /var/lib/unbound/root.key
|
||||
chown -R unbound:unbound /var/lib/unbound 2>/dev/null || true
|
||||
fi
|
||||
|
||||
unbound
|
||||
# Poll up to 10 seconds for Unbound to start
|
||||
for _ in $(seq 1 10); do
|
||||
if pgrep -x unbound >/dev/null; then
|
||||
echo "PASS: Unbound is running"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if ! pgrep -x unbound >/dev/null; then
|
||||
echo "FAIL: Unbound failed to start"
|
||||
# Show debug info
|
||||
unbound-checkconf /etc/unbound/unbound.conf 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "FAIL: /etc/unbound/unbound.conf not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Verifying Unbound Installation ==="
|
||||
|
||||
# Verify Unbound config exists in conf.d directory
|
||||
UNBOUND_OPENVPN_CONF="/etc/unbound/unbound.conf.d/openvpn.conf"
|
||||
if [ -f "$UNBOUND_OPENVPN_CONF" ]; then
|
||||
echo "PASS: Found Unbound config at $UNBOUND_OPENVPN_CONF"
|
||||
else
|
||||
echo "FAIL: OpenVPN Unbound config not found at $UNBOUND_OPENVPN_CONF"
|
||||
echo "Contents of /etc/unbound/:"
|
||||
ls -la /etc/unbound/
|
||||
ls -la /etc/unbound/unbound.conf.d/ 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify Unbound listens on VPN gateway
|
||||
if grep -q "interface: 10.8.0.1" "$UNBOUND_OPENVPN_CONF"; then
|
||||
echo "PASS: Unbound configured to listen on 10.8.0.1"
|
||||
else
|
||||
echo "FAIL: Unbound not configured for 10.8.0.1"
|
||||
cat "$UNBOUND_OPENVPN_CONF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify OpenVPN pushes correct DNS
|
||||
if grep -q 'push "dhcp-option DNS 10.8.0.1"' /etc/openvpn/server.conf; then
|
||||
echo "PASS: OpenVPN configured to push Unbound DNS"
|
||||
else
|
||||
echo "FAIL: OpenVPN not configured to push Unbound DNS"
|
||||
grep "dhcp-option DNS" /etc/openvpn/server.conf || echo "No DNS push found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Unbound Installation Verified ==="
|
||||
echo ""
|
||||
|
||||
# Start OpenVPN server manually (systemd doesn't work in containers)
|
||||
echo "Starting OpenVPN server..."
|
||||
|
||||
|
||||
Reference in New Issue
Block a user