#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Краткое описание:
1. В '/var/log/gitlab/gitlab-shell/gitlab-shell.log' ищем имя проэкта, действие и 'key' юзера.
2. В базе данных ищем реальное имя пользователя и его 'fingerprint' по найденому ранее 'key'.
3. В "/var/log/auth.log" ищем IP пользователя в самой последней строчке, с нужным нам 'fingerprint'.
Строчка из gitlab-shell.log
I, [2017-10-17T12:19:56.526131 #21521] INFO -- : gitlab-shell: executing git command <git-receive-pack /var/opt/gitlab/git-data/repositories/web/markets.git> for user with key key-11.
SQL запрос для поиска 'fingerprint'
SELECT fingerprint FROM keys WHERE id = 11;
SQL запрос для поиска реального имени пользователя:
SELECT extern_uid FROM identities WHERE user_id = (SELECT user_id FROM keys WHERE id = 11);
"""
# Импортируем модули и библиотеки
from gelfHandler import GelfHandler
import logging
import psycopg2
import re
import os
import tail
import subprocess
import sys
import datetime
time=str(datetime.datetime.now())
# Включить/включить дебаг-on или отключить - off
debug = 'on'
#debug = ''
# Включить/включить отображение полного пути до файла GIT. Имя файла соответствует имени проекта.
#pach_git_all = 'on'
pach_git_all = 'off'
# Пути к лог-файлам:
log_file_GuiLab = '/var/log/gitlab/gitlab-shell/gitlab-shell.log'
log_file_SSH = '/var/log/auth.log'
# Параметры подключения к SQL. Для автоматического определения параметров, необходимо указать путь до конфигурационного файла гитлаба, либо указать свои параметры.
gitlab_rb = '/etc/gitlab/gitlab.rb'
#gitlab_rb = ''
sql_db = 'gitlab'
sql_user = 'python_user'
sql_password = 'qwer123'
sql_host = '127.0.0.1'
sql_port = '5432'
# Параметры грейлог сервера:
out_GrayLog='yes'
#out_GrayLog=''
logger = logging.getLogger()
gelfHandler = GelfHandler(
host='192.168.250.145',
port=6514,
protocol='UDP',
facility='Python_parsing_log_file_GitLab'
)
logger.addHandler(gelfHandler)
#параметры записи результатов в логфайл
out_log_file_name='/home/viktor/pars_log_GitLab.log'
#out_log_file_name=''
# Функция обработки ошибки при отсутствии лог-файлов
def funk_error_file(log_file):
print "Файл ", log_file, "не найден!!!"
out_log_file = open(out_log_file_name, 'a')
out_log_file.write(time);
out_log_file.write(" ");
out_log_file.write("Файл ");
out_log_file.write(log_file);
out_log_file.write(" не найден!!!");
out_log_file.close()
sys.exit()
# Весь скрипт это одна функция tail, она начинает выполняться, когда в файле "gitlab-shell.log" появляется новая строчка
def funk_pars_gitlab_shell(string_gitlab_shell):
# Локальные переменные:
action = 'action'
project = 'project'
user_key = 0
username = 'username'
time_ssh = 'time_ssh'
host_name = 'host_name'
id_ssh_log_message = 0
usr_name_git = 'usr_name_git'
ip_address = 'ip_address'
fingerprint_log_ssh = 'fingerprint_log_ssh'
fingerprint = 'fingerprint'
time_git = 'time_git'
id_git_log_message = 'id_git_log_message'
# Проверяем регуляркой, чтоб новая строчка соответствовала шаблону, в результате получаем массив с тремя элементами, в том числе и пустыми(предварительно проверка на полный и короткий путь до файла)
if pach_git_all == 'on':
regexp_string_gitlab_shell = re.findall(r'^.*\[([^ ]+)\.[\d]+\s\#([^ ]+)\]\s.*<([^ ]*)\s([^ ]*)>\s{1,}for user with key\s{1,}key-([^ ]*)\.$', string_gitlab_shell)
elif pach_git_all == 'off':
regexp_string_gitlab_shell = re.findall(r'^.*\[([^ ]+)\.[\d]+\s\#([^ ]+)\]\s.*<([^ ]*)\s.+repositories/([^ ]*)\.git>\s{1,}for user with key\s{1,}key-([^ ]*)\.$', string_gitlab_shell)
# Выдергиваем из полученного массива переменные
for arr_string__gitlab_shell in regexp_string_gitlab_shell:
time_git = arr_string__gitlab_shell[0] # Время записи сообщения в лог-файл GIT
id_git_log_message = arr_string__gitlab_shell[1] # Идентификатор сообщения в лог-файле GIT
action = arr_string__gitlab_shell[2] # Действие т.е. клон, пуш или что-то там ещё.
project = arr_string__gitlab_shell[3] # Проэкт в который клонили или пушили
user_key = arr_string__gitlab_shell[4] # Некий индетификатор пользователя присвоеный ему гитлабом.
# Проверим есть ли в переменной новые данные дабы не дергать лишний раз базу данный и не присылать пустоту
if action != 'action':
# Подключение к базе данных
connect = psycopg2.connect(database=sql_db, user=sql_user, password=sql_password, host=sql_host, port=sql_port)
# Открываем курсор (т.е. подключаемя к базе данных)
curs = connect.cursor()
# Формируем переменную для подстановки в SQL запрос. В запросе по идентификатору пользователя будем искать его user_id, по которому еже найдем вменяемое имя пользователя
sql_string_find_username="""SELECT extern_uid FROM identities WHERE user_id = (SELECT user_id FROM keys WHERE id = %s);""" %user_key
curs.execute(sql_string_find_username) # сам запрос
string_external_uid = curs.fetchall() # Массив с результтаом запроса
# Формируем переменную для подстановки в SQL запрос. В запросе по идентификатору пользователя будем искать его fingerprint.
sql_string_find_fingerprint="""SELECT fingerprint FROM keys WHERE id = %s;""" %user_key
curs.execute(sql_string_find_fingerprint) # сам запрос
sql_string_fingerprint = curs.fetchall() # Массив с результтаом запроса
# Закрываем соединение с базой
connect.close()
# В следующих двух циклах разбираем полученные массивы от SQL
for fingerprint_array in sql_string_fingerprint:
fingerprint = fingerprint_array[0]
for username_array in string_external_uid:
username = re.findall(r'^.*uid=([-\w\._]+)', username_array[0])
username = username[0]
# Формируем переменную и делаем системный вызов для получения последней строчки из лог-файла auth.log с полученным из SQL fingerprint.
command = """grep '%s' %s | tail -n 1""" %(fingerprint, log_file_SSH)
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
str_line = str(line)
retval = p.wait()
# Разбираем найденную сторочку из auth.log при помощи регулярки на время, IP адрес и остальные не очень нужные данные.(имя сервера, идентификатор сообщения, SSH гит пользователь)
arr_reg_exp_ssh_info = re.findall(r'^([\w*\s\d\:\s]+)\s([^ ]+)\ssshd\[(\d+)\]:\sAccepted publickey for\s([^ ]+)\sfrom\s([^ ]+)\sport\s[\s\w]+:\sRSA\s([\w\:]+)$', str_line)
# Раскладываем массив по переменным
for data_ssh_info in arr_reg_exp_ssh_info:
time_ssh = data_ssh_info[0]
host_name = data_ssh_info[1]
id_ssh_log_message = data_ssh_info[2]
usr_name_git = data_ssh_info[3]
ip_address = data_ssh_info[4]
fingerprint_log_ssh = data_ssh_info[5]
# Изменим значение переменной 'action' на более привычные нам значения.
if action == 'git-receive-pack':
action = 'push'
elif action == 'git-upload-pack':
action = 'clone'
# Печатаем переменные для отладки
if debug:
print '----'
print ' ', 'time_ssh', '\t\t', time_ssh
print ' ', 'time_git', '\t\t', time_git
print ' ', 'username','\t\t', username
print ' ', 'action', '\t\t', action
print ' ', 'project', '\t\t', project
print ' ', 'ip_address', '\t\t', ip_address
print ' ', 'user_key', '\t\t', user_key
print ' ', 'host_name', '\t\t', host_name
print ' ', 'id_ssh_log_message','\t', id_ssh_log_message
print ' ', 'id_git_log_message','\t', id_git_log_message
print ' ', 'usr_name_git', '\t', usr_name_git
print ' ', 'fingerprint_ssh', '\t', fingerprint_log_ssh
print ' ', 'fingerprint_sql', '\t', fingerprint
print '----'
print '\n'
# Формирование и отправка сообщения в грейлог
if out_GrayLog != 'no':
logger.warning(
'Now message', extra={'gelf_props': {
'title_time_ssh':time_ssh,
'title_time_git':time_git,
'title_username':username,
'title_action':action,
'title_project':project,
'title_ip_address':ip_address,
'title_user_key':user_key,
'title_host_name':host_name,
'title_id_ssh_log_message':id_ssh_log_message,
'title_id_git_log_message':id_git_log_message,
'title_fingerprint':fingerprint
}})
# Формирование и отправка сообщения в файл
if out_log_file_name:
out_log_file = open(out_log_file_name, 'a')
out_log_file.write("{time_ssh:");
out_log_file.write(time_ssh);
out_log_file.write("}{time_git:");
out_log_file.write(time_git);
out_log_file.write("}{username:");
out_log_file.write(username);
out_log_file.write("}{action:");
out_log_file.write(action);
out_log_file.write("}{project:");
out_log_file.write(project);
out_log_file.write("}{ip_address:");
out_log_file.write(ip_address);
out_log_file.write("}{user_key:");
out_log_file.write(user_key);
out_log_file.write("}{host_name:");
out_log_file.write(host_name);
out_log_file.write("}{id_ssh_log_message:");
out_log_file.write(id_ssh_log_message);
out_log_file.write("}{id_git_log_message:");
out_log_file.write(id_git_log_message);
out_log_file.write("}{fingerprint:");
out_log_file.write(fingerprint);
out_log_file.write("}");
out_log_file.write("\n");
out_log_file.close()
# Выполним проверку на наличие лог файлов, и корректно закроем скрипт если их нет(вызовом соответствующей функции)
if os.path.exists(log_file_GuiLab):
if os.path.exists(log_file_SSH):
if gitlab_rb:
command_searh_db = """grep "gitlab_rails\['db_" %s|tr -s '\\n' '\ '""" %gitlab_rb
p = subprocess.Popen(command_searh_db, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
str_db_line = str(line)
retval = p.wait()
arr_db_con = re.findall(r'^.*db_database\'\]\s\=\s\"([^ ]+)\".*db_username\'\]\s\=\s\"([^ ]+)\".*db_password\'\]\s\=\s\"([^ ]+)\".*db_host\'\]\s\=\s\"([^ ]+)\".*db_port\'\]\s\=\s([^ ]+)\s.*$', str_db_line)
# Раскладываем массив по переменным
for data_db_info in arr_db_con:
sql_db = data_db_info[0]
sql_user = data_db_info[1]
sql_password = data_db_info[2]
sql_host = data_db_info[3]
sql_port = data_db_info[4]
# Если включен дебаг, выводим шапку при запуске
if debug:
print '\n'
print 'debug ==> on'
print 'Start', (os.path.basename(__file__))
print '\n'
print 'sql_db ==> ', sql_db
print 'sql_user ==> ', sql_user
print 'sql_password ==> ', sql_password
print 'sql_host ==> ', sql_host
print 'sql_port ==> ', sql_port
print '----'
# Запускаем главную функцию
t = tail.Tail(log_file_GuiLab) # 'log_file_GuiLab' - Файл который будем парсить тайлом
t.register_callback(funk_pars_gitlab_shell) # Вызов сомой функции 'funk_pars_gitlab_shell'
t.follow(s=1) # Частота парсинга файла
funk_error_file(log_file_SSH)
funk_error_file(log_file_GuiLab)