Last active
February 23, 2020 22:14
-
-
Save jmeubank/9f6a0ffa5d559c5c45b8e00c3d705ed0 to your computer and use it in GitHub Desktop.
[PATCH] Make the script more-or-less idempotent when headless.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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