mirror of
https://github.com/angristan/openvpn-install.git
synced 2025-12-10 22:42:41 +01:00
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:
166
.github/workflows/docker-test.yml
vendored
Normal file
166
.github/workflows/docker-test.yml
vendored
Normal 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
|
||||
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@@ -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
|
||||
|
||||
136
Makefile
Normal file
136
Makefile
Normal 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
|
||||
27
README.md
27
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
|
||||
|
||||
55
docker-compose.yml
Normal file
55
docker-compose.yml
Normal 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
|
||||
@@ -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
|
||||
|
||||
24
test/Dockerfile.client
Normal file
24
test/Dockerfile.client
Normal 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
42
test/Dockerfile.server
Normal 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
90
test/client-entrypoint.sh
Executable 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
109
test/server-entrypoint.sh
Executable 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
|
||||
Reference in New Issue
Block a user