#!/usr/bin/env bash script_name="$(basename $(realpath $0))" working_dir="$(dirname $(realpath $0))" log_file="$(mktemp)" _date="$(date +%Y-%m-%d-%H%M)" key_names_reserverd="ta ca server" #if [[ $# -eq 0 ]] ; then # BATCH_MODE=false #elif [[ -z "${BATCH_MODE}" ]] ; then # BATCH_MODE=false #fi # - For checking if revokation was successfull # - _CHECK_DIR="$(mktemp -d)" _RT_CERT="${_CHECK_DIR}/revoke-test.pem" # ---------- # Base Function(s) # ---------- usage() { [[ -n "$1" ]] && error "$1" [[ $terminal ]] && echo -e " \033[1mUsage:\033[m $(basename $0) [OPTION [OPTION .. \033[1mDescription\033[m Revoke a given key from a alos given (local) OpenVPN Service \033[1mOptions\033[m -b No interaction possible - run this script in batch mode -C The nae of the instance of the OpenVPN servive from which a key is requested to remove. Example: -C \033[1mopp-home\033[m - Revoke a key from OpenVPN service, which is startet with configuration \033[1m/etc/openvpn/opp-home\033[m -h Prints this help. -N the name of the key, which is requested ti revoke from OpenVPN Service -S Don't restart Service after (successfully) revoking the given key \033[1mFiles\033[m No script configuration file is used. Configuration files for the OpenVPN service must be exists, i.e. for service \033[1mopp-home\033[m ${working_dir}/conf/server-opp-home.conf \033[1mExample:\033[m interactive run - all needed parameters will be requested: $(basename $0) running in batch mode - revoke key 'gudrun' from local OpenVPN service 'opp-home'. Dont restart service if finished. $(basename $0) -b -S -C opp-home -N gudrun " clean_up 1 } clean_up() { # Perform program exit housekeeping rm $log_file rm -rf $_CHECK_DIR blank_line exit $1 } blank_line() { if $terminal && ! ${BATCH_MODE}; then echo "" fi } is_number() { return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1); # - also possible # - #[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1 #return $([[ ! -z "${1##*[!0-9]*}" ]]) } is_int() { return $(test "$@" -eq "$@" > /dev/null 2>&1); } echononl(){ if $terminal && ! ${BATCH_MODE} ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ fi } fatal(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: $*" echo "" echo -e " \033[31m\033[1mScript was interupted\033[m!" else echo " [ Fatal ]: $*" echo "" echo " Script was terminated...." fi echo "" clean_up 1 } error (){ echo "" if $terminal && ! ${BATCH_MODE} ; then echo -e " [ \033[31m\033[1mError\033[m ]: $*" else echo "[ Error ]: $*" fi echo "" } warn (){ echo "" if $terminal && ! ${BATCH_MODE} ; then echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" else echo "[ Warning ]: $*" fi echo "" } warn_only_terminal () { if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" echo "" fi } info (){ if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ] $*" echo "" fi } ok (){ if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e " [ \033[32m\033[1mOk\033[m ] $*" echo "" fi } print_command () { echo "" echo -e "\t\033[33m\033[1mCommand was\033[m: $*" echo "" } echo_done() { if $terminal && ! ${BATCH_MODE} ; then echo -e "\033[75G[ \033[32mdone\033[m ]" fi } echo_ok() { if $terminal && ! ${BATCH_MODE} ; then echo -e "\033[75G[ \033[32mok\033[m ]" fi } echo_warn() { if $terminal && ! ${BATCH_MODE} ; then echo -e "\033[75G[ \033[33mwarn\033[m ]" fi } echo_failed(){ if $terminal && ! ${BATCH_MODE} ; then echo -e "\033[75G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal && ! ${BATCH_MODE} ; then echo -e "\033[75G[ \033[90m\033[1mskipped\033[m ]" fi } echo_wait(){ if $terminal && ! ${BATCH_MODE} ; then echo -en "\033[75G[ \033[5m\033[1m...\033[m ]" fi } trap clean_up SIGHUP SIGINT SIGTERM # ---------- # - Some checks .. # ---------- # - Running in a terminal? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi # -Is systemd supported on this system? # - systemd_supported=false systemd=$(which systemd) systemctl=$(which systemctl) if [[ -n "$systemd" ]] && [[ -n "$systemctl" ]] ; then systemd_supported=true fi # ---------- # - Read commandline parameter # ---------- BATCH_MODE=false OPENVPN_SERVER_CONFIG= KEY_NAME_TO_REVOKE= RESTART_OPENVPN_SERVICE=true while getopts bC:hN:S opt ; do case $opt in b) BATCH_MODE=true ;; C) OPENVPN_SERVER_CONFIG="${OPTARG}" ;; h) usage ;; N) KEY_NAME_TO_REVOKE="${OPTARG}" ;; S) RESTART_OPENVPN_SERVICE=false ;; esac done if ${BATCH_MODE} ; then if [[ -z "${OPENVPN_SERVER_CONFIG}" ]] && [[ -z "${KEY_NAME_TO_REVOKE}" ]]; then fatal "In batch mode both a server configuration (-C) and the key name (-N) musit be specified!" elif [[ -z "${OPENVPN_SERVER_CONFIG}" ]] ; then fatal "In batch mode also a server configuration (-C) must be specified!" elif [[ -z "${KEY_NAME_TO_REVOKE}" ]]; then fatal "In batch mode also the key name (-N) must be specified!" fi fi #clear if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e "\033[21G\033[32mRevoke OpenVPN key.. \033[m" echo "" echo "" fi declare -a conf_file_arr=() declare -a conf_name_arr=() for _conf_file in `ls ${working_dir}/conf/server-*.conf 2>/dev/null` ; do _basename=$(basename $_conf_file) _tmp_name=${_basename%%.*} _tmp_name=${_tmp_name#*-} if ${BATCH_MODE} ; then if [[ "${_tmp_name}" = "${OPENVPN_SERVER_CONFIG}" ]]; then conf_file="${_conf_file}" break fi else conf_name_arr+=("$_tmp_name") conf_file_arr+=("${_conf_file}") fi done if [[ ${#conf_file_arr[@]} -lt 1 ]] && [[ -z "${conf_file}" ]]; then fatal "NO Configuration found!" fi blank_line declare -i i=0 if ! ${BATCH_MODE} ; then if [[ ${#conf_file_arr[@]} -gt 1 ]] ; then if $terminal ; then echo "" echo "Which Configuration should be loaded?" echo "" fi for _conf_file in ${conf_file_arr[@]} ; do if $terminal ; then echo " [${i}] ${conf_name_arr[${i}]}" fi (( i++ )) done _OK=false blank_line echononl "Eingabe: " while ! $_OK ; do read _IN if is_number "$_IN" && [[ -n ${conf_file_arr[$_IN]} ]]; then conf_file=${conf_file_arr[$_IN]} _OK=true else if is_number "$_IN" && [[ -n ${conf_file_arr[$_IN]} ]]; then if $terminal ; then echo "" echo -e "\tFalsche Eingabe !" echo "" fi echononl "Eingabe: " fi fi done else conf_file=${conf_file_arr[0]} fi fi if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e "\033[32m--\033[m" echo "" fi # ---------- # - Read Configurations from $conf_file # ---------- echononl " Load Configuration File $(basename ${conf_file}).." if [[ ! -f "$conf_file" ]]; then echo_failed fatal "Configuration file \033[37m\033[1m$(basename ${conf_file})\033[m not found!" else source "${conf_file}" > $log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed fatal "$(cat $log_file)" fi fi EASY_RSA_DIR="${OPENVPN_BASE_DIR}/easy-rsa" if [[ -d "${OPENVPN_BASE_DIR}/pki" ]] ; then EASYRSA_LAYOUT_NEW=true else EASYRSA_LAYOUT_NEW=false fi if [[ -z "$OPENVPN_KEY_DIR" ]] ; then if $EASYRSA_LAYOUT_NEW ; then OPENVPN_KEY_DIR="${OPENVPN_BASE_DIR}/pki" else OPENVPN_KEY_DIR="${OPENVPN_BASE_DIR}/keys" fi fi if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e "\033[32m--\033[m" echo "" fi #KEY_NAME_TO_REVOKE="" if [ -z "$KEY_NAME_TO_REVOKE" ]; then if $terminal && ! ${BATCH_MODE} ; then echo "Insert key name you wish to revoke." echo "" echo "" fi echononl "key name to revoke: " read KEY_NAME_TO_REVOKE while [ "X$KEY_NAME_TO_REVOKE" = "X" ] ; do if $terminal && ! ${BATCH_MODE} ; then echo -e "\n\t\033[33m\033[1mKey name is required!\033[m\n" fi echononl "key name: " read KEY_NAME_TO_REVOKE done fi # - Remove '${KEY_CN}-' from key name # - KEY_NAME_TO_REVOKE="${KEY_NAME_TO_REVOKE/${KEY_CN}-/}" _CLIENT_CN="${KEY_CN}-${KEY_NAME_TO_REVOKE}" if $EASYRSA_LAYOUT_NEW ; then _CLIENT_CERT="${OPENVPN_KEY_DIR}/issued/${_CLIENT_CN}.crt" _CLIENT_KEY="${OPENVPN_KEY_DIR}/private/${_CLIENT_CN}.key" else _CLIENT_CERT="${OPENVPN_KEY_DIR}/${KEY_NAME_TO_REVOKE}.crt" _CLIENT_KEY="${OPENVPN_KEY_DIR}/${KEY_NAME_TO_REVOKE}.key" fi _CRL="${OPENVPN_KEY_DIR}/crl.pem" _CA_CERT="${OPENVPN_KEY_DIR}/ca.crt" for _name in $key_names_reserverd ; do [[ "$_name" = "$KEY_NAME_TO_REVOKE" ]] && fatal "Name '$KEY_NAME_TO_REVOKE' cannot be used - its a reserved name!" done if [[ ! -f "${_CLIENT_CERT}" ]]; then fatal "Key '$KEY_NAME_TO_REVOKE' not found!" fi if $terminal && ! ${BATCH_MODE} ; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Server Configuration....: ${OPENVPN_SERVER_CONFIG}" echo "Key to revoke...........: $(basename "$_CLIENT_KEY")" info "Going to revoke key \033[37m\033[1m$(basename "$_CLIENT_KEY")\033[m.." echo -n "To continue type uppercase 'YES': " read OK echo "" if [[ "$OK" != "YES" ]] ; then fatal "Abort by user request - Answer as not 'YES'" fi fi # ---------- # - Main part of script # ---------- if $terminal && ! ${BATCH_MODE} ; then echo "" echo "" echo -e " \033[1mMain part of script - Goimg to revoke key of requested user $(basename "$_CLIENT_KEY")..\033[m" echo "" fi blank_line # --- # - Backup existing OpenVPN directory # --- echononl "Backup existing OpenVPN directory '$OPENVPN_BASE_DIR'.." if [[ -d "$OPENVPN_BASE_DIR" ]]; then cp -a "$OPENVPN_BASE_DIR" "${OPENVPN_BASE_DIR}.$_date" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed fatal "$(cat $log_file)" fi else echo_skipped fatal "OpenVPN directory '$OPENVPN_BASE_DIR' not found!" fi echononl "Backup cert to revoke for further check. (see below" cp -a "$_CLIENT_CERT" "$_CHECK_DIR" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed fatal "$(cat $log_file)" fi if $EASYRSA_LAYOUT_NEW ; then # --- # - Revoke Key # --- echononl "Revoke key '$(basename "$_CLIENT_KEY")'.." $EASY_RSA_DIR/easyrsa --vars="${EASY_RSA_DIR}/vars" revoke "$_CLIENT_CN" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" print_command "${EASY_RSA_DIR}/easyrsa --vars=\"${EASY_RSA_DIR}/vars\" revoke \"$_CLIENT_CN\"" echononl "continue anyway [yes/no]: " read OK OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')" while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do echononl "Wrong entry! - repeat [yes/nno]: " read OK done [[ $OK = "yes" ]] || fatal "Abbruch durch User" fi # --- # - Generate new crl.pem # --- echononl "Generate new CRL (Certificate Revokation List) 'crl.pem'.." $EASY_RSA_DIR/easyrsa --vars="${EASY_RSA_DIR}/vars" gen-crl > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" print_command "${EASY_RSA_DIR}/easyrsa --vars=\"${EASY_RSA_DIR}/vars\" gen-crl" echononl "continue anyway [yes/no]: " read OK OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')" while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do echononl "Wrong entry! - repeat [yes/nno]: " read OK done [[ $OK = "yes" ]] || fatal "Abbruch durch User" fi else # --- # - source file vars # --- echononl " Load configuration '${EASY_RSA_DIR}/vars'.." source ${EASY_RSA_DIR}/vars > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" fi if [[ -z "$KEY_CONFIG" ]] ; then if [[ -h "${EASY_RSA_DIR}/openssl.cnf" ]] ; then KEY_CONFIG="$(realpath "${EASY_RSA_DIR}/openssl.cnf")" fi fi if [[ -z "$KEY_CONFIG" ]] ; then fatal "No OpenSSL configuration file found.." fi # --- # - Revoke Key # --- echo "" echo -e "\033[32m--\033[m" echo "Revoke Key ${KEY_NAME_TO_REVOKE}.key .." echo -e "\033[32m--\033[m" echo "" echononl "Revoke key ${KEY_NAME_TO_REVOKE}.key and update data base .." $OPENSSL ca -revoke "${_CLIENT_CERT}" -config "$KEY_CONFIG" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed fatal "$(cat $log_file)" fi echononl "Generate a new CRL -- try to be compatible with intermediate PKIs" $OPENSSL ca -gencrl -out "$_CRL" -config "$KEY_CONFIG" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed fatal "$(cat $log_file)" fi fi # --- # - Change group (nogroup) for file 'crl.pem' # --- echononl "Change group (to nogroup) for '${OPENVPN_KEY_DIR}/crl.pem'.." chgrp nogroup "${_CRL}" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" fi # --- # - Change permission (640) for file 'crl.pem' # --- echononl "Change permissions (640) for ${OPENVPN_KEY_DIR}/crl.pem" chmod 640 "${_CRL}" > "$log_file" 2>&1 if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" fi # --- # - Check if Revokation was sucessfully. # --- echo "" echo -e "\033[32m--\033[m" echo "Check if Revokation of Key $(basename "$_CLIENT_KEY") was sucessfully.." echo -e "\033[32m--\033[m" echo "" # - "Create CA file '$_RT_CERT' including (new) $_CRL to check against.. # - echononl "Create CA file '$_RT_CERT' to check against.." cat "$_CA_CERT" "$_CRL" >"$_RT_CERT" 2> "$log_file" if [[ $? -eq 0 ]] ; then echo_ok else echo_failed error "$(cat $log_file)" fatal "Verifying the revocation is not possible!" fi [[ -z "$OPENSSL" ]] && OPENSSL="$(which openssl)" echononl "Verify the revocation.." $OPENSSL verify -CAfile "$_RT_CERT" -crl_check "${_CHECK_DIR}/$(basename "$_CLIENT_CERT")" > "$log_file" 2>&1 if [[ $? -eq 2 ]]; then echo_ok info "Key \033[37m\033[1m$(basename "$_CLIENT_KEY")\033[m successfully revoked." else echo_failed error "$(cat $log_file)" fi # --- # - Restart OpenVPN service # --- echo "" echononl "\tRestart OpenVPN Service.." if ${RESTART_OPENVPN_SERVICE} ; then if $systemd_supported ; then systemctl restart openvpn > $log_file 2>&1 else /etc/init.d/openvpn restart > $log_file 2>&1 fi if [ "$?" = "0" ]; then echo_ok else echo_failed error "$(cat $log_file)" warn "Restarting OpenVPN Service failed!." fi else echo_skipped fi clean_up 0