Now that OpenVPN was all set up, the only thing left to do was the Automation. The script that I created, takes care of the certificate/key creation of the users, the configuration customization, the configuration delivery, the Certificate Revoke List creation and configuration updates. Since this article is almost exclusively about one script, I will first loose few words about each of the main functions and post the entire script afterwards.
Getting the VPN users
One of the more important parts of the script is getting the list of VPN users. The function uses winbind to get the users in the VPN group in the Active Directory and cuts the output into a comma separated list. For this to work, the computer needs to be a member of the Active Directory. The function changes a variable if winbind fails or the list of users is empty. The variable will be checked in the script after the function is used. The Administrator gets notified by E-Mail in case of an error. In order for the E-Mail part to work, you need to configure the MTA on the System. On my server Exim4 had trouble resolving some aliases on my Mail Server, so i replaced it with SSMTP.
function func_VPN_get_user_list { VPN_user_list="" VPN_user_list=$(wbinfo --group-info $VPN_AD_Group | cut -d ':' -f 4) #output is a comma seperated list of VPN Users if [ -z "$VPN_user_list" ] then VPN_ERR=1 echo -e 'There is a problem with the VPN script -- user list is empty' | mutt -s "ERROR the OpenVPN user script encountered a problem" --openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script >> $VPN_userscript_log fi }
Creating the individual configuration files
This function is called when a new VPN user is found and the configuration files for all users are updated. It copies every ovpn file in the template folder to the users folder and appends all necessary certificates and keys. After creating the files, it sends a welcome E-Mail to the user with the configuration files as attachment. The E-Mails will be sent to the address “username [at] domainname [dot] tlâ. So if your E-Mail Addresses have another naming scheme this needs to be changed. Furthermore E-Mail is only a reasonably save way to deliver these files, if the Mail Server is on your local network and all user Access is only possible through encrypted connections.
function func_VPN_create_config { #This function creates the OpenVPN configuration files for every user. #The user specific configuration files are created from nomal OpenVPN configs and get all necesarry certificates and keys appended. #After this all files are E-Mailed to the user. E-Mail might not be a good way to distribute these files in every environment. VPN_Mailattach="" VPN_config_usr=$1 for VPN_templateloc in ${VPN_template_dir}/*.ovpn; do if [ ! "$(ls -A ${VPN_template_dir}/*.ovpn)" ] then #check if there are actually any templates echo ERROR! $(date +"%d.%m.%Y %T") there are no templates in the directory $VPN_temp_dir >> $VPN_userscript_log echo ERROR! $(date +"%d.%m.%Y %T") there are no templates in the directory $VPN_temp_dir exit else #use ${VPN_templateloc##*/} to get just the filename of the template echo creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} >> $VPN_userscript_log echo creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} echo VPN user: ${VPN_config_usr} echo Client dir: ${VPN_client_dir} echo ----------- echo copieing to ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo ----------- cp $VPN_templateloc ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat $VPN_CA_CERT >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #While many webgui solutions i used had this in the client config, OpenVPN doku says its only needed on the server side #echo>> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #cat $VPN_DH_KEY >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #echo >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat $VPN_TLS_KEY >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}.crt >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}.key >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo finished creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} >> $VPN_userscript_log fi #list all Config loacations in a variable, so we wont have to retrieve them again later on export VPN_Mailattach="$VPN_Mailattach -a ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/}" done echo mailattach outside: $VPN_Mailattach echo $(date +"%d.%m.%Y %T") done creating files for user ${VPN_config_usr} >> $VPN_userscript_log #Send all config files to the users E-Mail address #This assumes the user has a Mail-Account as username@$AD_NAME #mutt requires you to have fully configured MTA on the Server this script is running on #the MTA on my server is limited to sending Mails to my local mail server and does not accept incoming mail echo -e 'Hello '${VPN_config_usr}',\n Copy the desired config file to the OpenVPN config folder. \n For WinXP and Vista/Win7 32 Bit this is:"C:\\Program Files\\OpenVPN\\config"\n For Win Vista/Win7 64Bit this is:"C:\\Program Files(x86)\\OpenVPN\\config"\n If you do not know which config to choose, check the internal section of https://www.skelleton.net \n This file contains your private VPN key, please store it safely!' | mutt -s "Your new OpenVPN config files for ${AD_NAME}"$VPN_Mailattach -- ${VPN_config_usr}@$AD_NAME }
Adding new users
This function uses the list of users in the vpn group and matches it against the folder names in the client directory. If there is a user name, that does not match any folder name in there, the folder will be created. After that the key and certificate for the user will be created with OpenSSL and once that is done, the function to create the configuration files is called.
function func_VPN_new_users { #This function checks if a Client config directory exists for every user in the group. #If not create the dir, certificates and configs. #The function func_VPN_get_user_list has to be run before this function if [ -z "$VPN_user_list" ] then VPN_ERR=1 #echo -e 'There is a problem with the VPN script -- user list is empty' | mutt -s "ERROR the OpenVPN user script encountered a problem" --openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script >> $VPN_userscript_log echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script fi echo $(date +"%d.%m.%Y %T") checking for new users #input for the for loop is a comma seperated list, so we have to tell it, that the delim is "," for i in ${VPN_user_list//,/ }; do echo "Checking if ccd for $i" exists #check if the user directory already exists, create it if not else move on echo ${VPN_client_dir}/$i if [ ! -d "${VPN_client_dir}/${i}" ] then echo $(date +"%d.%m.%Y %T") Found new VPN user $i creating key certs and configs.... >> $VPN_userscript_log echo $(date +"%d.%m.%Y %T") Found new VPN user $i creating key certs and configs.... mkdir "${VPN_client_dir}/${i}" #create the key and cert for the user after creating his dir #the -nodes in the request specifies, that the key will not be encryptes and thus will not require a password #dont use - nodes if you do not have another form of authentication in your OpenVPN #Batch mode is important for Automated Scripts #it would probably be enough to state only cn and email in the subject, since the other values are also the defaults in my ssl config #openssl req -nodes -new -keyout ${VPN_client_dir}/${i}/${i}.key -out ${VPN_client_dir}/${i}/${i}_csr.pem -rand $VPN_RANDFILE 4096 -subj"/C=DE/ST=Skelletonia/O=skelleton.net/OU=OpenVPN_Users/CN=${i}/emailAddress=${i}@${AD_NAME}" -batch -config $KEY_CONFIG echo creating certificate request and key openssl req -nodes -new -keyout ${VPN_client_dir}/${i}/${i}.key -out ${VPN_client_dir}/${i}/${i}_csr.pem -rand $VPN_RANDFILE -subj "/C=DE/ST=Skelletonia/O=skelleton.net/OU=OpenVPN_Users/CN=${i}/emailAddress=${i}@${AD_NAME}" -batch -config $KEY_CONFIG echo signing request openssl ca -out ${VPN_client_dir}/${i}/${i}.crt -in ${VPN_client_dir}/${i}/${i}_csr.pem -batch -config $KEY_CONFIG chmod 0600 ${VPN_client_dir}/${i}/${i}.key #build the config file(s) for all templates found in $VPN_template_dir func_VPN_create_config $i #notify the OpenVPN admin echo -e 'New user added to OpenVPN' | mutt -s "The user $i has been added to OpenVPN" -- openvpn@$AD_NAME elif [ -d "${VPN_client_dir}/${i}" ] then echo directory ${VPN_client_dir}/${i} exists else echo check if directory ${VPN_client_dir}/${i} exists failed fi done func_VPN__archive_certs echo $(date +"%d.%m.%Y %T") done checking for new users }
Revoking certificates and creating the Certificate Revoke List
This function checks every folder in the client directory for a corresponding user in the OpenVPN group. If none is found, the certificate will be revoked and a new certificate revoke list will be created at the end of the check. A new certificate revoke list will also be created if the current CRL is too old.
function func_VPN_revoke { #This function revokes the certificates for all users, who are not in the AD group anymore echo $(date +"%d.%m.%Y %T") checking if certificates need to be revoked NEW_CRL=0 for VPN_ccd in ${VPN_client_dir}/*; do VPN_ccd=${VPN_ccd%*/} #if [ ! ${VPN_ccd##*/} in $VPN_user_list ]; then if echo $VPN_user_list | grep -q ${VPN_ccd##*/} then echo ${VPN_ccd##*/} is in list: $VPN_user_list else NEW_CRL=1 echo revoking ${VPN_client_dir}/${VPN_ccd##*/}/${VPN_ccd##*/}.crt echo userlist $VPN_user_list openssl ca -revoke ${VPN_client_dir}/${VPN_ccd##*/}/${VPN_ccd##*/}.crt -batch -config $KEY_CONFIG rm -R $VPN_ccd echo -e "Hello ${VPN_ccd##*/}, your access to skelleton.net OpenVPN has been revoked. This is an automated message. If you believe there has been a mistake contact openvpn@${AD_NAME}" | mutt -s "OpenVPN access has been revoked." -- ${VPN_ccd##*/}@$AD_NAME echo $(date +"%d.%m.%Y %T") $Vpn_ccd has been deleted and user certificates have been revoked, because the user is no longer in the Active Directory group echo $(date +"%d.%m.%Y %T") $Vpn_ccd has been deleted and user certificates have been revoked, because the user is no longer in the Active Directory group >> $VPN_userscript_log fi done #create CRL if a certificate was revoked or if the current CRL is about to expire echo NEW_CRL: $NEW_CRL if [ $NEW_CRL == 1 ] then echo certificate revoked creating new crl func_VPN_create_CRL elif [ -e "$(find $CRLPATH -mmin +5)" ] then echo crl to old creating new one func_VPN_create_CRL fi echo $(date +"%d.%m.%Y %T") done checking if certificates need to be revoked }
Updating the configuration files
This function calls the function to create configuration files for all users. In the script it is only called if the file ânew_configs.txtâ can be found in the template directory.
function func_VPN_update_configs { #This function creates new config files for all users. It assumes that all current users have a client config dir echo $(date +"%d.%m.%Y %T") creating new configs for all users for VPN_ccd in ${VPN_client_dir}/*; do func_VPN_create_config ${VPN_ccd##*/} done echo -e "All client configurations for server $VPN_NAME have been updated and distributed" | mutt -s "OpenVPN configuration files updated for $VPN_NAME " -- openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") done updating configs echo $(date +"%d.%m.%Y %T") updated all user configuration files >> $VPN_userscript_log }
Preparing the E-Mail System (mutt and ssmtp)
At first I used Exim4 as MTA, but it had some problems with resolving some of my mail aliases. Because of this i switched to ssmtp for E-Mail delivery. Ssmtp is not a full featured MTA, it can not deliver mail to the local system. Since I only need to send mail to my Mail Server, it is perfect. The configuration is done in one file and very straight forward.
I use Mutt as a mail client, because it offers all the functions I need and it is preinstalled on Debian.
And finally the complete script
#!/bin/bash # This Script querys the members of an AD group and performs actions for each member # The computer has to be a Member of the Active Directory since winbind is used # This Scrip assumes, that you use an Active Directory Group for your OpenVPN authorisation #Fixed Variables VPN_AD_Group=OpenVPN_Users VPN_NAME=openvpn.skelleton.net VPN_dir="/etc/openvpn/${VPN_NAME}" VPN_client_dir="${VPN_dir}/clients" VPN_template_dir="${VPN_dir}/templates" VPN_userscript_log="${VPN_dir}/logs/VPN_userscript.log" VPN_UPDATE_ALL="$VPN_template_dir/new_configs.txt" KEY_CONFIG="${VPN_dir}/CA_OpenVPN_skelleton.net/skelleton.net_ca_openvpn.cnf" VPN_CA_CERT="${VPN_dir}/cacert_root.pem" VPN_DH_KEY="${VPN_dir}/DH4096.pem" VPN_TLS_KEY="${VPN_dir}/private/ta.key" AD_NAME=skelleton.net VPN_RANDFILE="${VPN_dir}/CA_OpenVPN_skelleton.net/private/.rand" CRLPATH="${VPN_dir}/CA_OpenVPN_skelleton.net/crl_OpenVPN.pem" VPN_ERR=0 CA_newcerts_dir="${VPN_dir}/CA_OpenVPN_skelleton.net/newcerts" CA_certs_dir="${VPN_dir}/CA_OpenVPN_skelleton.net/certs" #Define Functions function func_VPN__archive_certs { #this function archives issued certificates by moving them from the newcerts to certs and and link the hash value as index echo $(date +"%d.%m.%Y %T") Starting Archiving of certificates in the directory $CA_newcerts_dir >> $VPN_userscript_log if [ ! "$(ls -A ${CA_newcerts_dir}/*.pem)" ]; then #check if there are actually any certs in the newcerts folder echo no new certificates echo $(date +"%d.%m.%Y %T") Ending Archiving there are no new certificates in the directory $CA_newcerts_dir >> $VPN_userscript_log else for CertSN in ${CA_newcerts_dir}/*.pem; do echo certsn: $CertSN echo moving $certSN to ${CA_certs_dir}/${CertSN##*/} mv $CertSN ${CA_certs_dir}/${CertSN##*/} || echo $(date +"%d.%m.%Y %T") ERROR_ARC while moving certificate $CERTSN into $CA_newcerts_dir >> $VPN_userscript_log #The Number at the end should increase, if the cert exists already -> cert has been reissued indexfile=$(openssl x509 -hash -noout -in ${CA_certs_dir}/${CertSN##*/}) if [ ! -e ${CA_certs_dir}/${indexfile}.0 ]; then echo the certificate has not been issued before creating link ${indexfile}.0 ln -s ${CA_certs_dir}/${CertSN##*/} ${CA_certs_dir}/${indexfile}.0 && echo $(date +"%d.%m.%Y %T") Archived Certificate ${CertSN##*/} >> $VPN_userscript_log || echo $(date +"%d.%m.%Y %T") ERROR_ARC error while creating symlink for certificate ${CertSN##*/} >> $cert_archiver_log else echo certificate has been issued before incrementing index echo $(date +"%d.%m.%Y %T") Certificate ${CertSN##*/} has been reissued, incermenting index >> $VPN_userscript_log i=0 success=0 while [ ! $success == 1 ] do i=$((i + 1)) echo check if ${indexfile}.$i exists if [ ! -e ${CA_certs_dir}/${indexfile}.$i ]; then ln -s ${CA_certs_dir}/${CertSN##*/} ${CA_certs_dir}/${indexfile}.$i && echo $(date +"%d.%m.%Y %T") Archived Certificate ${CertSN##*/} >> $VPN_userscript_log || echo $(date +"%d.%m.%Y %T") ERROR_ARC error while creating symlink for certificate ${CertSN##*/} >> $VPN_userscript_log success=1 fi done fi done; fi echo $(date +"%d.%m.%Y %T") Ending Archiving >> $VPN_userscript_log } function func_VPN_get_user_list { VPN_user_list="" VPN_user_list=$(wbinfo --group-info $VPN_AD_Group | cut -d ':' -f 4) #output is a comma seperated list of VPN Users if [ -z "$VPN_user_list" ] then VPN_ERR=1 echo -e 'There is a problem with the VPN script -- user list is empty' | mutt -s "ERROR the OpenVPN user script encountered a problem" --openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script >> $VPN_userscript_log fi } function func_VPN_create_config { #This function creates the OpenVPN configuration files for every user. #The user specific configuration files are created from nomal OpenVPN configs and get all necesarry certificates and keys appended. #After this all files are E-Mailed to the user. E-Mail might not be a good way to distribute these files in every environment. VPN_Mailattach="" VPN_config_usr=$1 for VPN_templateloc in ${VPN_template_dir}/*.ovpn; do if [ ! "$(ls -A ${VPN_template_dir}/*.ovpn)" ] then #check if there are actually any templates echo ERROR! $(date +"%d.%m.%Y %T") there are no templates in the directory $VPN_temp_dir >> $VPN_userscript_log echo ERROR! $(date +"%d.%m.%Y %T") there are no templates in the directory $VPN_temp_dir exit else #use ${VPN_templateloc##*/} to get just the filename of the template echo creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} >> $VPN_userscript_log echo creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} echo VPN user: ${VPN_config_usr} echo Client dir: ${VPN_client_dir} echo ----------- echo copieing to ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo ----------- cp $VPN_templateloc ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat $VPN_CA_CERT >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #While many webgui solutions i used had this in the client config, OpenVPN doku says its only needed on the server side #echo>> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #cat $VPN_DH_KEY >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} #echo >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat $VPN_TLS_KEY >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}.crt >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo "" >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} cat ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}.key >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo " " >> ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/} echo finished creating configuration file ${VPN_config_usr}_${VPN_templateloc##*/} >> $VPN_userscript_log fi #list all Config loacations in a variable, so we wont have to retrieve them again later on export VPN_Mailattach="$VPN_Mailattach -a ${VPN_client_dir}/${VPN_config_usr}/${VPN_config_usr}_${VPN_templateloc##*/}" done echo mailattach outside: $VPN_Mailattach echo $(date +"%d.%m.%Y %T") done creating files for user ${VPN_config_usr} >> $VPN_userscript_log #Send all config files to the users E-Mail address #This assumes the user has a Mail-Account as username@$AD_NAME #mutt requires you to have fully configured MTA on the Server this script is running on #the MTA on my server is limited to sending Mails to my local mail server and does not accept incoming mail echo -e 'Hello '${VPN_config_usr}',\n Copy the desired config file to the OpenVPN config folder. \n For WinXP and Vista/Win7 32 Bit this is:"C:\\Program Files\\OpenVPN\\config"\n For Win Vista/Win7 64Bit this is:"C:\\Program Files(x86)\\OpenVPN\\config"\n If you do not know which config to choose, check the internal section of https://www.skelleton.net \n This file contains your private VPN key, please store it safely!' | mutt -s "Your new OpenVPN config files for ${AD_NAME}"$VPN_Mailattach -- ${VPN_config_usr}@$AD_NAME } function func_VPN_create_CRL { echo creating CRL $CRLPATH openssl ca -gencrl -config $KEY_CONFIG -out $CRLPATH && echo $(date +"%d.%m.%Y %T") New CRL created >> $VPN_userscript_log || echo $(date +"%d.%m.%Y %T") ERROR_CRL error creating CRL >> $VPN_userscript_log } function func_VPN_new_users { #This function checks if a Client config directory exists for every user in the group. #If not create the dir, certificates and configs. #The function func_VPN_get_user_list has to be run before this function if [ -z "$VPN_user_list" ] then VPN_ERR=1 #echo -e 'There is a problem with the VPN script -- user list is empty' | mutt -s "ERROR the OpenVPN user script encountered a problem" --openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script >> $VPN_userscript_log echo $(date +"%d.%m.%Y %T") ERROR VPN user list is empty, aborting script fi echo $(date +"%d.%m.%Y %T") checking for new users #input for the for loop is a comma seperated list, so we have to tell it, that the delim is "," for i in ${VPN_user_list//,/ }; do echo "Checking if ccd for $i" exists #check if the user directory already exists, create it if not else move on echo ${VPN_client_dir}/$i if [ ! -d "${VPN_client_dir}/${i}" ] then echo $(date +"%d.%m.%Y %T") Found new VPN user $i creating key certs and configs.... >> $VPN_userscript_log echo $(date +"%d.%m.%Y %T") Found new VPN user $i creating key certs and configs.... mkdir "${VPN_client_dir}/${i}" #create the key and cert for the user after creating his dir #the -nodes in the request specifies, that the key will not be encryptes and thus will not require a password #dont use - nodes if you do not have another form of authentication in your OpenVPN #Batch mode is important for Automated Scripts #it would probably be enough to state only cn and email in the subject, since the other values are also the defaults in my ssl config #openssl req -nodes -new -keyout ${VPN_client_dir}/${i}/${i}.key -out ${VPN_client_dir}/${i}/${i}_csr.pem -rand $VPN_RANDFILE 4096 -subj"/C=DE/ST=Skelletonia/O=skelleton.net/OU=OpenVPN_Users/CN=${i}/emailAddress=${i}@${AD_NAME}" -batch -config $KEY_CONFIG echo creating certificate request and key openssl req -nodes -new -keyout ${VPN_client_dir}/${i}/${i}.key -out ${VPN_client_dir}/${i}/${i}_csr.pem -rand $VPN_RANDFILE -subj "/C=DE/ST=Skelletonia/O=skelleton.net/OU=OpenVPN_Users/CN=${i}/emailAddress=${i}@${AD_NAME}" -batch -config $KEY_CONFIG echo signing request openssl ca -out ${VPN_client_dir}/${i}/${i}.crt -in ${VPN_client_dir}/${i}/${i}_csr.pem -batch -config $KEY_CONFIG chmod 0600 ${VPN_client_dir}/${i}/${i}.key #build the config file(s) for all templates found in $VPN_template_dir func_VPN_create_config $i #notify the OpenVPN admin echo -e 'New user added to OpenVPN' | mutt -s "The user $i has been added to OpenVPN" -- openvpn@$AD_NAME elif [ -d "${VPN_client_dir}/${i}" ] then echo directory ${VPN_client_dir}/${i} exists else echo check if directory ${VPN_client_dir}/${i} exists failed fi done func_VPN__archive_certs echo $(date +"%d.%m.%Y %T") done checking for new users } function func_VPN_revoke { #This function revokes the certificates for all users, who are not in the AD group anymore echo $(date +"%d.%m.%Y %T") checking if certificates need to be revoked NEW_CRL=0 for VPN_ccd in ${VPN_client_dir}/*; do VPN_ccd=${VPN_ccd%*/} #if [ ! ${VPN_ccd##*/} in $VPN_user_list ]; then if echo $VPN_user_list | grep -q ${VPN_ccd##*/} then echo ${VPN_ccd##*/} is in list: $VPN_user_list else NEW_CRL=1 echo revoking ${VPN_client_dir}/${VPN_ccd##*/}/${VPN_ccd##*/}.crt echo userlist $VPN_user_list openssl ca -revoke ${VPN_client_dir}/${VPN_ccd##*/}/${VPN_ccd##*/}.crt -batch -config $KEY_CONFIG rm -R $VPN_ccd echo -e "Hello ${VPN_ccd##*/}, your access to skelleton.net OpenVPN has been revoked. This is an automated message. If you believe there has been a mistake contact openvpn@${AD_NAME}" | mutt -s "OpenVPN access has been revoked." -- ${VPN_ccd##*/}@$AD_NAME echo $(date +"%d.%m.%Y %T") $Vpn_ccd has been deleted and user certificates have been revoked, because the user is no longer in the Active Directory group echo $(date +"%d.%m.%Y %T") $Vpn_ccd has been deleted and user certificates have been revoked, because the user is no longer in the Active Directory group >> $VPN_userscript_log fi done #create CRL if a certificate was revoked or if the current CRL is about to expire echo NEW_CRL: $NEW_CRL if [ $NEW_CRL == 1 ] then echo certificate revoked creating new crl func_VPN_create_CRL elif [ -e "$(find $CRLPATH -mmin +5)" ] then echo crl to old creating new one func_VPN_create_CRL fi echo $(date +"%d.%m.%Y %T") done checking if certificates need to be revoked } function func_VPN_update_configs { #This function creates new config files for all users. It assumes that all current users have a client config dir echo $(date +"%d.%m.%Y %T") creating new configs for all users for VPN_ccd in ${VPN_client_dir}/*; do func_VPN_create_config ${VPN_ccd##*/} done echo -e "All client configurations for server $VPN_NAME have been updated and distributed" | mutt -s "OpenVPN configuration files updated for $VPN_NAME " -- openvpn@$AD_NAME echo $(date +"%d.%m.%Y %T") done updating configs echo $(date +"%d.%m.%Y %T") updated all user configuration files >> $VPN_userscript_log } #run the script echo $(date +"%d.%m.%Y %T") Starting VPN_userscript >> $VPN_userscript_log func_VPN_get_user_list if [ $VPN_ERR == 0 ] then func_VPN_new_users func_VPN_revoke if [ -f $VPN_UPDATE_ALL ] then func_VPN_update_configs rm $VPN_UPDATE_ALL fi else echo $(date +"%d.%m.%Y %T") ERROR the script encountered an error while fetching VPN users fi echo $(date +"%d.%m.%Y %T") VPN_userscript finished running >> $VPN_userscript_log
Site that where helpful in wrting this Article Series
Paul Eggleton’s Weblog
The OpenVPN Documentation
The OpenSSL Documentation
This Article on macfreek.nl