commit 9564f3e870a211f898972525523b56e3e6c47e86 Author: Sergey Kalinin Date: Fri Jan 15 18:41:02 2021 +0300 Initial release diff --git a/README.md b/README.md new file mode 100644 index 0000000..04cbdf3 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# logon/logoff скрипты для VPN сервера ocserv + +- ocserv_logon.sh - скрипт выполняется сервером посчле авторизации пользователя. Формирует журнал с данными сессии и проверяет код страны через GeoIP и на основе пропускает дальше или нет +- ocserv_logout.sh - запускается при окончании пользовательской сессии и пишет данные в лог. + +## Использование + +Для использования скрипты компируются в любое удобное место и прописываются в конфиге "/etc/ocserv/ocserv.conf" + +``` +connect-script = /usr/local/bin/ocserv_logon.sh +disconnect-script = /usr/local/bin/ocserv_logout.sh +``` diff --git a/ocserv_logon.sh b/ocserv_logon.sh new file mode 100755 index 0000000..e32ef70 --- /dev/null +++ b/ocserv_logon.sh @@ -0,0 +1,141 @@ +#!/usr/bin/bash +################################################################################# +# +# Logon-скрипт для ocserv. +# +# На основе переменных передаваемых ocserv и географических +# данных формирует файл-журнал с данными пользовательских сессий +# +################################################################################# +# Автор: Сергей Калинин +# https://nuk-svk.ru +# svk@nuk-svk.ru +################################################################################# +# Список переменных: +# REASON, VHOST, USERNAME, GROUPNAME, DEVICE, IP_REAL (the real IP of the client), +# IP_REAL_LOCAL (the local interface IP the client connected), IP_LOCAL +# (the local IP in the P-t-P connection), IP_REMOTE (the VPN IP of the client), +# IPV6_LOCAL (the IPv6 local address if there are both IPv4 and IPv6 +# assigned), IPV6_REMOTE (the IPv6 remote address), IPV6_PREFIX, and +# ID (a unique numeric ID); REASON may be "connect" or "disconnect". +# In addition the following variables OCSERV_ROUTES (the applied routes for this +# client), OCSERV_NO_ROUTES, OCSERV_DNS + +LOG_DIR="/var/log/ocserv" +LOG_FILE=${LOG_DIR}/sessions.log + +# Формат выходной строки (text, json) +LOG_FORMAT="json" + +# Коды стран разрешённые к соединению +# можно передать ввиде списка "RU SA IC TW" +ALLOW_COUNTRY_CODES="RU" + + +if [ ! -d "$LOG_DIR" ]; then + mkdir -p ${LOG_DIR} +fi + +# Получаем географические данные по IP адресу, вернет строку вида: +#109.106.141.24,RU,Russia,VOR,Voronezhskaya Oblast',Voronezh,394000,Europe/Moscow,51.6592,39.2269,0 +# можно получать в json - "curl -s https://freegeoip.live/json/${IP_REAL}" +GEO_DATA=`curl --connect-timeout 5 -s https://freegeoip.live/csv/${IP_REAL}` + +if [ -z "${GEO_DATA}" ]; then + COUNTRY_CODE="RU" + LAT="0" + LON="0" +else + # Выбираем код страны и координаты + COUNTRY_CODE=`echo ${GEO_DATA} | cut -d"," -f 2` + LAT=`echo ${GEO_DATA} | cut -d"," -f 9` + LON=`echo ${GEO_DATA} | cut -d"," -f 10` +fi + +DATE=`date +'%d.%m.%Y %T'` + +# Преобразуем имена пользователей к нижнему регистру +USER_NAME=`echo ${USERNAME} | tr '[:upper:]' '[:lower:]'` + +# Проверяем имя пользователя на имя домена, +# сперва в формате "user@domain" потом "domain\user" \\domain\user +# и выдергиваем только имя пользователя + +TEMP_USER_NAME=`echo ${USER_NAME} | egrep -E "[[:alnum:]]+@" -o | tr -d "@"` + +if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" +else + TEMP_USER_NAME=`echo $USER_NAME | awk -F '\\' '{print $2}'` + if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" + else + TEMP_USER_NAME=`echo $USER_NAME | awk -F '\\' '{print $4}'` + if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" + else + SHORT_USER_NAME="${USER_NAME}" + fi + fi +fi + +# Проверяем что ocserv вернул нам реальный ip адрес (откуда пользователь заходит) +# если адреса нет - в соединении отказываем +# И соответствующим образом формируем строку для журнала в нужном формате +if [ "x${IP_REAL}" = "x" ]; then + if [ ${LOG_FORMAT} == 'json' ]; then + OUT_STRING="{\"session-datetime\": \"${DATE}\", \"reason\": \"empty_real_ip\", \"remote-user\": \"${SHORT_USER_NAME}\", \ +\"remote-user-group\": \"${GROUPNAME}\", \"session-id\": \"${ID}\", \ +\"country_code\": \"${COUNTRY_CODE}\", \"location\": {\"lat\": ${LAT},\"lon\": ${LON}}}" + else + OUT_STRING="${DATE} ${SHORT_USER_NAME} \"Empty real IP address\" ${ID} ${VHOST} ${DEVICE} ${IP_REAL} ${IP_REAL_LOCAL} \ +${IP_LOCAL} ${IP_REMOTE} ${OCSERV_ROUTES} ${OCSERV_NO_ROUTES} ${OCSERV_DNS} ${COUNTRY_CODE}" + fi + echo "${OUT_STRING}" >> ${LOG_FILE} + exit 1 +fi + +# Формируем выходную строку, для раазрешенного соединения, в нужном формате +if [ ${LOG_FORMAT} == 'json' ]; then + OUT_STRING="{\"session-datetime\": \"${DATE}\", \"reason\": \"${REASON}\", \"remote-user\": \"${SHORT_USER_NAME}\", \ +\"remote-user-group\": \"${GROUPNAME}\", \"session-id\": \"${ID}\", \"vhost\": \"${VHOST}\", \"device\": \"${DEVICE}\", \ +\"real-ip\": \"${IP_REAL}\", \"real-local-ip\": \"${IP_REAL_LOCAL}\", \"local-ip\": \"${IP_LOCAL}\", \"remote-ip\": \"${IP_REMOTE}\", \ +\"ocserv-routes\": \"${OCSERV_ROUTES}\", \"ocserv-no-routes\": \"${OCSERV_NO_ROUTES}\", \"ocserv-dns\": \"${OCSERV_DNS}\", \ +\"country_code\": \"${COUNTRY_CODE}\", \"location\": {\"lat\": ${LAT},\"lon\": ${LON}}}" +else + OUT_STRING="${DATE} ${SHORT_USER_NAME} ${REASON} ${ID} ${VHOST} ${DEVICE} ${IP_REAL} ${IP_REAL_LOCAL} \ +${IP_LOCAL} ${IP_REMOTE} ${OCSERV_ROUTES} ${OCSERV_NO_ROUTES} ${OCSERV_DNS} ${COUNTRY_CODE}" +fi + +# Если значение COUNTRY_CODE пустое (e.g. RFC 1918 addresses) то +if [ "x${COUNTRY_CODE}" = "x" ]; then + echo "${OUT_STRING}" >> ${LOG_FILE} + # Разрешаем соединение + exit 0 + # Запрещаем соединение + # exit 1 +fi + +# Если COUNTRY_CODE совпадает с одним из разрешенных то пишем в лог +# и благополучно завершаем скрипт +for c in $ALLOW_COUNTRY_CODES; do + if [ "${c}" = "${COUNTRY_CODE}" ]; then + echo "${OUT_STRING}" >> ${LOG_FILE} + exit 0 + fi +done + +# Если COUNTRY_CODE не совпадает с разрешенным то даём отлуп +# И пишем о сём факте в лог +if [ ${LOG_FORMAT} == 'json' ]; then + OUT_STRING="{\"session-datetime\": \"${DATE}\", \"reason\": \"not_allowed_country_code\", \"remote-user\": \"${SHORT_USER_NAME}\", \ +\"remote-user-group\": \"${GROUPNAME}\", \"session-id\": \"${ID}\", \"real-ip\": \"${IP_REAL}\", \ +\"country_code\": \"${COUNTRY_CODE}\", \"location\": {\"lat\": ${LAT},\"lon\": ${LON}}}" +else + OUT_STRING="${DATE} ${SHORT_USER_NAME} \"Not allowed country code\" ${ID} ${VHOST} ${DEVICE} ${IP_REAL} ${IP_REAL_LOCAL} \ +${IP_LOCAL} ${IP_REMOTE} ${OCSERV_ROUTES} ${OCSERV_NO_ROUTES} ${OCSERV_DNS} ${COUNTRY_CODE}" +fi + +echo "${OUT_STRING}" >> ${LOG_FILE} + +exit 1 \ No newline at end of file diff --git a/ocserv_logout.sh b/ocserv_logout.sh new file mode 100755 index 0000000..ce9a802 --- /dev/null +++ b/ocserv_logout.sh @@ -0,0 +1,65 @@ +#!/usr/bin/bash +# Ocserv logout script for sending statistics into log +# +################################################################################# +# Автор: Сергей Калинин +# https://nuk-svk.ru +# svk@nuk-svk.ru +################################################################################# +# REASON, VHOST, USERNAME, GROUPNAME, DEVICE, IP_REAL (the real IP of the client), +# IP_REAL_LOCAL (the local interface IP the client connected), IP_LOCAL +# (the local IP in the P-t-P connection), IP_REMOTE (the VPN IP of the client), +# IPV6_LOCAL (the IPv6 local address if there are both IPv4 and IPv6 +# assigned), IPV6_REMOTE (the IPv6 remote address), IPV6_PREFIX, and +# ID (a unique numeric ID); REASON may be "connect" or "disconnect". +# In addition the following variables OCSERV_ROUTES (the applied routes for this +# client), OCSERV_NO_ROUTES, OCSERV_DNS +# STATS_BYTES_IN, +# STATS_BYTES_OUT, STATS_DURATION + +LOG_DIR="/var/log/ocserv" +LOG_FILE=${LOG_DIR}/sessions.log +# Format out string text, json +LOG_FORMAT="json" + +if [ ! -d "$LOG_DIR" ]; then + mkdir -p ${LOG_DIR} +fi + +DATE=`date +'%d.%m.%Y %T'` + +# Преобразуем имена пользователей к нижнему регистру +USER_NAME=`echo ${USERNAME} | tr '[:upper:]' '[:lower:]'` + +# Проверяем имя пользователя на имя домена, +# сперва в формате "user@domain" потом "domain\user" \\domain\user +# и выдергиваем только имя пользователя + +TEMP_USER_NAME=`echo ${USER_NAME} | egrep -E "[[:alnum:]]+@" -o | tr -d "@"` + +if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" +else + TEMP_USER_NAME=`echo $USER_NAME | awk -F '\\' '{print $2}'` + if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" + else + TEMP_USER_NAME=`echo $USER_NAME | awk -F '\\' '{print $4}'` + if [ -n "${TEMP_USER_NAME}" ]; then + SHORT_USER_NAME="${TEMP_USER_NAME}" + else + SHORT_USER_NAME="${USER_NAME}" + fi + fi +fi + +if [ ${LOG_FORMAT} == 'text' ]; then + OUT_STRING="${DATE} ${USERNAME} ${REASON} ${ID} ${STATS_BYTES_IN} ${STATS_BYTES_OUT} ${STATS_DURATION}" +fi + +if [ ${LOG_FORMAT} == 'json' ]; then + OUT_STRING="{\"session-datetime\": \"${DATE}\", \"reason\": \"${REASON}\", \"remote-user\": \"${SHORT_USER_NAME}\", \ +\"session-id\": \"${ID}\", \"duration\": ${STATS_DURATION}, \"in_bytes\": ${STATS_BYTES_IN}, \"out_bytes\": ${STATS_BYTES_OUT}}" +fi + +echo "${OUT_STRING}" >> ${LOG_FILE}