From a3389c126c7daeaf05306f23949ec7be33f5a58d Mon Sep 17 00:00:00 2001 From: Stanislas Date: Sun, 7 Dec 2025 12:27:41 +0100 Subject: [PATCH] Add Docker-based E2E testing (#1320) ### Summary - Add automated end-to-end testing using Docker to verify the installation script works across 18 Linux distributions - Add Oracle Linux 9 support to the installation script - Drop support for EOL distributions (Debian 8/9/10, CentOS 7, Ubuntu 16.04) - Disable Digital Ocean droplets based end-to-end tests, let's use docker from now on ### Changes **New test infrastructure:** - `test/Dockerfile.server` - Multi-OS server image with `BASE_IMAGE` build arg - `test/Dockerfile.client` - Ubuntu 24.04 client for connectivity testing - `test/server-entrypoint.sh` - Runs install script, verifies files exist, asserts iptables NAT rules, starts OpenVPN - `test/client-entrypoint.sh` - Connects to VPN, verifies tun0 interface, pings gateway - `docker-compose.yml` - Orchestrates server + client with shared volume - `.github/workflows/docker-test.yml` - CI matrix testing 18 OS variants - `.github/workflows/test.yml` - Removed push/PR triggers, now manual only for DO tests - `Makefile` - Local testing commands (`make test`, `make test-ubuntu-24.04`, etc.) **Distributions tested (18 total):** | Family | Versions | |--------|----------| | Ubuntu | 18.04, 20.04, 22.04, 24.04 | | Debian | 11, 12 | | Fedora | 40, 41 | | Rocky Linux | 8, 9 | | AlmaLinux | 8, 9 | | Oracle Linux | 8, 9 | | Amazon Linux | 2, 2023 | | CentOS Stream | 9 | | Arch Linux | latest | --- .github/workflows/docker-test.yml | 166 ++++++++++++++++++++++++++++++ .github/workflows/test.yml | 7 +- Makefile | 136 ++++++++++++++++++++++++ README.md | 27 +++-- docker-compose.yml | 55 ++++++++++ openvpn-install.sh | 45 ++++---- test/Dockerfile.client | 24 +++++ test/Dockerfile.server | 42 ++++++++ test/client-entrypoint.sh | 90 ++++++++++++++++ test/server-entrypoint.sh | 109 ++++++++++++++++++++ 10 files changed, 655 insertions(+), 46 deletions(-) create mode 100644 .github/workflows/docker-test.yml create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100644 test/Dockerfile.client create mode 100644 test/Dockerfile.server create mode 100755 test/client-entrypoint.sh create mode 100755 test/server-entrypoint.sh diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml new file mode 100644 index 0000000..7a16cf1 --- /dev/null +++ b/.github/workflows/docker-test.yml @@ -0,0 +1,166 @@ +--- +on: + push: + workflow_dispatch: + +name: Docker Test + +permissions: + contents: read + +jobs: + docker-test: + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + os: + - name: ubuntu-18.04 + image: ubuntu:18.04 + - name: ubuntu-20.04 + image: ubuntu:20.04 + - name: ubuntu-22.04 + image: ubuntu:22.04 + - name: ubuntu-24.04 + image: ubuntu:24.04 + - name: debian-11 + image: debian:11 + - name: debian-12 + image: debian:12 + - name: centos-stream-9 + image: quay.io/centos/centos:stream9 + - name: fedora-40 + image: fedora:40 + - name: fedora-41 + image: fedora:41 + - name: rocky-8 + image: rockylinux:8 + - name: rocky-9 + image: rockylinux:9 + - name: almalinux-8 + image: almalinux:8 + - name: almalinux-9 + image: almalinux:9 + - name: archlinux + image: archlinux:latest + - name: oraclelinux-8 + image: oraclelinux:8 + - name: oraclelinux-9 + image: oraclelinux:9 + - name: amazonlinux-2 + image: amazonlinux:2 + - name: amazonlinux-2023 + image: amazonlinux:2023 + + name: ${{ matrix.os.name }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build server image + run: | + docker build \ + --build-arg BASE_IMAGE=${{ matrix.os.image }} \ + -t openvpn-server \ + -f test/Dockerfile.server . + + - name: Build client image + run: docker build -t openvpn-client -f test/Dockerfile.client . + + - name: Create Docker network + run: docker network create --subnet=172.28.0.0/24 vpn-test + + - name: Create shared volume + run: docker volume create shared-config + + - name: Start OpenVPN server + run: | + docker run -d \ + --name openvpn-server \ + --hostname openvpn-server \ + --cap-add=NET_ADMIN \ + --device=/dev/net/tun:/dev/net/tun \ + --sysctl net.ipv4.ip_forward=1 \ + --network vpn-test \ + --ip 172.28.0.10 \ + -v shared-config:/shared \ + openvpn-server + + - name: Wait for server installation and startup + run: | + echo "Waiting for OpenVPN server to install and start..." + for i in {1..60}; do + if docker exec openvpn-server pgrep openvpn > /dev/null 2>&1; then + echo "OpenVPN server is running!" + break + fi + echo "Waiting... ($i/60)" + sleep 5 + # Show logs for debugging + docker logs --tail 20 openvpn-server 2>&1 || true + done + + # Final check + if ! docker exec openvpn-server pgrep openvpn > /dev/null 2>&1; then + echo "ERROR: OpenVPN server failed to start" + docker logs openvpn-server + exit 1 + fi + + - name: Verify client config was generated + run: | + docker run --rm -v shared-config:/shared alpine \ + ls -la /shared/ + docker run --rm -v shared-config:/shared alpine \ + cat /shared/client.ovpn + + - name: Start OpenVPN client and run tests + run: | + docker run \ + --name openvpn-client \ + --hostname openvpn-client \ + --cap-add=NET_ADMIN \ + --device=/dev/net/tun:/dev/net/tun \ + --network vpn-test \ + --ip 172.28.0.20 \ + -v shared-config:/shared:ro \ + openvpn-client & + + # Wait for tests to complete (look for success message) + for i in {1..60}; do + if docker logs openvpn-client 2>&1 | grep -q "ALL TESTS PASSED" + then + echo "Tests passed!" + exit 0 + fi + if docker logs openvpn-client 2>&1 | grep -q "FAIL:"; then + echo "Tests failed!" + docker logs openvpn-client + exit 1 + fi + echo "Waiting for tests... ($i/60)" + sleep 2 + done + + echo "Timeout waiting for tests" + docker logs openvpn-client + exit 1 + + - name: Show server logs + if: always() + run: docker logs openvpn-server 2>&1 || true + + - name: Show client logs + if: always() + run: docker logs openvpn-client 2>&1 || true + + - name: Cleanup + if: always() + run: | + docker stop openvpn-server openvpn-client 2>/dev/null || true + docker rm openvpn-server openvpn-client 2>/dev/null || true + docker network rm vpn-test 2>/dev/null || true + docker volume rm shared-config 2>/dev/null || true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e823678..5d609f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,7 @@ +# DigitalOcean E2E tests (manual trigger only) +# Primary CI testing is now done via Docker in docker-test.yml +# This workflow is kept for real-world VM testing when needed on: - push: - branches: - - master - - ci workflow_dispatch: name: Test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..89ec301 --- /dev/null +++ b/Makefile @@ -0,0 +1,136 @@ +.PHONY: test test-build test-up test-down test-logs test-clean + +# Run the full test suite +test: test-build test-up + @echo "Waiting for tests to complete..." + @for i in $$(seq 1 60); do \ + if docker logs openvpn-client 2>&1 | grep -q "ALL TESTS PASSED"; then \ + echo "✓ Tests passed!"; \ + $(MAKE) test-down; \ + exit 0; \ + fi; \ + if docker logs openvpn-client 2>&1 | grep -q "FAIL:"; then \ + echo "✗ Tests failed!"; \ + docker logs openvpn-client; \ + $(MAKE) test-down; \ + exit 1; \ + fi; \ + echo "Waiting... ($$i/60)"; \ + sleep 2; \ + done; \ + echo "Timeout waiting for tests"; \ + $(MAKE) test-down; \ + exit 1 + +# Build test containers +test-build: + BASE_IMAGE=$(BASE_IMAGE) docker compose build + +# Start test containers +test-up: + docker compose up -d + +# Stop and remove test containers +test-down: + docker compose down -v --remove-orphans + +# View logs +test-logs: + docker compose logs -f + +# View server logs only +test-logs-server: + docker logs -f openvpn-server + +# View client logs only +test-logs-client: + docker logs -f openvpn-client + +# Full cleanup +test-clean: test-down + docker rmi openvpn-install-openvpn-server openvpn-install-openvpn-client 2>/dev/null || true + docker volume prune -f + +# Interactive shell into server container +test-shell-server: + docker exec -it openvpn-server /bin/bash + +# Interactive shell into client container +test-shell-client: + docker exec -it openvpn-client /bin/bash + +# Test specific distributions +test-ubuntu-18.04: + $(MAKE) test BASE_IMAGE=ubuntu:18.04 + +test-ubuntu-20.04: + $(MAKE) test BASE_IMAGE=ubuntu:20.04 + +test-ubuntu-22.04: + $(MAKE) test BASE_IMAGE=ubuntu:22.04 + +test-ubuntu-24.04: + $(MAKE) test BASE_IMAGE=ubuntu:24.04 + +test-debian-11: + $(MAKE) test BASE_IMAGE=debian:11 + +test-debian-12: + $(MAKE) test BASE_IMAGE=debian:12 + +test-fedora-40: + $(MAKE) test BASE_IMAGE=fedora:40 + +test-fedora-41: + $(MAKE) test BASE_IMAGE=fedora:41 + +test-rocky-8: + $(MAKE) test BASE_IMAGE=rockylinux:8 + +test-rocky-9: + $(MAKE) test BASE_IMAGE=rockylinux:9 + +test-almalinux-8: + $(MAKE) test BASE_IMAGE=almalinux:8 + +test-almalinux-9: + $(MAKE) test BASE_IMAGE=almalinux:9 + +test-oracle-8: + $(MAKE) test BASE_IMAGE=oraclelinux:8 + +test-oracle-9: + $(MAKE) test BASE_IMAGE=oraclelinux:9 + +test-amazon-2: + $(MAKE) test BASE_IMAGE=amazonlinux:2 + +test-amazon-2023: + $(MAKE) test BASE_IMAGE=amazonlinux:2023 + +test-arch: + $(MAKE) test BASE_IMAGE=archlinux:latest + +test-centos-stream-9: + $(MAKE) test BASE_IMAGE=quay.io/centos/centos:stream9 + +# Test all distributions (runs sequentially) +test-all: + $(MAKE) test-ubuntu-18.04 + $(MAKE) test-ubuntu-20.04 + $(MAKE) test-ubuntu-22.04 + $(MAKE) test-ubuntu-24.04 + $(MAKE) test-debian-11 + $(MAKE) test-debian-12 + $(MAKE) test-fedora-40 + $(MAKE) test-fedora-41 + $(MAKE) test-rocky-8 + $(MAKE) test-rocky-9 + $(MAKE) test-almalinux-8 + $(MAKE) test-almalinux-9 + $(MAKE) test-oracle-8 + $(MAKE) test-oracle-9 + $(MAKE) test-amazon-2 + $(MAKE) test-amazon-2023 + $(MAKE) test-arch + $(MAKE) test-centos-stream-9 diff --git a/README.md b/README.md index 75d449c..6a58353 100644 --- a/README.md +++ b/README.md @@ -132,26 +132,23 @@ export PASS="1" The script supports these Linux distributions: -| | Support | -| ---------------------- | ------- | -| AlmaLinux 8 | ✅ | -| Amazon Linux 2 | ✅ | -| Amazon Linux >= 2023.6 | ✅ | -| Arch Linux | ✅ | -| CentOS 7 | ✅ | -| CentOS Stream >= 8 | ✅ 🤖 | -| Debian >= 10 | ✅ 🤖 | -| Fedora >= 35 | ✅ 🤖 | -| Oracle Linux 8 | ✅ | -| Rocky Linux 8 | ✅ | -| Ubuntu >= 18.04 | ✅ 🤖 | +| | Support | +| ------------------ | ------- | +| AlmaLinux >= 8 | ✅ 🤖 | +| Amazon Linux 2 | ✅ 🤖 | +| Amazon Linux 2023 | ✅ 🤖 | +| Arch Linux | ✅ 🤖 | +| CentOS Stream >= 8 | ✅ 🤖 | +| Debian >= 11 | ✅ 🤖 | +| Fedora >= 40 | ✅ 🤖 | +| Oracle Linux >= 8 | ✅ 🤖 | +| Rocky Linux >= 8 | ✅ 🤖 | +| Ubuntu >= 18.04 | ✅ 🤖 | To be noted: - The script is regularly tested against the distributions marked with a 🤖 only. - It's only tested on `amd64` architecture. -- It should work on older versions such as Debian 8+, Ubuntu 16.04+ and previous Fedora releases. But versions not in the table above are not officially supported. - - It should also support versions between the LTS versions, but these are not tested. - The script requires `systemd`. ## Fork diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abce116 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,55 @@ +--- +services: + openvpn-server: + build: + context: . + dockerfile: test/Dockerfile.server + args: + BASE_IMAGE: ${BASE_IMAGE:-} + container_name: openvpn-server + hostname: openvpn-server + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + sysctls: + - net.ipv4.ip_forward=1 + volumes: + - shared-config:/shared + networks: + vpn-test: + ipv4_address: 172.28.0.10 + healthcheck: + test: ["CMD", "pgrep", "openvpn"] + interval: 5s + timeout: 3s + retries: 30 + + openvpn-client: + build: + context: . + dockerfile: test/Dockerfile.client + container_name: openvpn-client + hostname: openvpn-client + cap_add: + - NET_ADMIN + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - shared-config:/shared:ro + networks: + vpn-test: + ipv4_address: 172.28.0.20 + depends_on: + openvpn-server: + condition: service_healthy + +volumes: + shared-config: + +networks: + vpn-test: + driver: bridge + ipam: + config: + - subnet: 172.28.0.0/24 diff --git a/openvpn-install.sh b/openvpn-install.sh index 9170b7e..e6f0eca 100755 --- a/openvpn-install.sh +++ b/openvpn-install.sh @@ -3,7 +3,7 @@ # SC1091: Not following /etc/os-release (sourced dynamically) # SC2034: Variables used indirectly or exported for subprocesses -# Secure OpenVPN server installer for Debian, Ubuntu, CentOS, Amazon Linux 2, Fedora, Oracle Linux 8, Arch Linux, Rocky Linux and AlmaLinux. +# Secure OpenVPN server installer for Debian, Ubuntu, CentOS, Amazon Linux 2, Fedora, Oracle Linux, Arch Linux, Rocky Linux and AlmaLinux. # https://github.com/angristan/openvpn-install # Configuration constants @@ -30,10 +30,10 @@ function checkOS() { source /etc/os-release if [[ $ID == "debian" || $ID == "raspbian" ]]; then - if [[ $VERSION_ID -lt 9 ]]; then + if [[ $VERSION_ID -lt 11 ]]; then echo "⚠️ Your version of Debian is not supported." echo "" - echo "However, if you're using Debian >= 9 or unstable/testing, you can continue at your own risk." + echo "However, if you're using Debian >= 11 or unstable/testing, you can continue at your own risk." echo "" until [[ $CONTINUE =~ (y|n) ]]; do read -rp "Continue? [y/n]: " -e CONTINUE @@ -45,10 +45,10 @@ function checkOS() { elif [[ $ID == "ubuntu" ]]; then OS="ubuntu" MAJOR_UBUNTU_VERSION=$(echo "$VERSION_ID" | cut -d '.' -f1) - if [[ $MAJOR_UBUNTU_VERSION -lt 16 ]]; then + if [[ $MAJOR_UBUNTU_VERSION -lt 18 ]]; then echo "⚠️ Your version of Ubuntu is not supported." echo "" - echo "However, if you're using Ubuntu >= 16.04 or beta, you can continue at your own risk." + echo "However, if you're using Ubuntu >= 18.04 or beta, you can continue at your own risk." echo "" until [[ $CONTINUE =~ (y|n) ]]; do read -rp "Continue? [y/n]: " -e CONTINUE @@ -65,20 +65,20 @@ function checkOS() { fi if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]]; then OS="centos" - if [[ ${VERSION_ID%.*} -lt 7 ]]; then + if [[ ${VERSION_ID%.*} -lt 8 ]]; then echo "⚠️ Your version of CentOS is not supported." echo "" - echo "The script only supports CentOS 7 and CentOS 8." + echo "The script only supports CentOS Stream 8+ / Rocky Linux 8+ / AlmaLinux 8+." echo "" exit 1 fi fi if [[ $ID == "ol" ]]; then OS="oracle" - if [[ ! $VERSION_ID =~ (8) ]]; then + if [[ ! $VERSION_ID =~ ^(8|9) ]]; then echo "Your version of Oracle Linux is not supported." echo "" - echo "The script only supports Oracle Linux 8." + echo "The script only supports Oracle Linux 8 and 9." exit 1 fi fi @@ -98,7 +98,7 @@ function checkOS() { elif [[ -e /etc/arch-release ]]; then OS=arch else - echo "It looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS, Amazon Linux 2, Oracle Linux 8 or Arch Linux system." + echo "It looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS, Amazon Linux 2, Oracle Linux or Arch Linux system." exit 1 fi } @@ -718,20 +718,19 @@ function installOpenVPN() { if [[ $OS =~ (debian|ubuntu) ]]; then apt-get update apt-get -y install ca-certificates gnupg - # We add the OpenVPN repo to get the latest version. - if [[ $VERSION_ID == "16.04" ]]; then - echo "deb http://build.openvpn.net/debian/openvpn/stable xenial main" >/etc/apt/sources.list.d/openvpn.list - wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add - - apt-get update - fi - # Ubuntu > 16.04 and Debian > 8 have OpenVPN >= 2.4 without the need of a third party repository. + # Ubuntu >= 18.04 and Debian >= 11 have OpenVPN >= 2.4 without the need of a third party repository. apt-get install -y openvpn iptables openssl wget ca-certificates curl elif [[ $OS == 'centos' ]]; then yum install -y epel-release yum install -y openvpn iptables openssl wget ca-certificates curl tar 'policycoreutils-python*' elif [[ $OS == 'oracle' ]]; then - yum install -y oracle-epel-release-el8 - yum-config-manager --enable ol8_developer_EPEL + if [[ $VERSION_ID =~ ^8 ]]; then + yum install -y oracle-epel-release-el8 + yum-config-manager --enable ol8_developer_EPEL + elif [[ $VERSION_ID =~ ^9 ]]; then + yum install -y oracle-epel-release-el9 + yum-config-manager --enable ol9_developer_EPEL + fi yum install -y openvpn iptables openssl wget ca-certificates curl tar policycoreutils-python-utils elif [[ $OS == 'amzn' ]]; then amazon-linux-extras install -y epel @@ -993,11 +992,6 @@ verb 3" >>/etc/openvpn/server.conf systemctl daemon-reload systemctl enable openvpn-server@server systemctl restart openvpn-server@server - elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then - # On Ubuntu 16.04, we use the package from the OpenVPN repo - # This package uses a sysvinit service - systemctl enable openvpn - systemctl start openvpn else # Don't modify package-provided service cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service @@ -1297,9 +1291,6 @@ function removeOpenVPN() { systemctl stop openvpn-server@server # Remove customised service rm /etc/systemd/system/openvpn-server@.service - elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then - systemctl disable openvpn - systemctl stop openvpn else systemctl disable openvpn@server systemctl stop openvpn@server diff --git a/test/Dockerfile.client b/test/Dockerfile.client new file mode 100644 index 0000000..a8121e3 --- /dev/null +++ b/test/Dockerfile.client @@ -0,0 +1,24 @@ +# checkov:skip=CKV_DOCKER_2:Test container doesn't need healthcheck +# checkov:skip=CKV_DOCKER_3:OpenVPN client requires root for NET_ADMIN +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# Install OpenVPN client and testing tools +RUN apt-get update && apt-get install -y \ + openvpn \ + iproute2 \ + iputils-ping \ + procps \ + && rm -rf /var/lib/apt/lists/* + +# Create TUN device directory (device will be mounted at runtime) +RUN mkdir -p /dev/net + +# Copy test scripts +COPY test/client-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +WORKDIR /etc/openvpn + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/test/Dockerfile.server b/test/Dockerfile.server new file mode 100644 index 0000000..c79baef --- /dev/null +++ b/test/Dockerfile.server @@ -0,0 +1,42 @@ +# checkov:skip=CKV_DOCKER_2:Test container doesn't need healthcheck +# checkov:skip=CKV_DOCKER_3:OpenVPN server requires root for NET_ADMIN +# checkov:skip=CKV_DOCKER_7:Base image is parameterized, some use latest tag +ARG BASE_IMAGE=ubuntu:24.04 +FROM ${BASE_IMAGE} + +ARG BASE_IMAGE +ENV DEBIAN_FRONTEND=noninteractive + +# Install basic dependencies based on the OS +RUN if command -v apt-get >/dev/null; then \ + apt-get update && apt-get install -y \ + iproute2 iptables curl procps systemd systemd-sysv \ + && 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 \ + && dnf clean all; \ + elif command -v yum >/dev/null; then \ + yum install -y \ + iproute iptables curl procps-ng systemd tar gzip \ + && yum clean all; \ + elif command -v pacman >/dev/null; then \ + pacman -Syu --noconfirm \ + iproute2 iptables curl procps-ng \ + && pacman -Scc --noconfirm; \ + fi + +# Create TUN device (will be mounted at runtime) +RUN mkdir -p /dev/net + +# Copy the install script +COPY openvpn-install.sh /opt/openvpn-install.sh +RUN chmod +x /opt/openvpn-install.sh + +# Copy test scripts +COPY test/server-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +WORKDIR /opt + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/test/client-entrypoint.sh b/test/client-entrypoint.sh new file mode 100755 index 0000000..c2ed605 --- /dev/null +++ b/test/client-entrypoint.sh @@ -0,0 +1,90 @@ +#!/bin/bash +set -e + +echo "=== OpenVPN Client Container ===" + +# Create TUN device if it doesn't exist +if [ ! -c /dev/net/tun ]; then + mkdir -p /dev/net + mknod /dev/net/tun c 10 200 + chmod 600 /dev/net/tun +fi + +echo "TUN device ready" + +# Wait for client config to be available +echo "Waiting for client config..." +MAX_WAIT=120 +WAITED=0 +while [ ! -f /shared/client.ovpn ] && [ $WAITED -lt $MAX_WAIT ]; do + sleep 2 + WAITED=$((WAITED + 2)) + echo "Waiting... ($WAITED/$MAX_WAIT seconds)" +done + +if [ ! -f /shared/client.ovpn ]; then + echo "ERROR: Client config not found after ${MAX_WAIT}s" + exit 1 +fi + +echo "Client config found!" +cat /shared/client.ovpn + +# Connect to VPN +echo "Connecting to OpenVPN server..." +openvpn --config /shared/client.ovpn --daemon --log /var/log/openvpn.log + +# Wait for connection +echo "Waiting for VPN connection..." +MAX_WAIT=60 +WAITED=0 +while ! ip addr show tun0 2>/dev/null | grep -q "inet " && [ $WAITED -lt $MAX_WAIT ]; do + sleep 2 + WAITED=$((WAITED + 2)) + echo "Waiting for tun0... ($WAITED/$MAX_WAIT seconds)" + + # Check for errors + if [ -f /var/log/openvpn.log ]; then + tail -5 /var/log/openvpn.log + fi +done + +if ! ip addr show tun0 2>/dev/null | grep -q "inet "; then + echo "ERROR: VPN connection failed" + echo "=== OpenVPN log ===" + cat /var/log/openvpn.log || true + exit 1 +fi + +echo "=== VPN Connected! ===" +ip addr show tun0 + +# Run connectivity tests +echo "" +echo "=== Running connectivity tests ===" + +# Test 1: Check tun0 interface +echo "Test 1: Checking tun0 interface..." +if ip addr show tun0 | grep -q "10.8.0"; then + echo "PASS: tun0 interface has correct IP range (10.8.0.x)" +else + echo "FAIL: tun0 interface doesn't have expected IP" + exit 1 +fi + +# Test 2: Ping VPN gateway +echo "Test 2: Pinging VPN gateway (10.8.0.1)..." +if ping -c 3 10.8.0.1; then + echo "PASS: Can ping VPN gateway" +else + echo "FAIL: Cannot ping VPN gateway" + exit 1 +fi + +echo "" +echo "==========================================" +echo " ALL TESTS PASSED!" +echo "==========================================" + +# Keep container running for debugging if needed +exec tail -f /var/log/openvpn.log diff --git a/test/server-entrypoint.sh b/test/server-entrypoint.sh new file mode 100755 index 0000000..5661e3f --- /dev/null +++ b/test/server-entrypoint.sh @@ -0,0 +1,109 @@ +#!/bin/bash +set -e + +echo "=== OpenVPN Server Container ===" + +# Create TUN device if it doesn't exist +if [ ! -c /dev/net/tun ]; then + mkdir -p /dev/net + mknod /dev/net/tun c 10 200 + chmod 600 /dev/net/tun +fi + +echo "TUN device ready" + +# Set up environment for auto-install +export AUTO_INSTALL=y +export APPROVE_INSTALL=y +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 COMPRESSION_ENABLED=n +export CUSTOMIZE_ENC=n +export CLIENT=testclient +export PASS=1 +export ENDPOINT=openvpn-server + +# Prepare script for container environment: +# - Replace systemctl calls with no-ops (systemd doesn't work in containers) +# 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 +chmod +x /tmp/openvpn-install.sh + +echo "Running OpenVPN install script..." +# Run in subshell because the script calls 'exit 0' after generating client config +# Use || true to prevent set -e from exiting on failure, then check exit code +(bash -x /tmp/openvpn-install.sh) && INSTALL_EXIT_CODE=0 || INSTALL_EXIT_CODE=$? + +echo "=== Installation complete (exit code: $INSTALL_EXIT_CODE) ===" + +if [ "$INSTALL_EXIT_CODE" -ne 0 ]; then + echo "ERROR: Install script failed with exit code $INSTALL_EXIT_CODE" + exit 1 +fi + +# Verify all expected files were created +echo "Verifying installation..." +MISSING_FILES=0 +for f in \ + /etc/openvpn/server.conf \ + /etc/openvpn/ca.crt \ + /etc/openvpn/ca.key \ + /etc/openvpn/tls-crypt.key \ + /etc/openvpn/crl.pem \ + /etc/openvpn/easy-rsa/pki/ca.crt \ + /etc/iptables/add-openvpn-rules.sh \ + /root/testclient.ovpn; do + if [ ! -f "$f" ]; then + echo "ERROR: Missing file: $f" + MISSING_FILES=$((MISSING_FILES + 1)) + fi +done + +if [ $MISSING_FILES -gt 0 ]; then + echo "ERROR: $MISSING_FILES required files are missing" + exit 1 +fi + +echo "All required files present" +echo "" +echo "Server config:" +cat /etc/openvpn/server.conf + +# Copy client config to shared volume +cp /root/testclient.ovpn /shared/client.ovpn +# Modify remote address to use container hostname +sed -i 's/^remote .*/remote openvpn-server 1194/' /shared/client.ovpn +echo "Client config copied to /shared/client.ovpn" + +# Start OpenVPN server manually (systemd doesn't work in containers) +echo "Starting OpenVPN server..." + +# Apply iptables rules manually (systemd not available in containers) +echo "Applying iptables rules..." +bash /etc/iptables/add-openvpn-rules.sh + +# Verify iptables NAT rules exist +echo "Verifying iptables NAT rules..." +if iptables -t nat -L POSTROUTING -n | grep -q "10.8.0.0"; then + echo "PASS: NAT POSTROUTING rule for 10.8.0.0/24 exists" +else + echo "FAIL: NAT POSTROUTING rule for 10.8.0.0/24 not found" + echo "Current NAT rules:" + iptables -t nat -L POSTROUTING -n -v + exit 1 +fi + +# Enable IP forwarding (may already be set via docker-compose sysctls) +if [ "$(cat /proc/sys/net/ipv4/ip_forward)" != "1" ]; then + echo 1 >/proc/sys/net/ipv4/ip_forward || { + echo "ERROR: Failed to enable IP forwarding" + exit 1 + } +fi + +# Start OpenVPN in foreground (run from /etc/openvpn so relative paths work) +cd /etc/openvpn +exec openvpn --config /etc/openvpn/server.conf