Example OpenVPN Script

Here is an example of how you can use shell scripts to set up an OpenVPN server.

Setting up an OpenVPN server using Docker consists of five commands. We can wrap these commands in a script. The script completes the process from start to finish. It has a menu for the user to (1) install it on a new VPS or rebuild, (2) remove and restart the containers, and (3) generate client configs.

#!/bin/bash

###############################################################################
# © Copyright 2019, BilimEdtech | CC BY 4.0 |
# This script automates building a Docker-based OpenVPN server from
# https://github.com/kylemanna/docker-openvpn
#
# kylemanna's docker-openvpn project has 5 basic commands. This script
# automates these five commands to set up an OpenVPN server with additional
# configurations:
#  - multiple ports
#  - a stronger cipher
#  - TLS auth using 'auth SHA256'
#
# 1) Generate the openvpn.conf file.
#    docker run -v $PWD/vpn-data:/etc/openvpn --rm myownvpn ovpn_genconfig -u udp://IP_ADDRESS:1194
#
# 2) Init the public key infrastructure (PKI).
#    docker run -v $PWD/vpn-data:/etc/openvpn --rm -it myownvpn ovpn_initpki
#
# 3) Run the OpenVPN server.
#    docker run -v $PWD/vpn-data:/etc/openvpn -d -p 1194:1194/udp --restart unless-stopped --cap-add=NET_ADMIN myownvpn
#
# 4) Generate client config.
#    docker run -v $PWD/vpn-data:/etc/openvpn --rm -it myownvpn easyrsa build-client-full user1 nopass
#
# 5) Retrieve the config from the docker container
#    docker run -v $PWD/vpn-data:/etc/openvpn --rm myownvpn ovpn_getclient user1 > user1.ovpn
#
###############################################################################

# Uses the /opt folder for the VPN volume data and configs

###----- Variables
DIRECTORY="/opt/openvpn-udp"
IMAGE="openvpn-local"
CONTAINER="openvpn-udp"
IP_ADDRESS=$(hostname -I | cut -d' ' -f1);
DEFAULT_PORT=5004
UDP_PORTS="80 123 443 972 1234 1935 3074 3748 5004 5730 8080 11111 12345"
CLIENT="client1"
CIPHER="\n
# Uses a stronger cipher for a more secure connection \n
cipher AES-128-CBC \n
tls-version-min 1.2 \n
tls-cipher TLS-DHE-RSA-WITH-AES-128-GCM-SHA256 \n
auth SHA256";
###----- End Variables

# Create $DIRECTORY if it does not exist
if [ ! -d $DIRECTORY ]; then
    mkdir -p $DIRECTORY
fi

#-----------------------------------------------------------
# - Clones the GitHub site
# - Builds a new Docker image
# - Sets up the server config and keys
#-----------------------------------------------------------
create_new_instance () {

    read -r -p "Are you sure you want completely remove and rebuild OpenVPN? ?[N/y] " RESPONSE
    if [[ ! $RESPONSE =~ ^[Yy]$ ]]
    then
        exit;
    fi

    # TODO: Check if exists before removing to prevent displaying error messages
    docker stop $CONTAINER && docker rm $CONTAINER
    rm -r $DIRECTORY/vpn-data
    rm -r $DIRECTORY/docker-openvpn
    rm $DIRECTORY/*.ovpn

    # Build the image from Dockerfile
    cd $DIRECTORY
    git clone https://github.com/kylemanna/docker-openvpn.git

    cd $DIRECTORY/docker-openvpn/
    docker build -t $IMAGE .

    echo ""
    echo "-----------------------------------------------------------"
    echo "Setting up environment (Generating configs and keys)"
    echo "-----------------------------------------------------------"
    echo "running command:"
    echo "docker run -v $DIRECTORY/vpn-data:/etc/openvpn --rm $IMAGE ovpn_genconfig -u udp://$IP_ADDRESS:$DEFAULT_PORT"
    echo ""

    docker run -v $DIRECTORY/vpn-data:/etc/openvpn --rm $IMAGE ovpn_genconfig -u udp://$IP_ADDRESS:$DEFAULT_PORT
    docker run -v $DIRECTORY/vpn-data:/etc/openvpn --rm -it $IMAGE ovpn_initpki

    ## Add stronger cipher to the server config
    echo -e $CIPHER >> $DIRECTORY/vpn-data/openvpn.conf
    echo "tls-server" >> $DIRECTORY/vpn-data/openvpn.conf
}

#-----------------------------------------------------------
# Opens the firewall for each port in the list
#-----------------------------------------------------------
open_ports () {

    for value in $UDP_PORTS
    do
        echo "Adding rule: ufw allow $value/udp"
        ufw allow $value/udp
    done

    # Send 'y' to enable the new rules
    yes | ufw enable
}

#-----------------------------------------------------------
# - Opens the listed ports in the firewall
# - Restarts the container to add ports
# - Restarts the container to apply config changes
#-----------------------------------------------------------
restart_container () {

    echo ""
    echo "-----------------------------------------------------------"
    echo "Attempting to remove the existing container named '$CONTAINER'"
    echo "-----------------------------------------------------------"
    echo ""

    echo "Opening firewall for selected ports"
    open_ports

    docker stop $CONTAINER && docker rm $CONTAINER

    # Build the port list for the container
    for value in $UDP_PORTS
    do
        PORT_LIST="$PORT_LIST -p $value:1194/udp"
    done

    echo "Executing docker run --name $CONTAINER -v $DIRECTORY/vpn-data:/etc/openvpn -d $PORT_LIST --restart unless-stopped --cap-add=NET_ADMIN $IMAGE"
    docker run --name $CONTAINER -v $DIRECTORY/vpn-data:/etc/openvpn -d $PORT_LIST --restart unless-stopped --cap-add=NET_ADMIN $IMAGE
}

#-----------------------------------------------------------
# - Prompts the user for name of the client config
# - Generates the new config
# - Extracts the client config from the container
#-----------------------------------------------------------
create_client_config () {

    echo .
    echo "-----------------------------------------------------------"
    echo "Building client config named '$CLIENT.ovpn'"
    echo "-----------------------------------------------------------"
    echo ""

    read -r -p "Please enter name for client [client1]: " RESPONSE
    if [ -n "$RESPONSE" ]; then
        CLIENT=$RESPONSE
    fi

    docker run -v $DIRECTORY/vpn-data:/etc/openvpn --rm -it $IMAGE easyrsa build-client-full $CLIENT nopass

    echo ""
    echo "Extracting key..."
    docker run -v $DIRECTORY/vpn-data:/etc/openvpn --rm $IMAGE ovpn_getclient $CLIENT > $DIRECTORY/$CLIENT.ovpn

# Fix comp-lzo config issue
    echo "" >> $DIRECTORY/$CLIENT.ovpn
    echo "comp-lzo no" >> $DIRECTORY/$CLIENT.ovpn

    # Add cipher
    echo -e $CIPHER >> $DIRECTORY/$CLIENT.ovpn
    echo "tls-client" >> $DIRECTORY/$CLIENT.ovpn

    # Add port list for reference
    echo "" >> $CLIENT.ovpn
    echo "# Ports available" >> $DIRECTORY/$CLIENT.ovpn
    echo "# $UDP_PORTS"  >> $DIRECTORY/$CLIENT.ovpn

    echo ""
    echo "Please download '$DIRECTORY/$CLIENT.ovpn'"
    echo "-----------------------------------------------------------"
    exit;
}

while :
do
    echo ""
    cat<<EOF
==============================
OpenVPN Init
------------------------------
Please enter your choice:

(1) Set up OpenVPN (rebuild)
(2) Restart container
(3) Create client config
(Q)uit
------------------------------
EOF
    read -n1 -s
    case "$REPLY" in
    "1")  create_new_instance
        restart_container
        echo ""
        create_client_config
        echo ""
        exit
    ;;
    "2")  restart_container
        echo ""
        exit
    ;;
    "3")  create_client_config
        echo ""
        exit
    ;;
    "Q")  echo ""
        exit
    ;;
    "q")  echo ""
        exit
    ;;
    * )  echo "invalid option"     ;;
    esac
    sleep 1
done