From 9864cfab1f9dcc7712333db69daa4a2d7b815601 Mon Sep 17 00:00:00 2001 From: Richard T Bonhomme Date: Mon, 11 Oct 2021 22:25:30 +0100 Subject: [PATCH] Connection tracking - Work around Openvpn issue #160 Details: * https://github.com/TinCanTech/easy-tls/issues/213 * https://community.openvpn.net/openvpn/ticket/160 Signed-off-by: Richard T Bonhomme --- easytls-client-connect.sh | 86 ++++++++++++++++++++------- easytls-client-disconnect.sh | 112 +++++++++++++++++++++++++++-------- easytls-conntrac.lib | 32 +++++++--- 3 files changed, 173 insertions(+), 57 deletions(-) diff --git a/easytls-client-connect.sh b/easytls-client-connect.sh index 67a6aa2..ddd15d8 100755 --- a/easytls-client-connect.sh +++ b/easytls-client-connect.sh @@ -42,6 +42,7 @@ help_text () -p|--push-required Require all clients to use --push-peer-info. -c|--crypt-v2-required Require all clients to use a TLS-Crypt-V2 key. -k|--key-required Require all client keys to have a hardware-address. + -d|--dyn-opts= Path and name of Openvpn client dynamic options file. -t|--tmp-dir= Temp directory where server-scripts write data. Default: *nix /tmp/easytls Windows C:/Windows/Temp/easytls @@ -269,10 +270,9 @@ deps () EASYTLS_KILL_FILE="${temp_stub}-kill-client" # Dynamic opts file - EASYTLS_DYN_OPTS_FILE="${temp_stub}-dyn-opts" - if [ -f "${EASYTLS_DYN_OPTS_FILE}" ] + if [ -f "${EASYTLS_DYN_OPTS_FILE}" ] && [ -n "${ovpn_dyn_opts_file}" ] then - "${EASYTLS_CAT}" "${EASYTLS_DYN_OPTS_FILE}" > "${ovpn_cli_dyn_file}" + "${EASYTLS_CAT}" "${EASYTLS_DYN_OPTS_FILE}" > "${ovpn_dyn_opts_file}" update_status "dyn opts loaded" fi } @@ -302,6 +302,11 @@ do empty_ok=1 EASYTLS_VERBOSE=1 ;; + -d|--dyn-opts) + EASYTLS_DYN_OPTS_FILE="${val}" + [ -f "${EASYTLS_DYN_OPTS_FILE}" ] || \ + warn_die "Easy-TLS dynamic opts file missing" + ;; -a|--allow-no-check) empty_ok=1 allow_no_check=1 @@ -340,7 +345,7 @@ do then # Do not need this in the log but keep it here for reference #[ $EASYTLS_VERBOSE ] && echo "Ignoring temp file: $opt" - ovpn_cli_dyn_file="${opt}" + ovpn_dyn_opts_file="${opt}" else [ "${EASYTLS_VERBOSE}" ] && warn_die "Unknown option: ${opt}" fi @@ -380,21 +385,6 @@ deps unset env_file } -# Source conn-trac lib -[ $ENABLE_CONN_TRAC ] && { - prog_dir="${0%/*}" - lib_file="${prog_dir}/easytls-conntrac.lib" - [ -f "${lib_file}" ] || { - easytls_url="https://github.com/TinCanTech/easy-tls" - easytls_wiki="/wiki/download-and-install" - #easytls_rawurl="https://raw.githubusercontent.com/TinCanTech/easy-tls" - #easytls_file="/master/easytls-conntrac.lib" - help_note="See: ${easytls_url}${easytls_wiki}" - die "Missing ${lib_file}" - } - . "${lib_file}" - unset lib_file - } # flush auth-control file #"${EASYTLS_RM}" -f "${auth_control_file}" @@ -415,16 +405,66 @@ client_serial="$(format_number "${tls_serial_hex_0}")" } # Update connection tracking -[ $ENABLE_CONN_TRAC ] && { +if [ $ENABLE_CONN_TRAC ] +then + prog_dir="${0%/*}" + lib_file="${prog_dir}/easytls-conntrac.lib" + [ -f "${lib_file}" ] || { + easytls_url="https://github.com/TinCanTech/easy-tls" + easytls_wiki="/wiki/download-and-install" + #easytls_rawurl="https://raw.githubusercontent.com/TinCanTech/easy-tls" + #easytls_file="/master/easytls-conntrac.lib" + help_note="See: ${easytls_url}${easytls_wiki}" + die "Missing ${lib_file}" + } + . "${lib_file}" + unset lib_file + conntrac_record="${UV_TLSKEY_SERIAL:-TLSAC}" conntrac_record="${conntrac_record}=${client_serial}=${common_name}" # shellcheck disable=SC2154 conntrac_record="${conntrac_record}=${ifconfig_pool_remote_ip}" + # shellcheck disable=SC2154 + conntrac_record="${conntrac_record}++${untrusted_ip}:${untrusted_port}" + conn_trac_connect "${conntrac_record}" "${EASYTLS_CONN_TRAC}" || { - update_status "conn_trac_connect FAIL" - [ $FATAL_CONN_TRAC ] && die "CONNTRAC_CONNECT_FAIL" 99 + case $? in + 2) update_status "conn_trac_connect ERROR" + conntrac_error=1 + ;; + 1) + update_status "conn_trac_connect FAIL" + [ $FATAL_CONN_TRAC ] && die "CONNTRAC_CONNECT_FAIL" 99 + ;; + *) die "CONNTRAC_CONNECT_FAIL" 98 ;; + esac + conntrac_fail=1 } - } + + # Log failure + if [ $conntrac_fail ] + then + { + [ -f "${EASYTLS_CONN_TRAC}.fail" ] && \ + "${EASYTLS_CAT}" "${EASYTLS_CONN_TRAC}.fail" + "${EASYTLS_PRINTF}" '%s ' "$(date '+%x %X')" + [ $conntrac_error ] && "${EASYTLS_PRINTF}" '%s ' "AR" + "${EASYTLS_PRINTF}" '%s\n' "CON: ${conntrac_record}" + } > "${EASYTLS_CONN_TRAC}.fail.tmp" + "${EASYTLS_MV}" "${EASYTLS_CONN_TRAC}.fail.tmp" \ + "${EASYTLS_CONN_TRAC}.fail" + + env_file="${temp_stub}-client-connect.env" + if [ $EASYTLS_FOR_WINDOWS ]; then + set > "${env_file}" + else + env > "${env_file}" + fi + unset env_file + fi +else + update_status "conn-trac disabled" +fi # Check for kill signal if [ -f "${EASYTLS_KILL_FILE}" ] && \ diff --git a/easytls-client-disconnect.sh b/easytls-client-disconnect.sh index 9e7b1f3..7a1c6bf 100755 --- a/easytls-client-disconnect.sh +++ b/easytls-client-disconnect.sh @@ -383,45 +383,103 @@ disconnect_accepted # There is only one way out of this... if [ $absolute_fail -eq 0 ] then - # Update connection tracking - conntrac_record="${UV_TLSKEY_SERIAL:-TLSAC}" - conntrac_record="${conntrac_record}=${client_serial}=${common_name}" - # shellcheck disable=SC2154 - conntrac_record="${conntrac_record}=${ifconfig_pool_remote_ip}" if [ $ENABLE_CONN_TRAC ] then - conn_trac_disconnect "${conntrac_record}" "${EASYTLS_CONN_TRAC}" || { - ctd_fail=1 - update_status "conn_trac_disconnect FAIL" - [ $FATAL_CONN_TRAC ] && die "CONNTRAC_DISCONNECT_FAIL" 99 - } + # Update connection tracking + conntrac_record="${UV_TLSKEY_SERIAL:-TLSAC}" + conntrac_record="${conntrac_record}=${client_serial}" + # If common_name is not set then this is bug 160-2 + # Use username, which is still set, when common_name is lost + # Set the username alternative first + # shellcheck disable=SC2154 + conntrac_alt_rec="${conntrac_record}=${username}" + conntrac_alt2_rec="${conntrac_record}=${X509_0_CN}" + conntrac_record="${conntrac_record}=${common_name}" + + # shellcheck disable=SC2154 + conntrac_record="${conntrac_record}=${ifconfig_pool_remote_ip}" + conntrac_alt_rec="${conntrac_alt_rec}=${ifconfig_pool_remote_ip}" + conntrac_alt2_rec="${conntrac_alt2_rec}=${ifconfig_pool_remote_ip}" + # shellcheck disable=SC2154 + conntrac_record="${conntrac_record}++${untrusted_ip}:${untrusted_port}" + conntrac_alt_rec="${conntrac_alt_rec}++${untrusted_ip}:${untrusted_port}" + conntrac_alt2_rec="${conntrac_alt2_rec}++${untrusted_ip}:${untrusted_port}" + + # Disconnect common_name + conn_trac_disconnect "${conntrac_record}" "${EASYTLS_CONN_TRAC}" + conntrac_errcode=$? + + case $conntrac_errcode in + 2) update_status "conn_trac_disconnect ERROR" + conntrac_error=1 + log_error=1 + ;; + 1) + update_status "conn_trac_disconnect FAIL" + conntrac_fail=1 + log_error=1 + [ $FATAL_CONN_TRAC ] && die "CONNTRAC_DISCONNECT_FAIL" 99 + ;; + 0) : ;; # OK + *) die "CONNTRAC_CONNECT_FAIL" 98 ;; + esac + unset conntrac_errcode + + if [ $conntrac_error ] + then + # Disconnect username + conn_trac_disconnect "${conntrac_alt_rec}" "${EASYTLS_CONN_TRAC}" + conntrac_alt_errcode=$? + + case $conntrac_alt_errcode in + 2) update_status "conn_trac_disconnect ALT ERROR" + conntrac_alt_error=1 + log_error=1 + ;; + 1) + update_status "conn_trac_disconnect ALT FAIL" + conntrac_alt_fail=1 + [ $FATAL_CONN_TRAC ] && die "CONNTRAC_DISCONNECT_ALT_FAIL" 99 + log_error=1 + ;; + 0) : ;; # OK + *) die "CONNTRAC_CONNECT_ALT_FAIL" 98 ;; + esac + unset conntrac_alt_errcode + fi # Log failure - if [ $ctd_fail ] + if [ $conntrac_fail ] || [ $conntrac_error ] then { [ -f "${EASYTLS_CONN_TRAC}.fail" ] && \ "${EASYTLS_CAT}" "${EASYTLS_CONN_TRAC}.fail" - "${EASYTLS_PRINTF}" '%s\n' "${conntrac_record}" + "${EASYTLS_PRINTF}" '%s ' "$(date '+%x %X')" + [ $conntrac_fail ] && "${EASYTLS_PRINTF}" '%s ' "FAIL" + [ $conntrac_error ] && "${EASYTLS_PRINTF}" '%s ' "NFound" + "${EASYTLS_PRINTF}" '%s\n' "DIS: ${conntrac_record}" } > "${EASYTLS_CONN_TRAC}.fail.tmp" "${EASYTLS_MV}" "${EASYTLS_CONN_TRAC}.fail.tmp" \ "${EASYTLS_CONN_TRAC}.fail" + fi - conntrac_record="${UV_TLSKEY_SERIAL:-TLSAC}" - conntrac_record="${conntrac_record}=${client_serial}=.*" - # shellcheck disable=SC2154 - conntrac_record="${conntrac_record}=${ifconfig_pool_remote_ip}" - - if grep -q "${conntrac_record}" "${EASYTLS_CONN_TRAC}" - then - sed -i "/${conntrac_record}/d" "${EASYTLS_CONN_TRAC}" || \ - die "conntrac sed FAIL" - update_status "conn_trac_disconnect FORCED" - else - # This is bad - update_status "conn_trac_disconnect FORCED FAIL" - fi + if [ $conntrac_alt_fail ] || [ $conntrac_alt_error ] + then + { + [ -f "${EASYTLS_CONN_TRAC}.fail" ] && \ + "${EASYTLS_CAT}" "${EASYTLS_CONN_TRAC}.fail" + "${EASYTLS_PRINTF}" '%s ' "$(date '+%x %X')" + [ $conntrac_alt_fail ] && "${EASYTLS_PRINTF}" '%s ' "AFAIL" + [ $conntrac_alt_error ] && "${EASYTLS_PRINTF}" '%s ' "ANFound" + "${EASYTLS_PRINTF}" '%s\n' "DIS: ${conntrac_alt_rec}" + } > "${EASYTLS_CONN_TRAC}.fail.tmp" + "${EASYTLS_MV}" "${EASYTLS_CONN_TRAC}.fail.tmp" \ + "${EASYTLS_CONN_TRAC}.fail" + fi + # Capture env + if [ $log_error ] + then env_file="${temp_stub}-client-disconnect.env" if [ $EASYTLS_FOR_WINDOWS ]; then set > "${env_file}" @@ -430,6 +488,8 @@ then fi unset env_file fi + + [ $conntrac_alt_error ] && die "conntrac_alt_error" else update_status "conn-trac disabled" fi diff --git a/easytls-conntrac.lib b/easytls-conntrac.lib index 08d6d95..f4c98e3 100644 --- a/easytls-conntrac.lib +++ b/easytls-conntrac.lib @@ -46,21 +46,29 @@ conn_trac_connect () easytls_ct_lock_file="${2}.lock" easytls_temp_file="${2}.tmp" acquire_lock || die "ct acquire_lock" + err_exit=0 + + # Pattern to match + conntrac_pattern="${1}" if [ -f "${2}" ] then { - while read conn + while read full_conn do - if [ "${conn}" = "${1}" ] + conn="${full_conn}" + if [ "${conn}" = "${conntrac_pattern}" ] then + # Already exists - Print nothing already_registered=1 update_status "conntrac: already registered" [ $VERBOSE_CONN_TRAC ] && update_status "${1}" else - "${EASYTLS_PRINTF}" '%s\n' "${conn}" + # Print the existing record + "${EASYTLS_PRINTF}" '%s\n' "${full_conn}" fi done < "${2}" + # If not already registered then print it [ $already_registered ] || "${EASYTLS_PRINTF}" "%s\n" "${1}" } > "${easytls_temp_file}" @@ -69,12 +77,13 @@ conn_trac_connect () if [ $already_registered ] then - err_exit=1 + err_exit=2 else update_status "conn-trac: registered" [ $VERBOSE_CONN_TRAC ] && update_status "${1}" fi else + # conntrac file does not exist, create it now { "${EASYTLS_PRINTF}" "%s\n" "${1}" } > "${2}" || err_exit=1 @@ -94,19 +103,26 @@ conn_trac_disconnect () easytls_ct_lock_file="${2}.lock" easytls_temp_file="${2}.tmp" acquire_lock || die "ct acquire_lock" + err_exit=0 + + # Pattern to match + conntrac_pattern="${1%++*}" if [ -f "${2}" ] then { - while read conn + while read full_conn do - if [ "${conn}" = "${1}" ] + conn="${full_conn%++*}" + if [ "${conn}" = "${conntrac_pattern}" ] then + # Matched - Do not Print conntrac_updated=1 update_status "conntrac: unregistered" [ $VERBOSE_CONN_TRAC ] && update_status "${1}" else - "${EASYTLS_PRINTF}" '%s\n' "${conn}" + # No match - Print + "${EASYTLS_PRINTF}" '%s\n' "${full_conn}" fi done < "${2}" } > "${easytls_temp_file}" @@ -115,7 +131,7 @@ conn_trac_disconnect () [ $conntrac_updated ] || { update_status "conntrac: record not found" [ $VERBOSE_CONN_TRAC ] && update_status "${1}" - err_exit=1 + err_exit=2 } [ -s "${2}" ] || { "${EASYTLS_RM}" -f "${2}"