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 |
This commit is contained in:
Stanislas
2025-12-07 12:27:41 +01:00
committed by GitHub
parent 94c1af2b5d
commit a3389c126c
10 changed files with 655 additions and 46 deletions

166
.github/workflows/docker-test.yml vendored Normal file
View File

@@ -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

View File

@@ -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: on:
push:
branches:
- master
- ci
workflow_dispatch: workflow_dispatch:
name: Test name: Test

136
Makefile Normal file
View File

@@ -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

View File

@@ -132,26 +132,23 @@ export PASS="1"
The script supports these Linux distributions: The script supports these Linux distributions:
| | Support | | | Support |
| ---------------------- | ------- | | ------------------ | ------- |
| AlmaLinux 8 | ✅ | | AlmaLinux >= 8 | ✅ 🤖 |
| Amazon Linux 2 | ✅ | | Amazon Linux 2 | ✅ 🤖 |
| Amazon Linux >= 2023.6 | ✅ | | Amazon Linux 2023 | ✅ 🤖 |
| Arch Linux | ✅ | | Arch Linux | ✅ 🤖 |
| CentOS 7 | ✅ | | CentOS Stream >= 8 | ✅ 🤖 |
| CentOS Stream >= 8 | ✅ 🤖 | | Debian >= 11 | ✅ 🤖 |
| Debian >= 10 | ✅ 🤖 | | Fedora >= 40 | ✅ 🤖 |
| Fedora >= 35 | ✅ 🤖 | | Oracle Linux >= 8 | ✅ 🤖 |
| Oracle Linux 8 | ✅ | | Rocky Linux >= 8 | ✅ 🤖 |
| Rocky Linux 8 | ✅ | | Ubuntu >= 18.04 | ✅ 🤖 |
| Ubuntu >= 18.04 | ✅ 🤖 |
To be noted: To be noted:
- The script is regularly tested against the distributions marked with a 🤖 only. - The script is regularly tested against the distributions marked with a 🤖 only.
- It's only tested on `amd64` architecture. - 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`. - The script requires `systemd`.
## Fork ## Fork

55
docker-compose.yml Normal file
View File

@@ -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

View File

@@ -3,7 +3,7 @@
# SC1091: Not following /etc/os-release (sourced dynamically) # SC1091: Not following /etc/os-release (sourced dynamically)
# SC2034: Variables used indirectly or exported for subprocesses # 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 # https://github.com/angristan/openvpn-install
# Configuration constants # Configuration constants
@@ -30,10 +30,10 @@ function checkOS() {
source /etc/os-release source /etc/os-release
if [[ $ID == "debian" || $ID == "raspbian" ]]; then 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 "⚠️ Your version of Debian is not supported."
echo "" 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 "" echo ""
until [[ $CONTINUE =~ (y|n) ]]; do until [[ $CONTINUE =~ (y|n) ]]; do
read -rp "Continue? [y/n]: " -e CONTINUE read -rp "Continue? [y/n]: " -e CONTINUE
@@ -45,10 +45,10 @@ function checkOS() {
elif [[ $ID == "ubuntu" ]]; then elif [[ $ID == "ubuntu" ]]; then
OS="ubuntu" OS="ubuntu"
MAJOR_UBUNTU_VERSION=$(echo "$VERSION_ID" | cut -d '.' -f1) 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 "⚠️ Your version of Ubuntu is not supported."
echo "" 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 "" echo ""
until [[ $CONTINUE =~ (y|n) ]]; do until [[ $CONTINUE =~ (y|n) ]]; do
read -rp "Continue? [y/n]: " -e CONTINUE read -rp "Continue? [y/n]: " -e CONTINUE
@@ -65,20 +65,20 @@ function checkOS() {
fi fi
if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]]; then if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]]; then
OS="centos" OS="centos"
if [[ ${VERSION_ID%.*} -lt 7 ]]; then if [[ ${VERSION_ID%.*} -lt 8 ]]; then
echo "⚠️ Your version of CentOS is not supported." echo "⚠️ Your version of CentOS is not supported."
echo "" 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 "" echo ""
exit 1 exit 1
fi fi
fi fi
if [[ $ID == "ol" ]]; then if [[ $ID == "ol" ]]; then
OS="oracle" OS="oracle"
if [[ ! $VERSION_ID =~ (8) ]]; then if [[ ! $VERSION_ID =~ ^(8|9) ]]; then
echo "Your version of Oracle Linux is not supported." echo "Your version of Oracle Linux is not supported."
echo "" echo ""
echo "The script only supports Oracle Linux 8." echo "The script only supports Oracle Linux 8 and 9."
exit 1 exit 1
fi fi
fi fi
@@ -98,7 +98,7 @@ function checkOS() {
elif [[ -e /etc/arch-release ]]; then elif [[ -e /etc/arch-release ]]; then
OS=arch OS=arch
else 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 exit 1
fi fi
} }
@@ -718,20 +718,19 @@ function installOpenVPN() {
if [[ $OS =~ (debian|ubuntu) ]]; then if [[ $OS =~ (debian|ubuntu) ]]; then
apt-get update apt-get update
apt-get -y install ca-certificates gnupg apt-get -y install ca-certificates gnupg
# We add the OpenVPN repo to get the latest version. # Ubuntu >= 18.04 and Debian >= 11 have OpenVPN >= 2.4 without the need of a third party repository.
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.
apt-get install -y openvpn iptables openssl wget ca-certificates curl apt-get install -y openvpn iptables openssl wget ca-certificates curl
elif [[ $OS == 'centos' ]]; then elif [[ $OS == 'centos' ]]; then
yum install -y epel-release yum install -y epel-release
yum install -y openvpn iptables openssl wget ca-certificates curl tar 'policycoreutils-python*' yum install -y openvpn iptables openssl wget ca-certificates curl tar 'policycoreutils-python*'
elif [[ $OS == 'oracle' ]]; then elif [[ $OS == 'oracle' ]]; then
yum install -y oracle-epel-release-el8 if [[ $VERSION_ID =~ ^8 ]]; then
yum-config-manager --enable ol8_developer_EPEL 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 yum install -y openvpn iptables openssl wget ca-certificates curl tar policycoreutils-python-utils
elif [[ $OS == 'amzn' ]]; then elif [[ $OS == 'amzn' ]]; then
amazon-linux-extras install -y epel amazon-linux-extras install -y epel
@@ -993,11 +992,6 @@ verb 3" >>/etc/openvpn/server.conf
systemctl daemon-reload systemctl daemon-reload
systemctl enable openvpn-server@server systemctl enable openvpn-server@server
systemctl restart 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 else
# Don't modify package-provided service # Don't modify package-provided service
cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service
@@ -1297,9 +1291,6 @@ function removeOpenVPN() {
systemctl stop openvpn-server@server systemctl stop openvpn-server@server
# Remove customised service # Remove customised service
rm /etc/systemd/system/openvpn-server@.service rm /etc/systemd/system/openvpn-server@.service
elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then
systemctl disable openvpn
systemctl stop openvpn
else else
systemctl disable openvpn@server systemctl disable openvpn@server
systemctl stop openvpn@server systemctl stop openvpn@server

24
test/Dockerfile.client Normal file
View File

@@ -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"]

42
test/Dockerfile.server Normal file
View File

@@ -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"]

90
test/client-entrypoint.sh Executable file
View File

@@ -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

109
test/server-entrypoint.sh Executable file
View File

@@ -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