Skip to content

Instantly share code, notes, and snippets.

@jmeubank
Last active February 23, 2020 22:14
Show Gist options
  • Save jmeubank/9f6a0ffa5d559c5c45b8e00c3d705ed0 to your computer and use it in GitHub Desktop.
Save jmeubank/9f6a0ffa5d559c5c45b8e00c3d705ed0 to your computer and use it in GitHub Desktop.
[PATCH] Make the script more-or-less idempotent when headless.
From 683a806ce120ada13fdf1628ed89ecb7e28cc97c Mon Sep 17 00:00:00 2001
From: "J.M. Eubank" <[email protected]>
Date: Sat, 22 Feb 2020 05:24:52 +0000
Subject: [PATCH] Make the script more-or-less idempotent when headless.
This allows it to be called multiple times if desired by
state-enforcers such as Terraform/Chef/Puppet/Ansible.
---
README.md | 2 +
openvpn-install.sh | 211 +++++++++++++++++++++++++--------------------
2 files changed, 119 insertions(+), 94 deletions(-)
diff --git a/README.md b/README.md
index ca3cdf7..7006644 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,8 @@ Other variables can be set depending on your choice (encryption, compression). Y
Password-protected clients are not supported by the headless installation method since user input is expected by Easy-RSA.
+The headless install is more-or-less idempotent, in that it has been made safe to run multiple times with the same parameters, e.g. by a state provisioner like Terraform/Salt/Chef/Puppet. It will only install and regenerate the Easy-RSA PKI if it doesn't already exist, and it will only install OpenVPN if it doesn't already exist. It will recreate all local config and re-export the client file on each headless run.
+
### Headless User Addition
It's also possible to automate the addition of a new user. Here, the key is to provide the (string) value of the `MENU_OPTION` variable along with the remaining mandatory variables before invoking the script.
diff --git a/openvpn-install.sh b/openvpn-install.sh
index 90ea433..e11152e 100755
--- a/openvpn-install.sh
+++ b/openvpn-install.sh
@@ -98,6 +98,7 @@ function initialCheck () {
}
function installUnbound () {
+ # If Unbound isn't installed, install it
if [[ ! -e /etc/unbound/unbound.conf ]]; then
if [[ "$OS" =~ (debian|ubuntu) ]]; then
@@ -137,7 +138,9 @@ prefetch: yes' >> /etc/unbound/unbound.conf
# Get root servers list
curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
- mv /etc/unbound/unbound.conf /etc/unbound/unbound.conf.old
+ if [[ ! -f /etc/unbound/unbound.conf.old ]]; then
+ mv /etc/unbound/unbound.conf /etc/unbound/unbound.conf.old
+ fi
echo 'server:
use-syslog: yes
@@ -606,33 +609,38 @@ function installOpenVPN () {
# Get the "public" interface from the default route
NIC=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
- 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" = "8" ]]; then
- echo "deb http://build.openvpn.net/debian/openvpn/stable jessie 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
- 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 -
+ # If OpenVPN isn't installed yet, install it. This script is more-or-less
+ # idempotent on multiple runs, but will only install OpenVPN from upstream
+ # the first time.
+ if [[ ! -e /etc/openvpn/server.conf ]]; then
+ 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" = "8" ]]; then
+ echo "deb http://build.openvpn.net/debian/openvpn/stable jessie 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
+ 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
+ elif [[ "$OS" = 'centos' ]]; then
+ yum install -y epel-release
+ yum install -y openvpn iptables openssl wget ca-certificates curl tar
+ elif [[ "$OS" = 'amzn' ]]; then
+ amazon-linux-extras install -y epel
+ yum install -y openvpn iptables openssl wget ca-certificates curl
+ elif [[ "$OS" = 'fedora' ]]; then
+ dnf install -y openvpn iptables openssl wget ca-certificates curl
+ elif [[ "$OS" = 'arch' ]]; then
+ # Install required dependencies and upgrade the system
+ pacman --needed --noconfirm -Syu openvpn iptables openssl wget ca-certificates curl
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
- elif [[ "$OS" = 'centos' ]]; then
- yum install -y epel-release
- yum install -y openvpn iptables openssl wget ca-certificates curl tar
- elif [[ "$OS" = 'amzn' ]]; then
- amazon-linux-extras install -y epel
- yum install -y openvpn iptables openssl wget ca-certificates curl
- elif [[ "$OS" = 'fedora' ]]; then
- dnf install -y openvpn iptables openssl wget ca-certificates curl
- elif [[ "$OS" = 'arch' ]]; then
- # Install required dependencies and upgrade the system
- pacman --needed --noconfirm -Syu openvpn iptables openssl wget ca-certificates curl
fi
# Find out if the machine uses nogroup or nobody for the permissionless group
@@ -647,60 +655,68 @@ function installOpenVPN () {
rm -rf /etc/openvpn/easy-rsa/
fi
- # Install the latest version of easy-rsa from source
- local version="3.0.6"
- wget -O ~/EasyRSA-unix-v${version}.tgz https://github.com/OpenVPN/easy-rsa/releases/download/v${version}/EasyRSA-unix-v${version}.tgz
- tar xzf ~/EasyRSA-unix-v${version}.tgz -C ~/
- mv ~/EasyRSA-v${version} /etc/openvpn/easy-rsa
- chown -R root:root /etc/openvpn/easy-rsa/
- rm -f ~/EasyRSA-unix-v${version}.tgz
-
- cd /etc/openvpn/easy-rsa/ || return
- case $CERT_TYPE in
- 1)
- echo "set_var EASYRSA_ALGO ec" > vars
- echo "set_var EASYRSA_CURVE $CERT_CURVE" >> vars
- ;;
- 2)
- echo "set_var EASYRSA_KEY_SIZE $RSA_KEY_SIZE" > vars
- ;;
- esac
+ if [[ ! -d /etc/openvpn/easy-rsa-auto/ ]]; then
+ # Install the latest version of easy-rsa from source
+ local version="3.0.6"
+ wget -O ~/EasyRSA-unix-v${version}.tgz https://github.com/OpenVPN/easy-rsa/releases/download/v${version}/EasyRSA-unix-v${version}.tgz
+ tar xzf ~/EasyRSA-unix-v${version}.tgz -C ~/
+ mkdir -p /etc/openvpn/easy-rsa-auto
+ mv ~/EasyRSA-v${version}/* /etc/openvpn/easy-rsa-auto/
+ chown -R root:root /etc/openvpn/easy-rsa-auto/
+ rm -f ~/EasyRSA-unix-v${version}.tgz
+
+ cd /etc/openvpn/easy-rsa-auto/ || return
+ case $CERT_TYPE in
+ 1)
+ echo "set_var EASYRSA_ALGO ec" > vars
+ echo "set_var EASYRSA_CURVE $CERT_CURVE" >> vars
+ ;;
+ 2)
+ echo "set_var EASYRSA_KEY_SIZE $RSA_KEY_SIZE" > vars
+ ;;
+ esac
- # Generate a random, alphanumeric identifier of 16 characters for CN and one for server name
- SERVER_CN="cn_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
- SERVER_NAME="server_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
- echo "set_var EASYRSA_REQ_CN $SERVER_CN" >> vars
+ # Generate a random, alphanumeric identifier of 16 characters for CN and one for server name
+ SERVER_CN="cn_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
+ echo "$SERVER_CN" > SERVER_CN_GENERATED
+ SERVER_NAME="server_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
+ echo "$SERVER_NAME" > SERVER_NAME_GENERATED
+ echo "set_var EASYRSA_REQ_CN $SERVER_CN" >> vars
- # Create the PKI, set up the CA, the DH params and the server certificate
- ./easyrsa init-pki
+ # Create the PKI, set up the CA, the DH params and the server certificate
+ ./easyrsa init-pki
- # Workaround to remove unharmful error until easy-rsa 3.0.7
- # https://github.com/OpenVPN/easy-rsa/issues/261
- sed -i 's/^RANDFILE/#RANDFILE/g' pki/openssl-easyrsa.cnf
+ # Workaround to remove unharmful error until easy-rsa 3.0.7
+ # https://github.com/OpenVPN/easy-rsa/issues/261
+ sed -i 's/^RANDFILE/#RANDFILE/g' pki/openssl-easyrsa.cnf
- ./easyrsa --batch build-ca nopass
+ ./easyrsa --batch build-ca nopass
- if [[ $DH_TYPE == "2" ]]; then
- # ECDH keys are generated on-the-fly so we don't need to generate them beforehand
- openssl dhparam -out dh.pem $DH_KEY_SIZE
- fi
+ if [[ $DH_TYPE == "2" ]]; then
+ # ECDH keys are generated on-the-fly so we don't need to generate them beforehand
+ openssl dhparam -out dh.pem $DH_KEY_SIZE
+ fi
- ./easyrsa build-server-full "$SERVER_NAME" nopass
- EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
+ ./easyrsa build-server-full "$SERVER_NAME" nopass
+ EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
- case $TLS_SIG in
- 1)
- # Generate tls-crypt key
- openvpn --genkey --secret /etc/openvpn/tls-crypt.key
- ;;
- 2)
- # Generate tls-auth key
- openvpn --genkey --secret /etc/openvpn/tls-auth.key
- ;;
- esac
+ case $TLS_SIG in
+ 1)
+ # Generate tls-crypt key
+ openvpn --genkey --secret /etc/openvpn/tls-crypt.key
+ ;;
+ 2)
+ # Generate tls-auth key
+ openvpn --genkey --secret /etc/openvpn/tls-auth.key
+ ;;
+ esac
+ else
+ cd /etc/openvpn/easy-rsa-auto/ || return
+ SERVER_NAME=$(cat SERVER_NAME_GENERATED)
+ fi
- # Move all the generated files
- cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn
+ # Copy all the generated files
+ cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/easy-rsa-auto/pki/crl.pem /etc/openvpn
if [[ $DH_TYPE == "2" ]]; then
cp dh.pem /etc/openvpn
fi
@@ -835,7 +851,7 @@ verb 3" >> /etc/openvpn/server.conf
mkdir -p /var/log/openvpn
# Enable routing
- echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/20-openvpn.conf
+ echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/20-openvpn.conf
if [[ "$IPV6_SUPPORT" = 'y' ]]; then
echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.d/20-openvpn.conf
fi
@@ -892,7 +908,7 @@ verb 3" >> /etc/openvpn/server.conf
fi
# Add iptables rules in two scripts
- mkdir /etc/iptables
+ mkdir -p /etc/iptables
# Script to add rules
echo "#!/bin/sh
@@ -1004,16 +1020,23 @@ function newClient () {
read -rp "Select an option [1-2]: " -e -i 1 PASS
done
- cd /etc/openvpn/easy-rsa/ || return
- case $PASS in
- 1)
- ./easyrsa build-client-full "$CLIENT" nopass
- ;;
- 2)
- echo "⚠️ You will be asked for the client password below ⚠️"
- ./easyrsa build-client-full "$CLIENT"
- ;;
- esac
+ CLIENTEXISTS=$(tail -n +2 /etc/openvpn/easy-rsa-auto/pki/index.txt | grep -c -E "/CN=$CLIENT\$")
+ if [[ "$CLIENTEXISTS" = '1' ]]; then
+ echo ""
+ echo "The specified client CN was found in easy-rsa."
+ else
+ cd /etc/openvpn/easy-rsa-auto/ || return
+ case $PASS in
+ 1)
+ ./easyrsa build-client-full "$CLIENT" nopass
+ ;;
+ 2)
+ echo "⚠️ You will be asked for the client password below ⚠️"
+ ./easyrsa build-client-full "$CLIENT"
+ ;;
+ esac
+ echo "Client $CLIENT added."
+ fi
# Home directory of the user, where the client configuration (.ovpn) will be written
if [ -e "/home/$CLIENT" ]; then # if $1 is a user name
@@ -1035,15 +1058,15 @@ function newClient () {
cp /etc/openvpn/client-template.txt "$homeDir/$CLIENT.ovpn"
{
echo "<ca>"
- cat "/etc/openvpn/easy-rsa/pki/ca.crt"
+ cat "/etc/openvpn/easy-rsa-auto/pki/ca.crt"
echo "</ca>"
echo "<cert>"
- awk '/BEGIN/,/END/' "/etc/openvpn/easy-rsa/pki/issued/$CLIENT.crt"
+ awk '/BEGIN/,/END/' "/etc/openvpn/easy-rsa-auto/pki/issued/$CLIENT.crt"
echo "</cert>"
echo "<key>"
- cat "/etc/openvpn/easy-rsa/pki/private/$CLIENT.key"
+ cat "/etc/openvpn/easy-rsa-auto/pki/private/$CLIENT.key"
echo "</key>"
case $TLS_SIG in
@@ -1062,14 +1085,14 @@ function newClient () {
} >> "$homeDir/$CLIENT.ovpn"
echo ""
- echo "Client $CLIENT added, the configuration file is available at $homeDir/$CLIENT.ovpn."
+ echo "The configuration file has been written to $homeDir/$CLIENT.ovpn."
echo "Download the .ovpn file and import it in your OpenVPN client."
exit 0
}
function revokeClient () {
- NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^V")
+ NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa-auto/pki/index.txt | grep -c "^V")
if [[ "$NUMBEROFCLIENTS" = '0' ]]; then
echo ""
echo "You have no existing clients!"
@@ -1078,15 +1101,15 @@ function revokeClient () {
echo ""
echo "Select the existing client certificate you want to revoke"
- tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
+ tail -n +2 /etc/openvpn/easy-rsa-auto/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') '
if [[ "$NUMBEROFCLIENTS" = '1' ]]; then
read -rp "Select one client [1]: " CLIENTNUMBER
else
read -rp "Select one client [1-$NUMBEROFCLIENTS]: " CLIENTNUMBER
fi
- CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p)
- cd /etc/openvpn/easy-rsa/ || return
+ CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa-auto/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p)
+ cd /etc/openvpn/easy-rsa-auto/ || return
./easyrsa --batch revoke "$CLIENT"
EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl
# Cleanup
@@ -1094,7 +1117,7 @@ function revokeClient () {
rm -f "pki/private/$CLIENT.key"
rm -f "pki/issued/$CLIENT.crt"
rm -f /etc/openvpn/crl.pem
- cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem
+ cp /etc/openvpn/easy-rsa-auto/pki/crl.pem /etc/openvpn/crl.pem
chmod 644 /etc/openvpn/crl.pem
find /home/ -maxdepth 2 -name "$CLIENT.ovpn" -delete
rm -f "/root/$CLIENT.ovpn"
@@ -1252,7 +1275,7 @@ function manageMenu () {
initialCheck
# Check if OpenVPN is already installed
-if [[ -e /etc/openvpn/server.conf ]]; then
+if [[ -e /etc/openvpn/server.conf && $AUTO_INSTALL != "y" ]]; then
manageMenu
else
installOpenVPN
--
2.17.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment