Skip to content

Instantly share code, notes, and snippets.

@benfairless
Last active June 25, 2016 17:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benfairless/faf9c83ed9d1b43e8977 to your computer and use it in GitHub Desktop.
Save benfairless/faf9c83ed9d1b43e8977 to your computer and use it in GitHub Desktop.
OpenVPN installation
#!/usr/bin/env bash
# Installs OpenVPN server on CentOS 7
# Check yo' privilege
[[ $(id -u) != 0 ]] && echo 'You must run this script as root!' && exit 1
################################################################################
################################## VARIABLES ###################################
################################################################################
# User-modifiable variables
IFACE='eth0'
ADDRESS=$(ip -4 addr show dev $IFACE | awk '/inet/{print $2}' | cut -d '/' -f 1)
PORT=1194
DNS=192.168.139.104
SUBNET=192.168.139.128
MASK=255.255.255.128
IFACE='eth0'
# Script static variables
CLIENT_TAR='/tmp/openvpn-client.tar.gz'
EASY_RSA='/etc/openvpn/.easy-rsa'
KEYS_DIR='/etc/openvpn/keys'
################################################################################
################################## UTILITIES ###################################
################################################################################
# Provides pretty colourful output with timestamps
output() {
local LABEL='OpenVPN'
local TIMESTAMP=$(date +%H:%M)
local COLOUR='\033[34m' # Blue
local RESET='\033[0m' # Standard
case $1 in
ERROR) local COLOUR='\033[31m' ;; # Red
SUCCESS) local COLOUR='\033[32m' ;; # Green
WARN) local COLOUR='\033[33m' ;; # Yellow
esac
while read LINE; do
echo -e "${COLOUR}${LABEL} [${TIMESTAMP}]${RESET} ${LINE}"
done
}
# Produces bold output
say() {
local BOLD=$(tput bold)
local STD=$(tput sgr0)
echo "${BOLD}$@${STD}"
}
# Creates a copy of $2 at $2.old
backup() {
[[ -z $1 ]] && echo 'backup() - No name variable set' | output ERROR
[[ -z $2 ]] && echo 'backup() - No file variable set' | output ERROR
local NAME=$1
local FILE=$2
if [[ -f $FILE ]]; then
echo "Backing up previous ${NAME} file..." | output
RESULT=$(cp -f ${FILE} ${FILE}.old 2>&1)
if [[ $? != 0 ]]; then
echo "Failed to backup previous ${FILE} file!" | output WARN
echo $RESULT | output WARN
else
echo "${NAME} backup created successfully" | output
fi
fi
}
# Just a pretty way of creating here documents, with error handling and backing
# up of the previous version.
createfile() {
[[ -z $1 ]] && echo 'createfile() - No name variable set' | output ERROR
[[ -z $2 ]] && echo 'createfile() - No file variable set' | output ERROR
local NAME=$1
local FILE=$2
backup "$NAME" "$FILE"
echo "Creating ${NAME}..." | output
while read -r LINE; do
# Add each line of STDIN to $CONTENT, along with a line break
local CONTENT+="${LINE}\n"
done
RESULT=$(echo -e ${CONTENT} > ${FILE} 2>&1)
if [[ $? != 0 ]]; then
echo "Failed to create ${NAME} file!" | output ERROR
echo $RESULT | output ERROR
exit 1
else
echo "${NAME} file created successfully" | output
fi
}
# Checks that the previous command exited correctly and quits the script if not,
# with a pretty error message. Optionally a second string can be provided to
# print on success.
onfail() {
if [[ $? != 0 ]]; then
echo $1 | output ERROR
elif [[ ! -z $2 ]]; then
echo $2 | output
fi
}
################################################################################
################################# INSTALLATION #################################
################################################################################
say 'Installing RPM packages...' | output
yum install -e 0 -y openvpn \
easy-rsa \
iptables \
iptables-utils \
iptables-services | output
onfail 'Failed to install RPM packages!' 'RPM packages installed successfully'
say 'Setting up Easy-RSA...' | output
mkdir -p $EASY_RSA
onfail "Failed to create Easy-RSA directory at ${EASY_RSA}"
cp -rf /usr/share/easy-rsa/2.0/* $EASY_RSA/
onfail "Unable to copy Easy-RSA to $EASY_RSA"
cp -f $EASY_RSA/openssl-1.0.0.cnf $EASY_RSA/openssl.cnf
onfail "Unable to create $EASY_RSA/openssl.cnf to force the OpenSSL version"
restorecon -R $EASY_RSA
onfail "Unable to set SELinux context labels for ${EASY_RSA}"
################################################################################
################################ CONFIGURATION #################################
################################################################################
say 'Generating configuration files...' | output
# This is the main configuration file for OpenVPN and provides the core
# configuration parameters for running the service.
createfile 'OpenVPN server configuration' '/etc/openvpn/server.conf' <<OPENVPN
local $ADDRESS
port $PORT
proto udp
dev tun
comp-lzo
topology subnet
user nobody
group nobody
persist-key
persist-tun
ifconfig-pool-persist ipp.txt
keepalive 10 120
max-clients 100
status openvpn-status.log
log-append openvpn.log
verb 3
mute 20
ca ${KEYS_DIR}/ca.crt
cert ${KEYS_DIR}/server.crt
key ${KEYS_DIR}/server.key
dh ${KEYS_DIR}/dh2048.pem
cipher AES-256-CBC
duplicate-cn
plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn
server ${SUBNET} ${MASK}
push "route 192.168.139.0 255.255.255.128"
push "dhcp-option DNS $DNS"
OPENVPN
# The PAM configuration file permits access through the VPN to users with local
# POSIX accounts. Additional configuration can be added to only permit certain
# groups or users to authenticate using this method.
createfile 'OpenVPN PAM security configuration' '/etc/pam.d/openvpn' <<PAM
auth required pam_unix.so shadow nodelay
account required pam_unix.so
PAM
# This minimal firewall configuration permits only SSH and OpenVPN connections
# as well as allowing SNAT connections through the VPN.
createfile 'Firewall rules' '/etc/sysconfig/iptables' <<IPTABLES
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o $IFACE -j SNAT --to-source $ADDRESS
-A POSTROUTING -o $IFACE -j SNAT --to-source $ADDRESS
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -i $IFACE -p udp -m state --state NEW -m udp --dport $PORT -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT
IPTABLES
createfile 'Easy-RSA configuration' "${EASY_RSA}/vars" <<EASYRSA
export EASY_RSA="$EASY_RSA"
export OPENSSL="openssl"
export PKCS11TOOL="pkcs11-tool"
export GREP="grep"
export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
export KEY_DIR="$KEYS_DIR"
export PKCS11_MODULE_PATH="dummy"
export PKCS11_PIN="dummy"
export KEY_SIZE=2048
export CA_EXPIRE=3650
export KEY_EXPIRE=3650
export KEY_COUNTRY="GB"
export KEY_PROVINCE="DEVON"
export KEY_CITY="PLYMOUTH"
export KEY_ORG="Land Registry"
export KEY_EMAIL="webops@digital.landregistry.gov.uk"
export KEY_OU="IT Operations"
export KEY_NAME="server"
export KEY_CN="$(hostname)"
EASYRSA
createfile 'Client OpenVPN configuration' '/etc/openvpn/client.ovpn' <<CLIENT
client
dev tun
proto udp
remote $ADDRESS $PORT
resolv-retry infinite
remote-cert-tls server
ns-cert-type server
auth-nocache
auth-user-pass
nobind
persist-key
persist-tun
comp-lzo
verb 3
ca ca.crt
cert client.crt
key client.key
cipher AES-256-CBC
CLIENT
say 'Configuring kernel parameters...' | output
createfile 'Kernel parameters' '/etc/sysctl.d/openvpn' <<KERNEL
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
KERNEL
sysctl -p
onfail 'Unable to set runtime parameters' 'Runtime parameters set'
################################################################################
################################### RUNTIME ####################################
################################################################################
say 'Creating authentication certificates...' | output
source $EASY_RSA/vars
onfail "Failed to load Easy-RSA variables from ${EASY_RSA}/vars!"
bash $EASY_RSA/clean-all | output
onfail "Failed to clean up ${KEYS_DIR}!"
echo 'Creating Diffie Hellman key... (this may take some time)' | output
bash $EASY_RSA/build-dh 2>/dev/null | output
onfail 'Failed to create Diffie Hellman key!'
echo 'Creating Certificate Authority...' | output
bash $EASY_RSA/build-ca --batch 2>/dev/null | output
onfail 'Failed to create Certificate Authority!'
echo 'Creating server-side certificate...' | output
bash $EASY_RSA/build-key-server --batch server 2>/dev/null | output
onfail 'Failed to create server certificate!'
echo 'Creating client-side certificate...' | output
KEY_NAME="client" bash $EASY_RSA/build-key --batch client 2>/dev/null | output
onfail 'Failed to create client certificate!'
restorecon -R ${KEYS_DIR}
onfail "Unable to set SELinnux context labels for ${KEYS_DIR}"
say 'Reconfiguring SystemD network services...' | output
systemctl stop NetworkManager.service 2>&1 | output
onfail 'Failed to stop NetworkManager.service!'
systemctl stop firewalld.service 2>&1 | output
onfail 'Failed to stop firewalld.service!'
systemctl start iptables.service 2>&1 | output
onfail 'Failed to start iptables.service'
systemctl restart network.service 2>&1 | output
onfail 'Failed to restart network.service'
systemctl enable iptables.service >/dev/null 2>&1
systemctl disable firewalld.service >/dev/null 2>&1
systemctl disable NetworkManager.service >/dev/null 2>&1
say 'Starting OpenVPN service...' | output
systemctl stop openvpn@server.service 2>&1 | output
systemctl start openvpn@server.service 2>&1 | output
onfail 'Failed to start OpenVPN service'
systemctl enable openvpn@server.service >/dev/null 2>&1
say 'Packaging client configuration...' | output
TMP_DIR="/tmp/$(openssl rand -hex 6)"
mkdir -p $TMP_DIR
onfail "Unable to create temporary directory at $TMP_DIR"
cp $KEYS_DIR/{ca.crt,client.crt,client.key} $TMP_DIR
onfail "Unable to copy necessary certificates to $TMP_DIR"
cp /etc/openvpn/client.ovpn $TMP_DIR
onfail "Unable to copy client configuration to $TMP_DIR"
tar -C $TMP_DIR -caf $CLIENT_TAR .
onfail "Unable to create compressed archive at $CLIENT_TAR"
rm -rf $TMP_DIR
echo "Client configuration can be found at $CLIENT_TAR" | output
[[ -d '/vagrant' ]] && cp -f $CLIENT_TAR /vagrant
say 'Installation complete!' | output SUCCESS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment