2016-10-04 17:46:14 +00:00
#!/bin/bash
# Create OVPN Client
# Default Variable Declarations
DEFAULT = "Default.txt"
FILEEXT = ".ovpn"
CRT = ".crt"
2016-12-06 15:56:51 +00:00
KEY = ".key"
2016-10-04 17:46:14 +00:00
CA = "ca.crt"
TA = "ta.key"
2016-12-06 15:56:51 +00:00
INDEX = "/etc/openvpn/easy-rsa/pki/index.txt"
2016-04-19 18:01:55 +00:00
INSTALL_USER = $( cat /etc/pivpn/INSTALL_USER)
2017-03-14 11:49:25 +00:00
helpFunc( ) {
2017-03-14 13:36:12 +00:00
echo "::: Create a client ovpn profile, optional nopass"
echo ":::"
2019-07-23 20:12:35 +00:00
echo "::: Usage: pivpn <-a|add> [-b|--bitwarden] [-n|--name <arg>] [-p|--password <arg>]|[nopass] [-d|--days <number>] [-h|--help]"
2017-03-14 13:36:12 +00:00
echo ":::"
echo "::: Commands:"
echo "::: [none] Interactive mode"
echo "::: nopass Create a client without a password"
2019-07-23 20:12:35 +00:00
echo "::: -b,--bitwarden Create and save a client through Bitwarden"
2019-05-08 11:01:56 +00:00
echo "::: -d,--days Expire the certificate after specified number of days (default: 1080)"
2017-03-14 13:36:12 +00:00
echo "::: -n,--name Name for the Client (default: '" $( hostname) "')"
echo "::: -p,--password Password for the Client (no default)"
echo "::: -h,--help Show this help dialog"
2017-03-14 11:49:25 +00:00
}
# Parse input arguments
while test $# -gt 0
do
2017-03-14 13:36:12 +00:00
_key = " $1 "
case " $_key " in
-n| --name| --name= *)
_val = " ${ _key ##--name= } "
if test " $_val " = " $_key "
then
test $# -lt 2 && echo " Missing value for the optional argument ' $_key '. " && exit 1
_val = " $2 "
shift
fi
NAME = " $_val "
; ;
-p| --password| --password= *)
_val = " ${ _key ##--password= } "
if test " $_val " = " $_key "
then
test $# -lt 2 && echo " Missing value for the optional argument ' $_key '. " && exit 1
_val = " $2 "
shift
fi
PASSWD = " $_val "
; ;
2019-05-08 11:01:56 +00:00
-d| --days| --days= *)
_val = " ${ _key ##--days= } "
if test " $_val " = " $_key "
then
test $# -lt 2 && echo " Missing value for the optional argument ' $_key '. " && exit 1
_val = " $2 "
shift
fi
DAYS = " $_val "
; ;
2017-03-14 13:36:12 +00:00
-h| --help)
helpFunc
exit 0
; ;
nopass)
NO_PASS = "1"
; ;
2019-07-23 20:12:35 +00:00
-b| --bitwarden)
BITWARDEN = "2"
; ;
2017-03-14 13:36:12 +00:00
*)
2018-02-15 09:14:03 +00:00
echo " Error: Got an unexpected argument ' $1 ' "
2017-03-14 13:36:12 +00:00
helpFunc
exit 1
; ;
esac
shift
2017-03-14 11:49:25 +00:00
done
2016-05-01 03:37:27 +00:00
# Functions def
2016-04-30 17:28:01 +00:00
2016-05-01 03:37:27 +00:00
function keynoPASS( ) {
2016-04-30 17:28:01 +00:00
2016-05-01 03:37:27 +00:00
#Build the client key
expect << EOF
2016-10-09 11:34:17 +00:00
set timeout -1
2019-05-08 11:01:56 +00:00
set env( EASYRSA_CERT_EXPIRE) " ${ DAYS } "
2016-12-11 18:36:14 +00:00
spawn ./easyrsa build-client-full " ${ NAME } " nopass
2016-05-01 03:37:27 +00:00
expect eof
EOF
2016-12-06 15:56:51 +00:00
cd pki || exit
2016-05-01 03:37:27 +00:00
}
2019-07-23 20:12:35 +00:00
function useBitwarden( ) {
# login and unlock vault
printf "****Bitwarden Login****"
printf "\n"
SESSION_KEY = ` bw login --raw`
export BW_SESSION = $SESSION_KEY
printf "Successfully Logged in!"
printf "\n"
# ask user for username
printf "Enter the username: "
read -r NAME
# check name
until [ [ " $NAME " = ~ ^[ a-zA-Z0-9.@_-] +$ && ${ NAME : : 1 } != "." && ${ NAME : : 1 } != "-" ] ]
do
echo "Name can only contain alphanumeric characters and these characters (.-@_). The name also cannot start with a dot (.) or a dash (-). Please try again."
# ask user for username again
printf "Enter the username: "
read -r NAME
done
# ask user for length of password
printf "Please enter the length of characters you want your password to be (minimum 12): "
read -r LENGTH
# check length
until [ [ " $LENGTH " -gt 11 && " $LENGTH " -lt 129 ] ]
do
echo "Password must be between from 12 to 128 characters, please try again."
# ask user for length of password
printf "Enter the length of characters you want your password to be (minimum 12): "
read -r LENGTH
done
printf "Creating a PiVPN item for your vault..."
printf "\n"
# create a new item for your PiVPN Password
PASSWD = ` bw generate -usln --length $LENGTH `
bw get template item | jq '.login.type = "1"' | jq '.name = "PiVPN"' | jq -r --arg NAME " $NAME " '.login.username = $NAME' | jq -r --arg PASSWD " $PASSWD " '.login.password = $PASSWD' | bw encode | bw create item
bw logout
}
2016-05-01 03:37:27 +00:00
function keyPASS( ) {
2016-04-30 17:28:01 +00:00
2016-11-12 02:55:47 +00:00
if [ [ -z " ${ PASSWD } " ] ] ; then
2017-03-14 11:49:25 +00:00
stty -echo
while true
do
printf "Enter the password for the client: "
read -r PASSWD
printf "\n"
printf "Enter the password again to verify: "
read -r PASSWD2
printf "\n"
[ " ${ PASSWD } " = " ${ PASSWD2 } " ] && break
printf "Passwords do not match! Please try again.\n"
done
stty echo
if [ [ -z " ${ PASSWD } " ] ] ; then
echo "You left the password blank"
echo "If you don't want a password, please run:"
echo "pivpn add nopass"
exit 1
fi
2017-03-14 13:36:12 +00:00
fi
2016-10-04 17:46:14 +00:00
if [ ${# PASSWD } -lt 4 ] || [ ${# PASSWD } -gt 1024 ]
then
echo "Password must be between from 4 to 1024 characters"
exit 1
fi
2016-05-01 03:37:27 +00:00
2016-11-11 22:45:48 +00:00
#Escape chars in PASSWD
2016-11-12 02:55:47 +00:00
PASSWD = $( echo -n ${ PASSWD } | sed -e 's/\\/\\\\/g' -e 's/\//\\\//g' -e 's/\$/\\\$/g' -e 's/!/\\!/g' -e 's/\./\\\./g' -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/\*/\\\*/g' -e 's/\@/\\\@/g' -e 's/\#/\\\#/g' -e 's/£/\\£/g' -e 's/%/\\%/g' -e 's/\^/\\\^/g' -e 's/\&/\\\&/g' -e 's/(/\\(/g' -e 's/)/\\)/g' -e 's/-/\\-/g' -e 's/_/\\_/g' -e 's/\+/\\\+/g' -e 's/=/\\=/g' -e 's/\[/\\\[/g' -e 's/\]/\\\]/g' -e 's/;/\\;/g' -e 's/:/\\:/g' -e 's/|/\\|/g' -e 's/</\\</g' -e 's/>/\\>/g' -e 's/,/\\,/g' -e 's/?/\\?/g' -e 's/~/\\~/g' -e 's/{/\\{/g' -e 's/}/\\}/g' )
2016-11-11 22:45:48 +00:00
2016-05-01 03:37:27 +00:00
#Build the client key and then encrypt the key
expect << EOF
2016-10-09 11:34:17 +00:00
set timeout -1
2019-05-08 11:01:56 +00:00
set env( EASYRSA_CERT_EXPIRE) " ${ DAYS } "
2016-12-11 18:36:14 +00:00
spawn ./easyrsa build-client-full " ${ NAME } "
2018-10-11 21:58:29 +00:00
expect "Enter PEM pass phrase" { send -- " ${ PASSWD } \r " }
expect "Verifying - Enter PEM pass phrase" { send -- " ${ PASSWD } \r " }
2016-05-01 03:37:27 +00:00
expect eof
2016-04-30 17:28:01 +00:00
EOF
2016-12-06 15:56:51 +00:00
cd pki || exit
2016-04-30 17:28:01 +00:00
2016-05-01 03:37:27 +00:00
}
2019-07-23 20:12:35 +00:00
# bitWarden first
if [ [ " ${ BITWARDEN } " = ~ "2" ] ] ; then
useBitwarden
fi
2017-03-14 11:49:25 +00:00
if [ -z " ${ NAME } " ] ; then
printf "Enter a Name for the Client: "
read -r NAME
fi
2016-10-04 17:46:14 +00:00
2019-05-08 11:01:56 +00:00
if [ [ ${ NAME : : 1 } = = "." ] ] || [ [ ${ NAME : : 1 } = = "-" ] ] ; then
echo "Names cannot start with a dot (.) or a dash (-)."
exit 1
fi
2019-07-23 20:12:35 +00:00
if [ [ " ${ NAME } " = ~ [ ^a-zA-Z0-9.@_-] ] ] ; then
2019-05-08 11:01:56 +00:00
echo "Name can only contain alphanumeric characters and these characters (.-@_)."
2016-10-04 17:46:14 +00:00
exit 1
fi
2016-05-01 03:37:27 +00:00
2016-12-06 15:56:51 +00:00
if [ [ -z " ${ NAME } " ] ] ; then
echo "You cannot leave the name blank."
2016-05-06 01:04:57 +00:00
exit 1
fi
2016-10-04 18:54:09 +00:00
# Check if name is already in use
2017-01-28 01:36:53 +00:00
while read -r line || [ -n " ${ line } " ] ; do
STATUS = $( echo " $line " | awk '{print $1}' )
2017-02-05 19:30:31 +00:00
if [ " ${ STATUS } " = = "V" ] ; then
2017-01-28 01:36:53 +00:00
CERT = $( echo " $line " | sed -e 's:.*/CN=::' )
if [ " ${ CERT } " = = " ${ NAME } " ] ; then
INUSE = "1"
2017-02-05 19:30:31 +00:00
break
2017-01-28 01:36:53 +00:00
fi
2016-10-04 18:54:09 +00:00
fi
2016-12-06 15:56:51 +00:00
done <${ INDEX }
2016-10-04 18:54:09 +00:00
2017-01-28 01:36:53 +00:00
if [ " ${ INUSE } " = = "1" ] ; then
printf "\n!! This name is already in use by a Valid Certificate."
printf "\nPlease choose another name or revoke this certificate first.\n"
exit 1
fi
2016-10-04 18:54:09 +00:00
# Check if name is reserved
2016-12-06 15:56:51 +00:00
if [ " ${ NAME } " = = "ta" ] || [ " ${ NAME } " = = "server" ] || [ " ${ NAME } " = = "ca" ] ; then
echo "Sorry, this is in use by the server and cannot be used by clients."
2016-10-04 18:54:09 +00:00
exit 1
fi
2019-05-08 11:01:56 +00:00
#As of EasyRSA 3.0.6, by default certificates last 1080 days, see https://github.com/OpenVPN/easy-rsa/blob/6b7b6bf1f0d3c9362b5618ad18c66677351cacd1/easyrsa3/vars.example
if [ -z " ${ DAYS } " ] ; then
read -r -e -p "How many days should the certificate last? " -i 1080 DAYS
2019-05-10 10:53:52 +00:00
fi
if [ [ ! " $DAYS " = ~ ^[ 0-9] +$ ] ] || [ " $DAYS " -lt 1 ] || [ " $DAYS " -gt 3650 ] ; then
2019-05-08 11:01:56 +00:00
#The CRL lasts 3650 days so it doesn't make much sense that certificates would last longer
echo "Please input a valid number of days, between 1 and 3650 inclusive."
exit 1
2019-05-10 10:53:52 +00:00
2019-05-08 11:01:56 +00:00
fi
2016-10-04 17:46:14 +00:00
cd /etc/openvpn/easy-rsa || exit
2016-05-01 03:37:27 +00:00
2017-03-14 11:49:25 +00:00
if [ [ " ${ NO_PASS } " = ~ "1" ] ] ; then
2017-03-16 09:25:17 +00:00
if [ [ -n " ${ PASSWD } " ] ] ; then
echo "Both nopass and password arguments passed to the script. Please use either one."
exit 1
else
keynoPASS
2017-03-16 09:28:50 +00:00
fi
2016-05-01 03:37:27 +00:00
else
keyPASS
fi
2016-10-04 17:46:14 +00:00
#1st Verify that clients Public Key Exists
2016-12-06 15:56:51 +00:00
if [ ! -f " issued/ ${ NAME } ${ CRT } " ] ; then
2016-10-04 17:46:14 +00:00
echo " [ERROR]: Client Public Key Certificate not found: $NAME $CRT "
exit
fi
echo " Client's cert found: $NAME $CRT "
#Then, verify that there is a private key for that client
2016-12-06 15:56:51 +00:00
if [ ! -f " private/ ${ NAME } ${ KEY } " ] ; then
echo " [ERROR]: Client Private Key not found: $NAME $KEY "
2016-10-04 17:46:14 +00:00
exit
fi
2016-04-19 18:01:55 +00:00
echo " Client's Private Key found: $NAME $KEY "
2016-10-04 17:46:14 +00:00
#Confirm the CA public key exists
2016-12-06 15:56:51 +00:00
if [ ! -f " ${ CA } " ] ; then
2016-10-04 17:46:14 +00:00
echo " [ERROR]: CA Public Key not found: $CA "
exit
fi
echo " CA public Key found: $CA "
2019-08-06 08:02:28 +00:00
#Confirm the tls key file exists
2016-12-06 15:56:51 +00:00
if [ ! -f " ${ TA } " ] ; then
2019-08-06 08:02:28 +00:00
echo " [ERROR]: tls Private Key not found: $TA "
2016-10-04 17:46:14 +00:00
exit
fi
2019-08-06 08:02:28 +00:00
echo " tls Private Key found: $TA "
2016-10-04 17:46:14 +00:00
#Ready to make a new .ovpn file
{
# Start by populating with the default file
2016-12-06 15:56:51 +00:00
cat " ${ DEFAULT } "
2016-10-04 17:46:14 +00:00
#Now, append the CA Public Cert
echo "<ca>"
2016-12-06 15:56:51 +00:00
cat " ${ CA } "
2016-10-04 17:46:14 +00:00
echo "</ca>"
#Next append the client Public Cert
echo "<cert>"
2016-12-06 15:56:51 +00:00
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < " issued/ ${ NAME } ${ CRT } "
2016-10-04 17:46:14 +00:00
echo "</cert>"
#Then, append the client Private Key
echo "<key>"
2016-12-06 15:56:51 +00:00
cat " private/ ${ NAME } ${ KEY } "
2016-10-04 17:46:14 +00:00
echo "</key>"
2019-08-06 08:02:28 +00:00
#Finally, append the tls Private Key
2018-04-02 10:07:58 +00:00
if [ -f /etc/pivpn/TWO_POINT_FOUR ] ; then
echo "<tls-crypt>"
cat " ${ TA } "
echo "</tls-crypt>"
else
echo "<tls-auth>"
cat " ${ TA } "
echo "</tls-auth>"
fi
2018-02-15 09:14:03 +00:00
2016-12-06 15:56:51 +00:00
} > " ${ NAME } ${ FILEEXT } "
2016-04-19 18:01:55 +00:00
2019-08-06 08:02:28 +00:00
if [ ! -d " /home/ $INSTALL_USER /ovpns " ] ; then
mkdir " /home/ $INSTALL_USER /ovpns "
chmod 0777 -R " /home/ $INSTALL_USER /ovpns "
fi
2019-08-20 16:36:05 +00:00
# If user is using Bitwarden, have them login again to submit their .ovpn file to their vault
printf "Would you like to export your .ovpn file to your Bitwarden vault? (y or n)"
read -r RESPONSE
if [ $RESPONSE = = "y" ] || [ $RESPONSE = = "Y" ] ; then
$OVPN_FILE = " $( < " /etc/openvpn/easy-rsa/pki/ $NAME $FILEEXT " ) "
# Login to Bitwarden
printf "****Bitwarden Login****"
printf "\n"
SESSION_KEY = ` bw login --raw`
export BW_SESSION = $SESSION_KEY
printf "Successfully Logged in!"
printf "\n"
# Create a Bitwarden secure note to export the .ovpn file
bw get template item | jq '.name = "PiVPN OVPN File"' | jq '.type = 2' | jq -r --arg VAL " $OVPN_FILE " '.notes = $VAL' | jq " .secureNote = $( bw get template item.secureNote) " | bw encode | bw create item
bw logout
exit
fi
2016-04-19 18:01:55 +00:00
# Copy the .ovpn profile to the home directory for convenient remote access
2016-12-06 15:56:51 +00:00
cp " /etc/openvpn/easy-rsa/pki/ $NAME $FILEEXT " " /home/ $INSTALL_USER /ovpns/ $NAME $FILEEXT "
2016-10-04 17:46:14 +00:00
chown " $INSTALL_USER " " /home/ $INSTALL_USER /ovpns/ $NAME $FILEEXT "
2018-12-24 13:24:20 +00:00
chmod o-r " /etc/openvpn/easy-rsa/pki/ $NAME $FILEEXT "
chmod o-r " /home/ $INSTALL_USER /ovpns/ $NAME $FILEEXT "
2016-04-30 17:28:01 +00:00
printf "\n\n"
printf "========================================================\n"
2016-10-04 17:46:14 +00:00
printf "\e[1mDone! %s successfully created!\e[0m \n" " $NAME $FILEEXT "
printf "%s was copied to:\n" " $NAME $FILEEXT "
printf " /home/%s/ovpns\n" " $INSTALL_USER "
2017-09-22 19:11:23 +00:00
printf "for easy transfer. Please use this profile only on one\n"
printf "device and create additional profiles for other devices.\n"
2016-04-30 17:28:01 +00:00
printf "========================================================\n\n"