воскресенье, 23 августа 2015 г.

Инкрементное резервное копирование с помощью Rsnapshot.


В данной статье разберем создание инкрементных резервных копий с помощью Rsnapshot.
Главное достоинство Rsnapshot, это простота получения файлов из резервной копии. У вас есть прямой доступ к папкам с резервными копиями по периодам копирования.
Как обычно, платформа для установки Debian 8.

1) Установка.
apt-get install rsnapshot
После установки, файл настроек доступен по пути: /etc/rsnapshot.conf.

2) Настройка.
mv /etc/rsnapshot.conf /etc/rsnapshot.conf.BACK
echo "
####Версия программы
config_version  1.2
####Где будут хранится резервные копии
snapshot_root   /home/where_we_do_backup/
####Что будем резервировать и как назвать копию
backup  /home/what_we_do_backup/   snapshots/
####Основные команды
cmd_cp          /bin/cp
cmd_rm          /bin/rm
cmd_rsync       /usr/bin/rsync
cmd_logger      /usr/bin/logger
####Задания копирования
#Резервные копии за каждый день в течении 60 дней
retain          daily   60
#Резервные копии 2 раза в месяц, 23 раза = почти 12 месяцев
#retain         weekly  23
####Уровень логов
# 1     Quiet           Print fatal errors only
# 2     Default         Print errors and warnings only
# 3     Verbose         Show equivalent shell commands being executed
# 4     Extra Verbose   Show extra verbose information
# 5     Debug mode      Everything
verbose         3
loglevel        3
####Файл лога
logfile         /var/log/rsnapshot.log
####pid
lockfile        /var/run/rsnapshot.pid
" >> /etc/rsnapshot.conf
В данном файле, как видно, есть 3 основные настройки:
1) snapshot_root - путь, где будут хранится наши резервные копии
2) backup - путь до папки, которую мы будем резервировать (/home/what_we_do_backup/) и папка, которая создастся в snapshot_root для этого пути (snapshots/)
3) retain - названия резервирования (daily/weekly) и количество копий, которые будет хранить Rsnapshot.

ВАЖНО! вместо пробелов в файле /etc/rsnapshot.conf используйте ТАБУЛЯЦИЮ

3) Автоматизация.
Для отладки процесса, можно использовать команду:
rsnapshot configtest
Которая проверит правильность конфигурации.
Либо:
rsnapshot -t daily
Которая уже протестирует вашу настройку под именем daily.
Если написать
rsnapshot daily
То произойдет резервное копирование по настройке daily.

Rsnapshot при установке создает собственный файл в Cron по пути /etc/cron.d/rsnapshot, можно использовать его, но нам необходимо получить подробный отчет о проделанной работе, поэтому создадим следующий файл:
echo "
#!/bin/sh
# Бекап с помощью rsnapshot
### Настройки ###
#Ошибки (по умолчанию нет)
ERRORS="no errors"
#Получатели письма счастья )
MAILTO='admin@myil.ru'
MAILFROM='backuper@myil.ru'
SUBJECTED='INCREMENT BACKUP report ('$ERRORS')'
LOGFILE=/tmp/rsnapshot.log.z
#расположение rsnapshot
RS=/usr/bin/rsnapshot
#Передаем параметр со скриптом (запуск скрипта в ежедневном или другом режиме)
PARAM=$1
#Путь до папки, которую будем проверять до и после бэкапа на изменение размера
ZERODAYPATH=/home/where_we_do_backup/daily.0/snapshots
#Временный файл, в который будет записан результат проверки размеров файлов папки ZERODAYPATH
TMPFILE=/tmp/size2.tmp
#Строка проверки размера папки
HOWMUCH='/usr/bin/du -sh *'


#Чистим лог для начала
cat /dev/null > $LOGFILE

##Записываем в начало лога данные, которые будут использованы при отправке письма
echo "To:$MAILTO
From:$MAILFROM
Subject: $SUBJECTED
Content-Type: text/plain; charset=UTF-8; format=flowed
Hi all" >> $LOGFILE

#Дописываем в лог разделитель
echo "
------------------------------------------------------------------
Before backup:
" >> $LOGFILE
#Нужно записать в лог размер содержимого из /BACKUP/daily.0/AUP
#Проверяем осталась ли запись после бэкапа с прошлого раза, если да то пишем ее
#Если нет, делаем du чтобы узнать размер и записываем в лог
if [ -f $TMPFILE ]; then
cat $TMPFILE >> $LOGFILE
else
cd $ZERODAYPATH
$HOWMUCH >> $LOGFILE
fi
#Дописываем в лог разделитель
echo "------------------------------------------------------------------" >> $LOGFILE

#Записываем время начала операции копирования
STARTTIME="$(date +%H:%M:%S)"

###########################################################################
## Запускаем бэкап
$RS $PARAM >> $LOGFILE 2>&1
###########################################################################

#Дописываем в лог разделитель
echo "
------------------------------------------------------------------
After backup:
" >> $LOGFILE
#Нужно записать в лог размер содержимого из /BACKUP/daily.0/AUP после бэкапа
#Проверяем осталась ли запись после бэкапа с прошлого раза, если да то удаляем
#Делаем du чтобы узнать размер и записываем в лог и файл
if [ -f $TMPFILE ]; then
rm $TMPFILE
fi
cd $ZERODAYPATH
$HOWMUCH >> $TMPFILE
cat $TMPFILE >> $LOGFILE

#Дописываем в лог разделитель
echo "------------------------------------------------------------------" >> $LOGFILE

#Записываем время начала и конца операции
ENDTIME="$(date +%H:%M:%S)"
sed -i '5a Start time: '$STARTTIME $LOGFILE
sed -i '6a End time: '$ENDTIME $LOGFILE

###Проверка на ошибки
##Если будет найдена ошибка в логе, переменная ERRORS будет изменена
##и в теме письма будет крупно написано ERRORS!!
if [ `cat $LOGFILE | grep 'ERROR:' | grep -cv grep` != "0" ]; then
        grep -i -e 's/no errors/ERRORS!!/' $LOGFILE
fi

#Отправка почты с помошью ssmtp
/usr/sbin/ssmtp -t < $LOGFILE

exit 0
" >> /etc/backuper_rsnapshot.bin
Сделаем файл исполняемым:
chmod +x /etc/backuper_rsnapshot.bin
Т.к. для отправки отчета используем ssmtp, установим ее:
apt-get install ssmtp
И настроим:
mv /etc/ssmtp/ssmtp.conf /etc/ssmtp/ssmtp.conf.BACK
echo "
###Тема для тестовых сообщений
Subject: test message
###Адрес почтового сервера
mailhub=smtp.myil.ru
###От кого будет посылаться сообщение
hostname=backuper@myil.ru
###Использовать шифрование STARTTLS
UseSTARTTLS=no
###метод авторизации
AuthMethod=LOGIN
###Логин
AuthUser=backuper
###Пароль
AuthPass=backuper_psw
###Разрешить изменить адрес отправителя (hostname)
FromLineOverride=YES
" >> /etc/ssmtp/ssmtp.conf
Теперь добавим соответствующую задачу в Cron. Напишите crontab -e, и добавьте следующую строку:
###Бэкап в 0 часов 5 минут каждые 2 дня
5 0 */2 * * /etc/backuper_rsnapshot.bin daily
Теперь, после выполнения скрипта, на почтовый адрес admin@myil.ru должно прийти примерно следующее письмо:
Hi all
Start time: 00:05:02
End time: 00:13:08

------------------------------------------------------------------
Before backup:

248G    Back_Folder1
22G    Back_Folder2
9.1G    Back_Folder3
4.4G    Back_Folder4
------------------------------------------------------------------
echo 21866 > /var/run/rsnapshot.pid mv /home/where_we_do_backup/daily.6/ /home/where_we_do_backup/daily.7/ mv /home/where_we_do_backup/daily.5/ /home/where_we_do_backup/daily.6/ mv /home/where_we_do_backup/daily.4/ /home/where_we_do_backup/daily.5/ mv /home/where_we_do_backup/daily.3/ /home/where_we_do_backup/daily.4/ mv /home/where_we_do_backup/daily.2/ /home/where_we_do_backup/daily.3/ mv /home/where_we_do_backup/daily.1/ /home/where_we_do_backup/daily.2/ /bin/cp -al /home/where_we_do_backup/daily.0 /home/where_we_do_backup/daily.1 /usr/bin/rsync -a --delete --numeric-ids --relative --delete-excluded /AUP \
   /home/where_we_do_backup/daily.0/snapshots/ touch /home/where_we_do_backup/daily.0/ rm -f /var/run/rsnapshot.pid
------------------------------------------------------------------
After backup:

249G    Back_Folder1
23G    Back_Folder2
9.1G    Back_Folder3
4.5G    Back_Folder4
------------------------------------------------------------------ 

четверг, 20 августа 2015 г.

Cisco IVR TCL. Или пишем голосовое IVR меню на языке TCL, с использованием Cisco IVR API.


Сегодня речь о голосовом меню IVR, которое мы будем создавать на языке TCL, и внедрять на маршрутизатор Cisco.
Итак, для начала давайте разберемся в азах.
Cisco начиная с версии IOS 12 поддерживает как VXML так и TCL скрипты для работы с голосовым меню. Однако, в отличии от VXML, скрипты на TCL имеют гораздо больше возможностей взаимодействия с IVR API Cisco.
Все документы, связанные с IVR от Cisco, которые мне довелось получить можно скачать здесь.

Первое, что при изучении голосового меню от Cisco очень трудно понять, дак это так называемые FSM переходы.
Выглядит это примерно так:
set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup same_state"
Переходов таких может быть сколько угодно, и расположены они в конце TCL скрипта.
Давайте разберемся, что это вообще такое. Общий синтаксис этой команды таков:
set array(CURRSTATE,curr_event) “act_proc NEXTSTATE”
где:
  array – это имя fsm массива.
  CURRSTATE – имя текущего состояния скрипта, при котором получено событие curr_event.
  act_proc – имя функции-обработчика события, которую необходимо выполнить при поступлении события curr_event в состоянии curr_state.
  NEXTSTATE – имя состояния, в которое должен перейти звонок.
Т.е. FSM это маркер, по которому Cisco сравнивает полученное от API состояние с curr_event и текущий статус с CURRSTATE, если в каком либо FSM переходе они описаны, вызывается процедура act_proc и состояние изменяется на NEXTSTATE.
Самое главное в этом - это то, что текущее состояние и статус сравниваются со всеми описанными FSM переходами ОДНОВРЕМЕННО. Т.е. для Cisco не имеет значения порядок, в котором расположены FSM переходы, все они обрабатываются одновременно.

Второй момент, это сами функции, которые должны быть описаны до инициализации скрипта. В данных функциях уже есть что то знакомое обычным программистам. Так например, объявление переменных должно быть до вызова этих самых переменных. Все. Остальная работа - уникальна.
Например последовательность, ставшая норой во всех ООИП. Если у вас есть 2 команды:
media play leg_incoming $playng_files(noexist)
leg setup $numbers(ckp) callinfo leg_incoming
То по логике объекто-ориентированного (или не очень) языка программирования, у вас сначала выполнится:
media play
а затем:
leg setup
Здесь тоже выполнится в таком же порядке, но интерпретатор НЕ БУДЕТ ДОЖИДАТЬСЯ выполнения 1й команды, и сразу после запуска медиа будет выполнена следующая команда. Это частный случай и относится он в основном только к проигрыванию медиа-файлов абоненту. Окончание проигрывания обрабатывается уже за счет FSM переходов.

Назначение всех команд и состояний подробно описано в файле tcl_ivr_2.0_programming_guide, который вы можете скачать здесь, я поподробнее останавлюсь только на тех, которые буду использовать непосредственно в скрипте

1) Инициализация. Или подключение скрипта.
Начало любого TCL IVR скрипта содержит процедуру init:
proc init { } {
    puts "\n proc Init start"
    global param
}
Запуск которого осуществляется так же просто:
init
На этом простые вещи закончились, дальше все гораздо интереснее ;)
Последней исполняемой строкой скрипта должна быть строка определения стартового FSM перехода и стартового состояния. В нашем случае это:
fsm define ivr_fsm CALLCOMES
Это значит, что имя FSM массива задано как ivr_fsm, и стартовое состояние CALLCOMES
С инициализацией закончим, дальше будет понятнее, что происходит.

2) Приветствие.
proc Play_Welcome { } {
 puts "\n\n IVR - proc Play_Welcome start \n\n"
 global playng_files
 global param
 global pattern
 global numbers
 global workingtime
 
 #Вызываем процедуру, где описаны все переменные
 init_perCallVars
 
 #Получаем время
 GetDate
 #В зависимости рабочее сейчас время или нет, устанавливаем приветствие 
 if {$workingtime} {
 set after_welcome $playng_files(takenumber) 
 } else {
 set after_welcome $playng_files(noworking)
 }
 
 #Устанавливаем параметры подключения входящего вызова
 set param(interruptPrompt) true
 set param(abortKey) *
 set param(terminationKey) # 
 
 #Подключаем входящий вызов
 leg setupack leg_incoming
 leg proceeding leg_incoming
 leg connect leg_incoming
 
 #Запускаем процедуру сбора нажатых цифр со стороны звонящего
 leg collectdigits leg_incoming param pattern
 #Запускаем проигрыш файлов звонящему абоненту, после их окончания начнет действовать 
 таймер param(interDigitTimeout), по истечении которого будет событие ev_collectdigits_done
 media play leg_incoming %s500 $playng_files(welcome) $after_welcome $playng_files(onhold)
 #Запускаем таймер, по истечении которого произойдет событие ev_named_timer
 timer start named_timer $numbers(waiting_time) t1
}
Здесь довольно подробно все описано.
Результатом выполнения данной процедуры будет подключение входящей линии к Cisco за счет команд leg setupack, leg proceeding, leg connect, и проигрыш музыкальных файлов по очереди во входящую линию за счет команды media play leg_incoming. Тут же запускается процесс сбора нажатых клавиш (leg collectdigit) и таймер (timer start).
Так как стартовое состояние, заданное в нашем случае как fsm define ivr_fsm CALLCOMES, в него попадают сразу 3 FSM:
set ivr_fsm(CALLCOMES,ev_setup_indication)    "Play_Welcome,   same_state"
set ivr_fsm(CALLCOMES,ev_collectdigits_done)  "CheckDestanation,  same_state"
set ivr_fsm(CALLCOMES,ev_named_timer)          "GoToReception,  same_state"
Событие ev_setup_indication произойдет при поступлении звонка, и будет запущена процедура Play_Welcome, в которой описан старт процесса сбора нажатых цифр и старт таймера.
После окончания проигрывания музыки абоненту, начнется обратный отчет таймера param(initialDigitTimeout) (который можно было задать чуть выше строкой set param(initialDigitTimeout) 15), т.к. он у нас не указан, его стандартное значение 10 секунд, после чего скрипт получит событие ev_collectdigits_done, при наступлении которого, как мы описали в FSM переходе, будет выполнена функция CheckDestanation.
Таймер, запущенный в Play_Welcome командой:
#Тип таймера named_timer, длительность, взята из переменной numbers(waiting_time), имя таймера t1
timer start named_timer $numbers(waiting_time) t1
После своего окончания сгенерирует событие ev_named_timer, которое будет обработано следующим FSM переходом и вызовется процедура GoToReception.

3) Проверка введенного номера.
proc CheckDestanation { } {
    puts "\n\n IVR - proc CheckDestanation start \n\n"
 global playng_files
 global numbers
 global digit
 #Останавливаем проигрыш медиа
 media stop leg_incoming
 
 #Определяем значение переменным
 set status [infotag get evt_status]
 set digit [infotag get evt_dcdigits]
 
 #Сравниваем полученные цифры и статусы
 #Если введенная цифра соответствует той, что задана в $numbers(fast_reception), 
 #подключаем на ресепшн, предварительно изменив статус на CALLCONNECTED, благодаря которому,
 #при наступлении события ev_setup_done (подключение к номеру секретаря) будет отработана процедура CallIsConnect
 if {$digit == $numbers(fast_reception)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(reception)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если введенная цифра соответствует той, что задана в $numbers(fast_ckp), подключаем на ЦКП
 } elseif {$digit == $numbers(fast_ckp)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next CKP \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(ckp)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если введенная цифра соответствует той, что задана в $numbers(fast_fax), подключаем на факс
 } elseif {$digit == $numbers(fast_fax)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next fax \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(fax)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если статус = cd_004 (введены корректные цифры номера) - подключаем к нужному номеру
 } elseif {$status == "cd_004"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate CALLCONNECTED
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если статус = cd_005 (совпадение с dial plan) - подключаем к нужному номеру 
 } elseif {$status == "cd_005"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate CALLCONNECTED 
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit 
 #Если статус = cd_006 (набран не существующий номер) - играем в линию $playng_files(noexist)
 #и изменяем статус на TORECEPTION, при действии которого и наступлении события ev_media_done
 #(конец проигрывания звукового файла) вызовется процедура GoToReception
 } elseif {$status == "cd_006"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noexist)
 #Во всех остальных случаях изменяем статус на TORECEPTION, при действии которого и 
 #наступлении события ev_media_done (конец проигрывания звукового файла) вызовется процедура GoToReception
 } else {
  #Проигрываем "Ваш вызов переадресовывается на секретаря"
  fsm setstate TORECEPTION
  media play leg_incoming $playng_files(toreception)  
  puts "\n\n IVR - proc CheckDestanation status = $status \n\n"
 } 
}
В процедуре CheckDestanation, которая будет вызвана после набора (или не набора) номера звонящим абонентом, мы сравниваем полученные при наборе цифры с настройками и переводим скрипт в соответствующее состояние командой fsm setstate.
Все состояния, попавшие в функцию, попадают под следующие FSM переходы:
set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"
set ivr_fsm(TORECEPTION,ev_media_done)           "GoToReception,  same_state"
set ivr_fsm(TRYAGAIN,ev_media_done)           "Play_TakeNumber,  TRYING"
set ivr_fsm(TRYING,ev_collectdigits_done)  "CheckDestanation,  same_state"
set ivr_fsm(TRYING,ev_named_timer)   "GoToReception,  same_state"
Давайте по порядку.
Итак, изначально функция CheckDestanation вызывается после окончания процедуры сбора нажатия клавиш.
Информацию о нажатых клавишах мы записываем в переменную digit с помощью команды set digit [infotag get evt_dcdigits]
Аналогично записываем состояние линии в переменную status
Затем сравниваем полученные результаты с заданными переменными и изменяем состояние скрипта при совпадении:
if {$digit == $numbers(fast_reception)} {
 puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n"
 fsm setstate CALLCONNECTED
 leg setup $numbers(reception) callinfo leg_incoming
}

4) Подключение номера.
proc CallIsConnect { } {
 puts "\n\n IVR - proc CallIsConnect start \n\n"
 global playng_files 

 #Определяем чему равен status
 set status [infotag get evt_status]
 
 #Если статус равен ls_000 (успешное соединение с требуемым номером), изменяем состояние на CALLACTIVE
 if {$status == "ls_000"} {
 fsm setstate CALLACTIVE 
 
 #Если статус равен ls_002 (никто не ответил на звонок), запускаем процедуру запроса номера
 } elseif {$status == "ls_002"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noanswer)
 #Если статус - неверный номер, запускаем процедуру запроса номера
 } elseif {$status == "ls_004" || $status == "ls_005" || $status == "ls_006"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noexist)
 #Если статус равен ls_007 (абонент занят), запускаем процедуру запроса номера
 } elseif {$status == "ls_007"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(busy)
 }
}
Данная функция вызывается следующим FSM переходом:
set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"
Событие ev_setup_done наступает после подключения звонящего к требуемой линии.

5) Разрыв соединения.
proc AbortCall { } {
 puts "\n\n IVR - proc AbortCall start \n\n"
 call close
}
Вызывается следующими FSM:
set ivr_fsm(any_state,ev_disconnected)      "AbortCall,   same_state"
set ivr_fsm(CALLACTIVE,ev_disconnected)     "AbortCall,  CALLDISCONNECTED"
set ivr_fsm(CALLDISCONNECTED,ev_disconnected)   "AbortCall,  same_state"
set ivr_fsm(CALLDISCONNECTED,ev_media_done)    "AbortCall,  same_state"
set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done)  "AbortCall,  same_state"

6) Полная версия скрипта.
Выше были рассмотрены только основные функции скрипта, далее полный текст, имейте в виду, это практически самый простой вариант:
#######################################################
# Cisco IVR TCL script by Konovalov D.A. v.2
#######################################################
#
# Для дебага скрипта
#  debug voip application script
# Более полный дебага (не рекомендуется, может привести к перегрузке)
#   debug voip ivr
# 
# Скрипт должен быть запущен со следующими параметрами:
#  param allowed_pattern [5-7]..
#  param fastto_reception 1
#  param reception_number 5501
# param fastto_ckp 2
#  param ckp_number 5604
#  param fastto_fax 3
#  param fax_number 5799
#  param waiting_time 20
#  param max_try 3
#  param file_welcome flash:en_welcome.au
#  param file_takenumber flash:en_takenumber.au
#  param file_after flash:en_after.au
#  param file_busy flash:en_busy.au
#  param file_noexist flash:en_noexist.au
#  param file_noanswer flash:en_noanswer.au
#  param file_onhold flash:music-on-hold.au
#  param file_noworking flash:music-on-hold.au


#Процедура инициализации скрипта
proc init { } {
    puts "\n proc Init start"
    global param
}

#Процедура с переменными
proc init_perCallVars { } {
 global pattern
 global numbers
 global playng_files
 
 #####Допустимая нумерация
 #Если в параметрах скрипта не указана допустимая нумерация, будет установлено значение .... - 4 любых цифры
 if {[infotag get cfg_avpair_exists allowed_pattern]} {
  set pattern(1) [string trim [infotag get cfg_avpair allowed_pattern]]
  puts "\n\n IVR - Allowed pattern set as: $pattern(1) \n\n"
  } else {
   set pattern(1) ....
   puts "\n\n IVR - Allowed pattern set as DEFAULT: $pattern(1) \n\n"
   } 
 #####Номера
 #Секретарь. Если в параметрах скрипта не указан номер секретаря, номер будет установлен в 0000
 if {[infotag get cfg_avpair_exists reception_number]} {
  set numbers(reception) [string trim [infotag get cfg_avpair reception_number]]
  puts "\n\n IVR - reception number set as: $numbers(reception) \n\n"
  } else {   
   set numbers(reception) 0000
   puts "\n\n IVR - reception number set as DEFAULT: $numbers(reception) \n\n"
   } 
 #ЦКП 
 if {[infotag get cfg_avpair_exists ckp_number]} {
  set numbers(ckp) [string trim [infotag get cfg_avpair ckp_number]]
  puts "\n\n IVR - ckp number set as: $numbers(ckp) \n\n"
  } else {   
   set numbers(ckp) 0000
   puts "\n\n IVR - ckp number set as DEFAULT: $numbers(ckp) \n\n"
   }
 #Факс 
 if {[infotag get cfg_avpair_exists fax_number]} {
  set numbers(fax) [string trim [infotag get cfg_avpair fax_number]]
  puts "\n\n IVR - fax number set as: $numbers(fax) \n\n"
  } else {   
   set numbers(fax) 0000
   puts "\n\n IVR - fax number set as DEFAULT: $numbers(fax) \n\n"
   }   
 #Быстрый перевод на Ресепшн 
 if {[infotag get cfg_avpair_exists fastto_reception]} {
  set numbers(fast_reception) [string trim [infotag get cfg_avpair fastto_reception]]
  puts "\n\n IVR - fast to reception set as: $numbers(fast_reception) \n\n"
  } else {   
   set numbers(fast_reception) 1
   puts "\n\n IVR - fast to reception set as DEFAULT: $numbers(fast_reception) \n\n"
   }
 #Быстрый перевод на ЦКП 
 if {[infotag get cfg_avpair_exists fastto_ckp]} {
  set numbers(fast_ckp) [string trim [infotag get cfg_avpair fastto_ckp]]
  puts "\n\n IVR - fast to ckp set as: $numbers(fast_ckp) \n\n"
  } else {   
   set numbers(fast_ckp) 2
   puts "\n\n IVR - fast to ckp set as DEFAULT: $numbers(fast_ckp) \n\n"
   }
 #Быстрый перевод на факс 
 if {[infotag get cfg_avpair_exists fastto_fax]} {
  set numbers(fast_fax) [string trim [infotag get cfg_avpair fastto_fax]]
  puts "\n\n IVR - fast to fax set as: $numbers(fast_fax) \n\n"
  } else {   
   set numbers(fast_fax) 3
   puts "\n\n IVR - fast to fax set as DEFAULT: $numbers(fast_fax) \n\n"
   }
 #Время ожидания введения номера (должно быть больше времени проигрыша всех файлов приветствия)
 if {[infotag get cfg_avpair_exists waiting_time]} {
  set numbers(waiting_time) [string trim [infotag get cfg_avpair waiting_time]]
  puts "\n\n IVR - wait number set as: $numbers(waiting_time) \n\n"
  } else {   
   set numbers(waiting_time) 10
   puts "\n\n IVR - wait number set as DEFAULT: $numbers(waiting_time) \n\n"
   }
 #Количество попыток ввести правильный номер, прежде чем звонок будет переведен на секретаря
 if {[infotag get cfg_avpair_exists max_try]} {
  set numbers(max_try) [string trim [infotag get cfg_avpair max_try]]
  puts "\n\n IVR - max try set as: $numbers(max_try) \n\n"
  set numbers(cur_try) 0
  } else {   
   set numbers(max_try) 5
   puts "\n\n IVR - max try set as DEFAULT: $numbers(max_try) \n\n"
   set numbers(cur_try) 0
   }
 #####Музыкальные файлы, которые будут проигрываться  
 #Файл приветствия
 if {[infotag get cfg_avpair_exists file_welcome]} {
  set playng_files(welcome) [string trim [infotag get cfg_avpair file_welcome]]
  puts "\n\n IVR - file_welcome set as: $playng_files(welcome) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(welcome) %s1
   puts "\n\n IVR - file_welcome set as DEFAULT: $playng_files(welcome) \n\n"
   }
 #Файл запроса ввести требуемый номер
 if {[infotag get cfg_avpair_exists file_takenumber]} {
  set playng_files(takenumber) [string trim [infotag get cfg_avpair file_takenumber]]
  puts "\n\n IVR - file_takenumber set as: $playng_files(takenumber) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(takenumber) %s1
   puts "\n\n IVR - file_takenumber set as DEFAULT: $playng_files(takenumber) \n\n"
   }
 #Файл "Пожалуйста перезвоните позднее"
 if {[infotag get cfg_avpair_exists file_after]} {
  set playng_files(callafter) [string trim [infotag get cfg_avpair file_after]]
  puts "\n\n IVR - file_after set as: $playng_files(callafter) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(callafter) %s1
   puts "\n\n IVR - file_after set as DEFAULT: $playng_files(callafter) \n\n"
   }
 #Файл "Номер занят"
 if {[infotag get cfg_avpair_exists file_busy]} {
  set playng_files(busy) [string trim [infotag get cfg_avpair file_busy]]
  puts "\n\n IVR - file_busy set as: $playng_files(busy) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(busy) %s1
   puts "\n\n IVR - file_busy set as DEFAULT: $playng_files(busy) \n\n"
   }
 #Файл "Номер не существует"
 if {[infotag get cfg_avpair_exists file_noexist]} {
  set playng_files(noexist) [string trim [infotag get cfg_avpair file_noexist]]
  puts "\n\n IVR - file_noexist set as: $playng_files(noexist) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(noexist) %s1
   puts "\n\n IVR - file_noexist set as DEFAULT: $playng_files(noexist) \n\n"
   }
 #Файл "Соеденяю с секретарем/оператором"
 if {[infotag get cfg_avpair_exists file_toreception]} {
  set playng_files(toreception) [string trim [infotag get cfg_avpair file_toreception]]
  puts "\n\n IVR - file_toreception set as: $playng_files(toreception) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(toreception) %s1
   puts "\n\n IVR - file_toreception set as DEFAULT: $playng_files(toreception) \n\n"
   }
 #Файл "Номер не отвечает, перезвоните позднее"
 if {[infotag get cfg_avpair_exists file_noanswer]} {
  set playng_files(noanswer) [string trim [infotag get cfg_avpair file_noanswer]]
  puts "\n\n IVR - file_noanswer set as: $playng_files(noanswer) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(noanswer) %s1
   puts "\n\n IVR - file_noanswer set as DEFAULT: $playng_files(noanswer) \n\n"
   }
 #Файл музыки, которая будет проигрываться при ожидании
 if {[infotag get cfg_avpair_exists file_onhold]} {
  set playng_files(onhold) [string trim [infotag get cfg_avpair file_onhold]]
  puts "\n\n IVR - file_onhold set as: $playng_files(onhold) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(onhold) %s1
   puts "\n\n IVR - file_onhold set as DEFAULT: $playng_files(onhold) \n\n"
   }
 #Файл музыки, которая будет проигрываться В нерабочее время
 if {[infotag get cfg_avpair_exists file_noworking]} {
  set playng_files(noworking) [string trim [infotag get cfg_avpair file_noworking]]
  puts "\n\n IVR - file_noworking set as: $playng_files(noworking) \n\n"
  } else {
   #Если файл не найден, он будет заменен на тишину в 1мс
   set playng_files(noworking) %s1
   puts "\n\n IVR - file_noworking set as DEFAULT: $playng_files(noworking) \n\n"
   }
}

proc GetDate { } {
 global workingtime
 
 #Час
 set houris [clock format [clock seconds] -format %H]
 #День недели
 set dayis [clock format [clock seconds] -format %A]
 #Проверяем рабочее время
 if {$houris > 17 || $houris < 8 || $dayis=="Sunday" || $dayis=="Saturday"} {
 set workingtime 0
 } else {
 set workingtime 1
 }
}

#Процедура проигрыша приветствия
proc Play_Welcome { } {
    puts "\n\n IVR - proc Play_Welcome start \n\n"
 global playng_files
 global param
 global pattern
 global numbers
 global workingtime
 
 #Вызываем процедуру, где описаны все переменные
 init_perCallVars
 
 #Получаем время
 GetDate
 #В зависимости рабочее сейчас время или нет, устанавливаем приветствие 
 if {$workingtime} {
 set after_welcome $playng_files(takenumber) 
 } else {
 set after_welcome $playng_files(noworking)
 }
 
 #Устанавливаем параметры подключения входящего вызова
 set param(interruptPrompt) true
 set param(abortKey) *
 set param(terminationKey) # 
 
 #Подключаем входящий вызов
 leg setupack leg_incoming
 leg proceeding leg_incoming
 leg connect leg_incoming
 
 #Запускаем процедуру сбора нажатых цифр со стороны звонящего
 leg collectdigits leg_incoming param pattern
 #Запускаем проигрыш файлов звонящему абоненту, после их окончания начнет действовать 
 таймер param(interDigitTimeout), по истечении которого будет событие ev_collectdigits_done
 media play leg_incoming %s500 $playng_files(welcome) $after_welcome $playng_files(onhold)
 #Запускаем таймер, по истечении которого произойдет событие ev_named_timer
 timer start named_timer $numbers(waiting_time) t1
}

#Процедура запроса ввести номер
proc Play_TakeNumber { } {
    puts "\n\n IVR - proc Play_TakeNumber start \n\n"
 global playng_files
 global numbers
 global param
 global pattern
 
 #Проверяем какой раз абонент пытается набрать номер
 if {$numbers(cur_try) <= $numbers(max_try)} {
 puts "\n\n IVR - proc Play_TakeNumber current try is: $numbers(cur_try) \n\n"
 incr numbers(cur_try)
 #Запускаем процедуру сбора нажатых цифр со стороны звонящего
 leg collectdigits leg_incoming param pattern
 #Запускаем проигрыш файлов
 media play leg_incoming $playng_files(takenumber)
 #Запускаем таймер, по истечении которого произойдет событие ev_named_timer
 timer start named_timer $numbers(waiting_time) t1
 
 #Если попытка больше чем $numbers(max_try) - разъединяем
 } else { 
  fsm setstate CALLDISCONNECTED
  media play leg_incoming $playng_files(callafter) 
 }
}

#Процедура перевода звонка на секретаря
proc GoToReception { } {
 puts "\n\n IVR - proc GoToReception start \n\n"
 global numbers
 #Останавливаем проигрыш медиа
 media stop leg_incoming
 #Меняем состояние
 fsm setstate CALLCONNECTED
 
 set digit $numbers(reception)

 #Передаем $digit в функцию CheckCallersAndConnect
 CheckCallersAndConnect $digit
}

#Здесь проверяем введенные или не введенные звонящим цифры 
proc CheckDestanation { } {
    puts "\n\n IVR - proc CheckDestanation start \n\n"
 global playng_files
 global numbers
 global digit
 #Останавливаем проигрыш медиа
 media stop leg_incoming
 
 #Определяем значение переменным
 set status [infotag get evt_status]
 set digit [infotag get evt_dcdigits]
 
 #Сравниваем полученные цифры и статусы
 #Если введенная цифра соответствует той, что задана в $numbers(fast_reception), 
 #подключаем на ресепшн, предварительно изменив статус на CALLCONNECTED, благодаря которому,
 #при наступлении события ev_setup_done (подключение к номеру секретаря) будет отработана процедура CallIsConnect
 if {$digit == $numbers(fast_reception)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next reception \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(reception)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если введенная цифра соответствует той, что задана в $numbers(fast_ckp), подключаем на ЦКП
 } elseif {$digit == $numbers(fast_ckp)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next CKP \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(ckp)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если введенная цифра соответствует той, что задана в $numbers(fast_fax), подключаем на факс
 } elseif {$digit == $numbers(fast_fax)} {
  puts "\n\n IVR - proc CheckDestanation digit = $digit\nGoing to next fax \n\n"
  fsm setstate CALLCONNECTED
  set digit $numbers(fax)
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если статус = cd_004 (введены корректные цифры номера) - подключаем к нужному номеру
 } elseif {$status == "cd_004"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate CALLCONNECTED
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit
 #Если статус = cd_005 (совпадение с dial plan) - подключаем к нужному номеру 
 } elseif {$status == "cd_005"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate CALLCONNECTED 
  #Передаем $digit в функцию CheckCallersAndConnect
  CheckCallersAndConnect $digit 
 #Если статус = cd_006 (набран не существующий номер) - играем в линию $playng_files(noexist)
 #и изменяем статус на TORECEPTION, при действии которого и наступлении события ev_media_done
 #(конец проигрывания звукового файла) вызовется процедура GoToReception
 } elseif {$status == "cd_006"} {
  puts "\n\n IVR - proc CheckDestanation status = $status digit = $digit \n\n"
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noexist)
 #Во всех остальных случаях изменяем статус на TORECEPTION, при действии которого и 
 #наступлении события ev_media_done (конец проигрывания звукового файла) вызовется процедура GoToReception
 } else {
  #Проигрываем "Ваш вызов переадресовывается на секретаря"
  fsm setstate TORECEPTION
  media play leg_incoming $playng_files(toreception)  
  puts "\n\n IVR - proc CheckDestanation status = $status \n\n"
 } 
}

#Проверяем звонящего, со списком, если будет совпадение, сменим Имя, которое будет отображаться на вызываемом телефоне
proc CheckCallersAndConnect {digit} {
 puts "\n\n IVR - proc CheckCallersAndConnect start \n\n"
 
 set callernumber [infotag get leg_ani]
 
 switch $callernumber {
  "9120000000" {set callInfo(displayInfo) "Director(mobile)"}
  "9130000000" {set callInfo(displayInfo) "Buhgalter(mobile)"}  
  default {} 
 }
 puts "\n\n IVR - caller is $callernumber connect with $digit\n\n"
 
 leg setup $digit callInfo leg_incoming
}

#Процедура проверки состоянии линии после подключения звонящего к требуемому номеру
proc CallIsConnect { } {
 puts "\n\n IVR - proc CallIsConnect start \n\n"
 global playng_files 

 #Определяем чему равен status
 set status [infotag get evt_status]
 
 #Если статус равен ls_000 (успешное соединение с требуемым номером), изменяем состояние на CALLACTIVE
 if {$status == "ls_000"} {
 fsm setstate CALLACTIVE 
 
 #Если статус равен ls_002 (никто не ответил на звонок), запускаем процедуру запроса номера
 } elseif {$status == "ls_002"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noanswer)
 #Если статус - неверный номер, запускаем процедуру запроса номера
 } elseif {$status == "ls_004" || $status == "ls_005" || $status == "ls_006"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(noexist)
 #Если статус равен ls_007 (абонент занят), запускаем процедуру запроса номера
 } elseif {$status == "ls_007"} {
  fsm setstate TRYAGAIN
  media play leg_incoming $playng_files(busy)
 }
}

#Процедура прерывания звонка
proc AbortCall { } {
 puts "\n\n IVR - proc AbortCall start \n\n"
 call close
}

#Исполнение скрипта
init

#Это набор состояний и возникающих при данных состояниях событий
#По сути именно это и описывает работу скрипта
 
 #Если в любом состоянии возникнет событие отключения ev_disconnected, вызвать AbortCall
 set ivr_fsm(any_state,ev_disconnected)      "AbortCall,   same_state"
 
 #Если в состоянии CALLCOMES возникнет событие ev_setup_indication (входящий вызов) 
        #запускается Play_Welcome, и состояние меняется на same_state (т.е. остается прежним)
 set ivr_fsm(CALLCOMES,ev_setup_indication)    "Play_Welcome,   same_state"
 
 #Если в состоянии CALLCOMES возникнет событие ev_collectdigits_done (закончен ввод цифр) 
        #запускается CheckDestanation, и состояние остается прежним
 set ivr_fsm(CALLCOMES,ev_collectdigits_done)  "CheckDestanation,  same_state"
 
 #Если в состоянии CALLCOMES возникнет событие ev_named_timer (закончился таймер ожидания ввода цифр) 
        #запускается GoToReception, и состояние остается прежним
 set ivr_fsm(CALLCOMES,ev_named_timer)   "GoToReception,  same_state"
 
 #Если в состоянии TORECEPTION возникнет событие ev_media_done (закончился проигрыш файла) 
        #запускается GoToReception, и состояние остается прежним
 set ivr_fsm(TORECEPTION,ev_media_done)           "GoToReception,  same_state"
 
 #Данные настройки описывают поведение скрипта при ошибке в номере
 set ivr_fsm(TRYAGAIN,ev_media_done)           "Play_TakeNumber,  TRYING"
 set ivr_fsm(TRYING,ev_collectdigits_done)  "CheckDestanation,  same_state"
 set ivr_fsm(TRYING,ev_named_timer)   "GoToReception,  same_state"
 
 #Если в состоянии CALLCONNECTED возникнет событие ev_setup_done (установлено/неустановлено соединение с требуемым номером)
        #запускается CallIsConnect, и состояние остается прежним
 set ivr_fsm(CALLCONNECTED,ev_setup_done)   "CallIsConnect,  same_state"
 
 #Эти события отрабатывают отключение линии
 set ivr_fsm(CALLACTIVE,ev_disconnected)     "AbortCall,   CALLDISCONNECTED"
 set ivr_fsm(CALLDISCONNECTED,ev_disconnected)   "AbortCall,   same_state"
 set ivr_fsm(CALLDISCONNECTED,ev_media_done)    "AbortCall,   same_state"
 set ivr_fsm(CALLDISCONNECTED,ev_disconnect_done)  "AbortCall,   same_state"

fsm define ivr_fsm CALLCOMES

среда, 5 августа 2015 г.

Установка и настройка FTP сервера vsftpd


Сегодня очень популярный (и не зря) ftp сервер vsftpd будет установлен и настроен на Debian 8.
1) Установка
apt-get install vsftpd

2) Предварительные настройки
Для начала создадим группу пользователей для FTP доступа ftpgroup и добавим туда пользователя ftpuser:
addgroup ftpgroup
useradd -d /home_folder -g ftpgroup ftpuser
passwd ftpuser
В качестве /home_folder необходимо указать папку, в которую будет попадать ftpuser при подключении к FTP
Обозначим права на папку /home_folder:
chown ftpuser:ftpgroup /home_folder
chmod 664 /home_folder

3) Настройки
Все настройки FTP сервера хранятся в файле /etc/vsftpd.conf.
Сделаем бекап и выполним настройки сервера:
mv /etc/vsftpd.conf /etc/vsftpd.conf.BACK
echo "
## Файл настроек FTP сервера vsftpd
## После изменения, нужно перезапустить сервер
## /etc/init.d/vsftpd restart
##Включить FTP сервер
listen=YES
##Активировать доступ для локальных пользователей
local_enable=YES
##Разрешаем запись
write_enable=YES
##chmod на закачиваемые файлы
#022 = 755
#000 = 777
#133 = 644
#111 = 666
local_umask=022
##Файл с пользователями, доступ которых на FTP  разрешен/запрещен
userlist_file=/etc/vsftpd.userlist
userlist_enable=YES
#NO=только тем, кто перечислен в файле пользователям будет разрешен доступ
#YES=всем локальным пользователям будет разрешен доступ на FTP, кроме тех, что прописаны в файле
userlist_deny=NO
##Пользователи могут получить доступ только к своим каталогам
chroot_local_user=YES
chroot_list_enable=NO
allow_writeable_chroot=YES
##Отключаем анонимных пользователей
anonymous_enable=NO
#anon_upload_enable=NO
#anon_mkdir_write_enable=NO
#anon_other_write_enable=NO
##Настройки соединения
max_clients=100
max_per_ip=10
hide_ids=YES
idle_session_timeout=600
data_connection_timeout=3000
dirmessage_enable=YES
ftpd_banner=Welcome!
##Логи
xferlog_enable=YES
xferlog_file=/var/log/vsftpd.log
" >> /etc/vsftpd.conf
Создадим файл /etc/vsftpd.userlist, в котором будут перечислены все пользователи, доступ которым на FTP сервер будет разрешен (в нашем случае это ftpuser):
echo "ftpuser" >> /etc/vsftpd.userlist
Не забываем перезагрузить FTP сервер:
/etc/init.d/vsftpd restart

3) Проверка
Для того чтобы проверить правильность настройки можно попробовать подключится к FTP серверу. Используем команду ftp localhost:
# ftp localhost
ftp: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
220 Welcome!

Вводим логин:
Name (localhost:ftp): ftpuser
331 Please specify the password.

И пароль:
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

вторник, 4 августа 2015 г.

Инкрементное резервное копирование данных с помощью rdiff-backup


Перепробовав решения под Windows, как бесплатные, так и платный Acronis Backup and Recovery, купленный на эти цели за "дорого для такой глючной программы", и уже было отчаявшись в поисках - решил попробовать бесплатные решения на Linux.
Из-за своей простоты, качества и скорости - выбор был сделан в пользу rdiff-backup.
Итак посмотрим, как сделать полноценный сервер резервирования данных на основе Rdiff (как обычно на Debian 8.0)

1)Установка
Т.к. Rdiff - идет в стандартных репозиториях, то тут ничего сложного нет:
apt-get -q -y install rdiff-backup
Все, программа готова к работе, вот так вот просто.

2) Синтаксис
Как и со всеми Linux-way дистрибутивами, работа с rdiff-backup очень и очень простая.
Для того чтобы сделать копию нужного каталога достаточно набрать:
rdiff-backup /data_path /destination_path
После чего в папке destination_path будет создана инкрементная копия data_path. Если после данного копирования вы измените состав или содержимое файлов в data_path и заново выполните копирование - скопируются только измененные файлы.
Чтобы посмотреть все доступные инкрементные данные поданной папке достаточно набрать:
rdiff-backup -l /destination_path
Вывод данной команды будет примерно вот такой:
Found 5 increments:
increments.2014-07-14T21:51:05+06:00.dir   Sat Jul 14 21:51:05 2014
increments.2014-07-15T21:51:03+06:00.dir   Sun Jul 15 21:51:03 2014
increments.2014-07-15T22:02:30+06:00.dir   Sun Jul 15 22:02:30 2014
increments.2014-07-16T21:51:08+06:00.dir   Mon Jul 16 21:51:08 2014
increments.2014-07-19T21:51:08+06:00.dir   Thu Jul 19 21:51:08 2014

Current mirror: Sat Aug 25 01:25:01 2014
Здесь мы видим 5 слепков данной дериктории, датированные 14-19 июля. Это значит, что в данные дни были выполнены какие-то изменения в data_path.

3) Восстановление данных
Чтобы восстановить данные за какой-то определенный день необходимо набрать:
rdiff-backup -r 2014-07-19T21:51:08+06:00 /destination_path/some_file_or_dir /local_dir
Разберем команду:
2014-07-19T21:51:08+06:00 - я взял из предыдущего вывода (последний инкремент). Программа поддерживает не только такое представление, чтобы восстановить данные 5-ти дневной давности можно набрать 5D, 2-х недельной 2W и так далее.
/destination_path/some_file_or_dir - можно указать конкретный файл из сохраненных данных, либо дирикторию полностью.
/local_dir - здесь нужно указать в какую директорию выполнить восстановление данных.

4) Диагностика
По умолчанию, после запуска, rdiff-backup ведет себя более чем скромно, и сообщает только если сталкивается с какими-либо ошибками при копировании.
Для повышения уровня дебага есть встроенная опция '-v' или '--verbosity', уровень можно регулировать от 0 до 9.
rdiff-backup -v 4 /tmp /backup
Using rdiff-backup version 1.2.8
Unable to import win32security module. Windows ACLs
not supported by filesystem at /tmp
escape_dos_devices not required by filesystem at /tmp
-----------------------------------------------------------------
Detected abilities for source (read only) file system:
  Access control lists                         On
  Extended attributes                          On
  Windows access control lists                 Off
  Case sensitivity                             On
  Escape DOS devices                           Off
  Escape trailing spaces                       Off
  Mac OS X style resource forks                Off
  Mac OS X Finder information                  Off
-----------------------------------------------------------------
Unable to import win32security module. Windows ACLs
not supported by filesystem at backup/rdiff-backup-data/rdiff-backup.tmp.0
escape_dos_devices not required by filesystem at backup/rdiff-backup-data/rdiff-backup.tmp.0
-----------------------------------------------------------------
Detected abilities for destination (read/write) file system:
  Ownership changing                           On
  Hard linking                                 On
  fsync() directories                          On
  Directory inc permissions                    On
  High-bit permissions                         On
  Symlink permissions                          Off
  Extended filenames                           On
  Windows reserved filenames                   Off
  Access control lists                         On
  Extended attributes                          On
  Windows access control lists                 Off
  Case sensitivity                             On
  Escape DOS devices                           Off
  Escape trailing spaces                       Off
  Mac OS X style resource forks                Off
  Mac OS X Finder information                  Off
-----------------------------------------------------------------
Backup: must_escape_dos_devices = 0
Starting mirror /tmp to /backup
При повышении уровня - количество сообщений в консоль увеличивается
5) Удаление старых инкрементных копий
Для того, чтобы не хранить старые версии файлов, есть замечательный метод удаления инкрементных данных:
rdiff-backup --remove-older-than 40W /destination_path
Опция --remove-older-than 40W выполнит удаление всех слепков, старше чем 40 недель.

6) Автоматизация процесса резервирования
Так, ну а теперь напишем небольшой скрипт, который будет выполнять резервное копирование данных и отправлять отчет о проделанной работе. Создадим файл backuper.bin
touch /etc/backuper.bin
И запишем в него следующий код:
#!/bin/sh
###!!! ВАШИ Настройки ЗДЕСЬ !!!###
#Определим сегодняшнюю и вчерашнюю даты
TODAY="$(date +%X.%Y.%m.%d)"
YESTERDAY="$(date -d 'yesterday' +%Y.%m.%d)"
#Ошибки (по умолчанию нет)
ERRORS="no errors"
#Получатели письма счастья )
MAILTO='user1@myil.ru,user2@myil.ru'
MAILFROM='backuper@myil.ru'
SUBJECTED='INCREMENT BACKUP report ('$ERRORS')'
#Что копировать
SAVE_DATA=/DATA_PATH
#Куда копировать
BACKUP_DATA=/BACKUP_PATH
#лог-файл процесса инкрементного копирования
LOGFILE=/tmp/backuplog
#Расположение rdiff-backup
RDIFF='/usr/bin/rdiff-backup'
#Строка запуска инкрементного копирования (доп опции)
EXT_STRING='--force --no-eas --exclude-symbolic-links --exclude-sockets --exclude-special-files --exclude-fifos --exclude-device-files --no-hard-links --print-statistics'
EXT_STRING2='--force --no-hard-links --remove-older-than'
#Уровень логов RDIFF
LOG_LEVEL='-v 7'
#Удалять файлы старше 40 недель
EXPIRE='40W'

#Чистим лог для начала
cat /dev/null > $LOGFILE

##Записываем в начало лога данные, которые будут использованы при отправке письма
echo "To:$MAILTO
From:$MAILFROM
Subject: $SUBJECTED
Content-Type: text/plain; charset=UTF-8; format=flowed
Hi all :D! " >> $LOGFILE

#Дописываем в лог разделитель
echo "
------------------------------------------------------------------
$TODAY
INCREMENT BACKUP for yesterday files($YESTERDAY):" >> $LOGFILE
#Записываем время начала операции копирования
STARTTIME="$(date +%H:%M:%S)"
#Запускаем процесс инкрементного копирования:
$RDIFF $EXT_STRING $LOG_LEVEL $SAVE_DATA $BACKUP_DATA >> $LOGFILE 2>&1

##Удаляем файлы старше чем EXPIRE
#Дописываем в лог разделитель
echo "
------------------------------------------------------------------
Removing old files (EXPIRE=$EXPIRE):" >> $LOGFILE
#Запускаем удаление
$RDIFF $EXT_STRING2 $EXPIRE $BACKUP_DATA >> $LOGFILE 2>&1

#Записываем время начала и конца операции
ENDTIME="$(date +%H:%M:%S)"
echo "
------------------------------------------------------------------
STARTTIME: $STARTTIME
ENDTIME: $ENDTIME" >> $LOGFILE

##Проверка на ошибки
#Если будет найдена ошибка в логе, переменная ERRORS будет изменена
#и в теме письма будет крупно написано ERRORS!!
if [ `cat $LOGFILE | grep 'Error' | grep -v 'Errors 0' | grep -cv grep` != "0" ]; then
    sed -i -e 's/$ERRORS/ERRORS!!/' $LOGFILE
fi

#Отправка почты с помошью ssmtp
/usr/sbin/ssmtp -t < $LOGFILE

exit 0
Думаю не нужно объяснять, что необходимо самостоятельно заполнить поле "ВАШИ Настройки ЗДЕСЬ"
Как вы наверно заметили, в данном примере отправка почты происходит с помощью ssmtp, давайте его установим:
apt-get install ssmtp
И настроим:
mv /etc/ssmtp/ssmtp.conf /etc/ssmtp/ssmtp.conf.BACK
echo "
#Тема для тестовых сообщений
Subject: Test message

#Адрес почтового сервера
mailhub=mail.myil.ru

#От кого будет посылаться сообщение
hostname=backuper@myil.ru

#Использовать шифрование STARTTLS
UseSTARTTLS=no

#метод авторизации
AuthMethod=LOGIN

#Логин
AuthUser=backuper@myil.ru

#Пароль
AuthPass=backuper_psw

#Разрешить изменить адрес отправителя (hostname)
FromLineOverride=YES
 
" >> /etc/ssmtp/ssmtp.conf
Вам самостоятельно нужно заполнить следующие поля:
mailhub - почтовый сервер на который будет выполнена отправка сообщения.
hostname - от кого будет сообщение.
UseSTARTTLS - использовать или нет шифрование STARTTLS.
AuthUser - имя пользователя на почтовом сервере.
AuthPass - пароль на почтовом сервере.

Осталось подкорректировать права запуска файла:
chmod +x /etc/backuper.bin
И добавить задачу в Cron. Пишем # crontab -e, и добавляем в конец строку:
5 0 * * *  /etc/backuper.bin
Таким образом, наш файл /etc/backuper.bin - будет запускаться каждый день в 00 часов 05 минут.

понедельник, 6 июля 2015 г.

Автоматическое выключение ESXi при пропадание питания на UPC APC


Сегодня речь пойдет о автоматическом выключении гипервизора ESXi при пропадании питания на ИБП.
Для работы данной связки нам необходим ИБП с сетевой картой. В данном конкретном случае это ИБП фирмы APC и для этих целей у APC есть продукт, который называется APC PowerChute Network Shutdown.
Со стороны ESXi в данной схеме будет участвовать виртуальная машина vSphere Management Assistant, или просто vMA.


Итак, первое, что нам нужно это установить vMA на ESXi.
1) Скачиваем и распаковываем vMA
2) Заходим в vSphere Client, жмем File -> Deploy OVF Template и выбираем скачанный .OVF файл
Принимаем лицензионное соглашение, и устанавливаем виртуальную машину.
3) Запускаем машину.
После запуска, первое что нам откроется это меню настройки сетевого интерфейса
Проходимся по всем настройкам и настраиваем интерфейсы, после того как закончили, жмем 1 (Exit this program)
4) Следующий шаг - смена стандартного пароля от пользователя vi-admin
Вводим в Old Password пароль vmware и 2 раза вводим придуманный вами пароль.
Здесь со стороны vmware есть довольно жесткие требования к сложности пароля.
Дело в том, что vMA будет "хранить" пароли доступа к гипервизору ESXi или к виртуальным машинам (зависит от настроек).
Соответственно, в ваших же интересах, чтобы пароль на vMA не был "простым".
5) После завершения установки можно зайти в консоль управления vMA по адресу https://vMA_IP:5480/ с введенными ранее логином и паролем.
Здесь можно изменить сетевые настройки, настройки часового пояса и автообновления.


Следующий этап - это настройка vMA
1) Настройки прокси:
Если у вас используется прокси сервер, то нужно отредактировать файл /etc/sysconfig/proxy следующим образом:
PROXY_ENABLED="yes"
HTTP_PROXY="http://proxy_login:proxy_password@proxy_IP:proxy_port"
HTTPS_PROXY="http://proxy_login:proxy_password@proxy_IP:proxy_port"
FTP_PROXY="http://proxy_login:proxy_password@proxy_IP:proxy_port"
GOPHER_PROXY="http://proxy_login:proxy_password@proxy_IP:proxy_port"
Подставив свои значения proxy_login, proxy_password и proxy_IP:proxy_port
2) Установка mc
sudo zypper install mc
3) Установка nano
sudo zypper install nano
4) Если вы установили данные программы, то следует перезагрузить vMA
sudo reboot


Настройка APC PowerChute Network Shutdown
1) Качаем APC PowerChute Network Shutdown for VMware ESXi
На момент написания статьи это была версия PowerChute Network Shutdown v4.0 for VMware ESXi
2) Закачиваем PowerChute «парашют» на сервер и распаковываем:
sudo wget ftp://'restrict:Kop$74!@ftp.apc.com/restricted/software/pcns/400/esxi/pcns400ESXi.tar.gz'
sudo tar -xvf pcns*.tar.gz
3) Установим «парашют»:
cd ESXi
sudo ./install_en.sh
Дальше нужно будет ответить на несколько вопросов:
Please enter the installation directory or press enter to install to the default directory (/opt/APC/PowerChute): ENTER
Are you sure you want to install PCNS to /opt/APC/PowerChute [Yes|No]? Yes
Please enter java directory if you want to use your system java (example:/usr/local/bin/jre/jre1.8.0_31) or press enter to install the bundled Java: ENTER
4) Заходим на web интерфейс PowerChute https://vMA_IP:6547
Нам предлагают выполнить несколько настроек:
1) выбираем IPv4 или IPv6 (в зависимости от версии используемого у вас протокола)
2) Standalone VMware Host.
3) Host Protocol - https
4) Host Address - ip адрес гипервизора ESXi
5) Здесь нужно задать логин и пароль администратора гипервизора ESXi. Это нужно для того, чтобы программа установки прописала необходимые параметры в самом гипервизоре:
   Host Username - root
   Host Password - root_password
6) Выбираем сколько UPC используется для питания сервера ESXi (в моем случае 1)
   Please select your UPS configuration - Single (Если у ваш сервер запитан от 1 UPS)
7) На вкладке PowerChute Setup: UPS Details
   Вводим IP Address ИБП
8) Do not turn off the UPS - если не хотите, чтобы PowerChute выключал сам ИБП после того как отработает сценарий
После чего можно подключаться на web-интерфейс PowerChute https://vMA_IP:6547


Теперь нам нужен скрипт отключения ESXi гипервизора
1) Будем пользоваться shutdownHostViaSOAPAPICall.pl, скачать можно от сюда
Переходим в режим root пользователя:
sudo su
Переходим в домашнюю директорию, качаем скрипт и переименовываем его для удобства:
cd /home/vi-admin
wget https://communities.vmware.com/servlet/JiveServlet/download/11623-3-32693/shutdownHostViaSOAPAPICall.pl
mv shutdownHostViaSOAPAPICall.pl shutdown.pl
Теперь нам нужно изменить логин и пароль доступа к гипервизору ESXi в файле shutdown.pl:
my $host_username = 'root';
my $host_password = 'root_ESXi_pswrd';
2) Дополнительно нам нужен файл, где будет написан ip нашего гипервизора, и файл лога - куда мы будем записывать события, связанные с запуском данного скрипта. Создаем фай лога:
touch /var/log/apc_esxi_shutdown.log
Создаем файл с ip гипервизора:
touch /home/vi-admin/hosts
Добавляем ip нашего гипервизора в файл /home/vi-admin/hosts
echo "IP_ESXi" >> /home/vi-admin/hosts
Заменив IP_ESXi на ip вашего ESXi
3) Теперь нам нужно поправить скачанный ранее shutdown.pl, т.к. в оригинале он отказывается работать и выводит ошибку:
 <html>
 <head><title>An Error Occurred</title></head>
 <body>
 <h1>An Error Occurred</h1>
 <p>500 Can't connect to IP_ESXi:443 (certificate verify failed)</p>
 </body>
 </html>
 Failed to issue shutdown command to IP_ESXi
Для того чтобы этого не было, добавим 1 строчку в shutdown.pl
sudo vi /home/vi-admin/shutdown.pl
перед строкой
use strict;
нужно добавить строку
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
и сохранить файл (esc -> :wq). Таким образом начало файла должно выглядеть вот так:
4) Ну и последнее, что нам нужно, это дополнительный файл, который будет запускать PowerChute при пропадании питания на UPS.
sudo vi /home/vi-admin/shutdown_esxi.pl
со следующим содержанием:
#!/bin/sh
echo Shuting down all virtual mashines and ESXi Server
/home/vi-admin/shutdown.pl /home/vi-admin/hosts >> /var/log/apc_esxi_shutdown.log
5) Не забываем про права запуска и владельца файла:
sudo chmod 755 /home/vi-admin/shutdown_esxi.pl /home/vi-admin/shutdown.pl /home/vi-admin/hosts
sudo chmod +x /home/vi-admin/shutdown_esxi.pl /home/vi-admin/shutdown.pl
sudo chown vi-admin:root /home/vi-admin/shutdown_esxi.pl /home/vi-admin/shutdown.pl /home/vi-admin/hosts /var/log/apc_esxi_shutdown.log


Так, теперь заходим на web интерфейс PowerChute https://vMA_IP:6547 и подключаем скрипт:
Configure Events - UPS On Battery - Command file
Ставим галку напротив Enable Command File
Выставляем Delay 20 seconds (если в течении этого времени питание вернется - скрипт отработан не будет)
Full path to command file - путь до нашего скрипта: /home/vi-admin/shutdown_esxi.pl
Применяем настройки


Ну и последнее, что нам нужно это в настройках гипервизора выставить очередность запуска и выключения виртуальных машин:
В Данном порядке они будут включаться, в обратном выключаться.
В поле Shutdown action лучше выбрать Guest shutdown, что аналогично "Пуск - Завершение работы" у windows, в отличии от Power Off, который аналогичен нажатию кнопки выключения питания на системном блоке:

Вот собственно и все, теперь если питание пропадет на 20 секунд, гипервизор получит сигнал выключения и произойдет поочередное выключение машин, а после и самого гипервизора.

четверг, 2 июля 2015 г.

Настройка Nagios 3


Сегодня добавим хост на мониторинг в Nagios3 (сервер Debian 8).
Итак, для начала немного теории.
1) Все настройки мониторинга хранятся в папке /etc/nagios3/conf.d
2) Nagios "воспринимает" только файлы с расширением .cfg
3) Все хосты можно разбить по разным файлам, а можно объеденить хосты и сервисы (методы проверки) в разные файлы

Начнем с самого начала.
Установим Nagios:
apt-get install nagios3
Cделаем backup существующих настроек:
mv /etc/nagios3/conf.d /etc/nagios3/conf.d.BACK
mkdir /etc/nagios3/conf.d
1) Первое, что нам нужно - это определить временные интервалы.
Создадим файл настроек временных интервалов /etc/nagios3/conf.d/01-timeperiods.cfg:
echo "
# Период 24х7 (Круглосуточная проверка) - которым в основном и будем пользоваться
define timeperiod{
    timeperiod_name 24x7        ; Имя временного периода (24x7)
    alias           24x7        ; Описание периода
    monday          00:00-24:00 ; Временной интервал в понедельник
    tuesday         00:00-24:00 ; Временной интервал во вторник
    wednesday       00:00-24:00 ; Временной интервал в среду
    thursday        00:00-24:00 ; Временной интервал в четверг
    friday          00:00-24:00 ; Временной интервал в пятницу
    saturday        00:00-24:00 ; Временной интервал в субботу
    sunday          00:00-24:00 ; Временной интервал в воскресение
    }
# Рабочие часы
define timeperiod{
    timeperiod_name workhours
    alias           workhours
    monday          09:00-17:00
    tuesday         09:00-17:00
    wednesday       09:00-17:00
    thursday        09:00-17:00
    friday          09:00-17:00
    }
# Не рабочие часы
define timeperiod{
    timeperiod_name nonworkhours
    alias           nonworkhours
    monday          00:00-09:00,17:00-24:00
    tuesday         00:00-09:00,17:00-24:00
    wednesday       00:00-09:00,17:00-24:00
    thursday        00:00-09:00,17:00-24:00
    friday          00:00-09:00,17:00-24:00
    saturday        00:00-24:00
    sunday          00:00-24:00
    }
# Для того "чтобы было" период "никогда"
# Здесь не заданны временные интервалы, это значит, что проверки не будет
define timeperiod{
    timeperiod_name never 
    alias           Never
    }
" >> /etc/nagios3/conf.d/01-timeperiods.cfg
2) Нужно задать команду оповещения по почте, и настроить эту самую почту.
Установим sendemail, с помощью которого будем производить почтовую рассылку:
apt-get install sendemail
Добавим следующий код в файл /etc/nagios3/commands.cfg:
#Оповещение по email о состоянии хоста
define command{
    command_name    notify-host-by-email
    command_line /usr/bin/printf "%b" "*****MONITORING SYSTEM*****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/sendemail -s youre.mail.server -xu monitoring_username -xp monitoring_password -t $CONTACTEMAIL$ -f frommail@mail.server -o tls=no -l /var/log/sendemail.log -u "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" -m "*****MONITORING SYSTEM*****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n"
    }
#Оповещение по email о состоянии сервиса
define command{
    command_name    notify-service-by-email
    command_line /usr/bin/printf "%b" "*****MONITORING SYSTEM*****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$" | /usr/bin/sendemail -s youre.mail.server -xu monitoring_username -xp monitoring_password -t $CONTACTEMAIL$ -f frommail@mail.server -o tls=no -l /var/log/sendemail.log -u "** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **" -m "*****MONITORING SYSTEM*****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$"
    }
#Команда проверки по SNMP
define command{
    command_name    check_snmp01
    command_line    /usr/lib/nagios/plugins/check_snmp -H $HOSTADDRESS$ $ARG1$
    }
Итак, если не вдаваться в подробности, видно, что мы только что добавили 3 команды:
notify-host-by-email - оповещение по почте о состоянии хоста
notify-service-by-email - оповещение по почте о состоянии сервиса
Ключевые моменты данных команд, которые нужно настроить исходя из сервера, через который будет происходить рассылка:
-s youre.mail.server    - ip или DNS имя почтового сервера
-xu monitoring_username - имя пользователя
-xp monitoring_password - пароль
-f frommail@mail.server - почтовый ящик мониторинга
-o tls=no               - используется или нет tls 
И check_snmp01, с помощью которой будем проверять состояние хостов по SNMP
3) Задаем контактные лица, которым будут отправляться оповещения о пропадании сервисов /etc/nagios3/conf.d/02-contacts.cfg:
echo "
#service_notification_options :
#w - оповещать, если состояние сервиса WARNING
#u - оповещать, если состояние сервиса UNKNOWN
#c - оповещать, если состояние сервиса CRITICAL
#r - оповещать, если состояние сервиса RECOVERY (сервис восстановился после сбоя)
#f - оповещать, если состояние сервиса FLAPPING - происходят кратковременные сбои (вкл/выкл) сервиса
#n - не оповещать (контакт не получит оповещений о состоянии сервиса)

#host_notification_options :
#d - оповещать, если состояние хоста DOWN
#u - оповещать, если состояние хоста UNREACHABLE
#r - оповещать, если состояние хоста RECOVERY (хост восстановился после сбоя)
#f - оповещать, если состояние хоста FLAPPING - происходят кратковременные сбои (вкл/выкл)
#s - оповещать, если хост выключился(включился) по расписанию
#n - не оповещать (контакт не получит оповещений о состоянии хоста)

#Контакт №1
define contact{
    contact_name                  main_admin              ; Имя контакта
    alias                         Glavnii admin           ; Описание контакта
    service_notification_period   24x7                    ; Оповещать о состоянии сервиса во временной интервал
    host_notification_period      24x7                    ; Оповещать о состоянии хоста во временной интервал
    service_notification_options  w,u,c,r                 ; WARNING,UNKNOWN,CRITICAL,RECOVERY
    host_notification_options     d,r                     ; DOWN,RECOVERY
    service_notifications_enabled 1                       ; Оповещать о изменениях сервисов
    host_notifications_enabled    1                       ; Оповещать о изменениях хоста
    service_notification_commands notify-service-by-email ; Способ оповещения о состоянии сервиса(задан в commands.cfg)
    host_notification_commands    notify-host-by-email    ; Способ оповещения о состоянии хоста(задан в commands.cfg)
    email                         main_admin@yordomain.com; Почтовый ящик контактного лица
    }
#Контакт №2
define contact{
define contact{
    contact_name                  second_admin              ; Имя контакта
    alias                         Ne Glavnii admin          ; Описание контакта
    service_notification_period   24x7                      ; Оповещать о состоянии сервиса во временной интервал
    host_notification_period      24x7                      ; Оповещать о состоянии хоста во временной интервал
    service_notification_options  w,u,c,r                   ; WARNING,UNKNOWN,CRITICAL,RECOVERY
    host_notification_options     d,r                       ; DOWN,RECOVERY
    service_notifications_enabled 1                         ; Оповещать о изменениях сервисов
    host_notifications_enabled    1                         ; Оповещать о изменениях хоста
    service_notification_commands notify-service-by-email   ; Способ оповещения о состоянии сервиса(задан в commands.cfg)
    host_notification_commands    notify-host-by-email      ; Способ оповещения о состоянии хоста(задан в commands.cfg)
    email                         second_admin@yordomain.com; Почтовый ящик контактного лица
    }
#Группа контактов
define contactgroup{
    contactgroup_name       admins                    ; Имя группы
    alias                   Nagios Administrators     ; Описание
    members                 main_admin,second_admin   ; Состав группы
    }
" >> /etc/nagios3/conf.d/02-contacts.cfg
4) Зададим шаблон для хостов. Это необязательно, но в дальнейшем значительно упростит конфигурацию Nagios-а
echo "
#notification_options :
#d - оповещать, если состояние хоста DOWN
#u - оповещать, если состояние хоста UNREACHABLE
#r - оповещать, если состояние хоста RECOVERY (хост восстановился после сбоя)
#f - оповещать, если состояние хоста FLAPPING - происходят кратковременные сбои (вкл/выкл)
#s - оповещать, если хост выключился(включился) по расписанию
#n - не оповещать (контакт не получит оповещений о состоянии хоста)

define host{
    name                         host-shablon      ; Имя шаблона
    notifications_enabled        1                 ; Включить оповещения
    event_handler_enabled        1                 ; Включить обработчик событий (контролировать состояние хоста)
    flap_detection_enabled       1                 ; Реагировать на ФЛАППИНГ или нет (кратковременный вкл/выкл хоста)
    failure_prediction_enabled   1                 ; Прогнозирование сбоев. В дальнейшем Nagios от этого 
                                                   ;  параметра отказался совсем, т.к. он часто сбоит
    process_perf_data            1                 ; Обработка данных о производительности
    retain_status_information    1                 ; Обработка состояния хоста(включен, выключен, перезагружен)
                                                   ;  Если включен retain_nonstatus_information - запоминает состояние
                                                   ;  хоста и восстанавливает значение в случае перезагрузки нагиоса
    retain_nonstatus_information 1                 ; Запоминать состояние хоста в случае перезагрузки нагиоса
    check_command                check-host-alive  ; Команда проверки. check-host-alive - проверка хоста на доступность
    max_check_attempts           3                 ; Количество проверок статуса отличного от ОК, 
                                                   ;  перед тем как будет создано оповещение
    notification_interval        60                ; Если статус отличен от ОК, выдавать оповещения каждый час 
                                                   ;  (0-чтобы оповещать только в случае восстановления)
    notification_period          24x7              ; Период оповещения 24х7
    notification_options         d,u,r             ; DOWN, UNREACHABLE, RECOVERY
    contact_groups               admins            ; Кому рассылать оповещения
    register                     0                 ; Не регистрировать в Нагиосе, т.к. это только шаблон
    }
" >> /etc/nagios3/conf.d/03-host_template.cfg
5) Зададим шаблон для сервисов:
echo "
#notification_options :
#w - оповещать, если состояние сервиса WARNING
#u - оповещать, если состояние сервиса UNKNOWN
#c - оповещать, если состояние сервиса CRITICAL
#r - оповещать, если состояние сервиса RECOVERY (сервис восстановился после сбоя)
#f - оповещать, если состояние сервиса FLAPPING - происходят кратковременные сбои (вкл/выкл) сервиса
#n - не оповещать (контакт не получит оповещений о состоянии сервиса)

define service{
    name                            service_shablon ; Имя шаблона
                                                    ; Есть 2 типа проверок состояния сервиса регулярно и по расписанию
    active_checks_enabled           1               ; Включить акивные (регулярные) проверки
    passive_checks_enabled          1               ; Включить пассивные (по расписанию) проверки
    parallelize_check               1               ; Активные проверки должны быть параллельными  
    obsess_over_service             1               ; Предпочитать эту проверку а не через ocsp_command
    check_freshness                 0               ; Проверка на "свежесть" пассивных проверок
    notifications_enabled           1               ; Включить оповещения
    event_handler_enabled           1               ; Включить обработчик событий
    flap_detection_enabled          1               ; Реагировать на ФЛАППИНГ или нет (кратковременный вкл/выкл сервиса)
    failure_prediction_enabled      1               ; Прогнозирование сбоев. В дальнейшем Nagios от этого 
                                                    ;  параметра отказался совсем, т.к. он часто сбоит
    process_perf_data               1               ; Обработка данных о производительности
    retain_status_information       1               ; Обработка состояния сервиса(включен, выключен, перезагружен)
                                                    ;  Если включен retain_nonstatus_information - запоминает состояние
                                                    ;  сервиса и восстанавливает значение в случае перезагрузки нагиоса
    retain_nonstatus_information    1               ; Запоминать состояние сервиса в случае перезагрузки нагиоса
    notification_interval           60              ; Если статус отличен от ОК, выдавать оповещения каждый час 
                                                    ;  (0-чтобы оповещать только в случае восстановления)
    is_volatile                     0               ; Постоянный(0) или непостоянный(1) сервис
    check_period                    24x7            ; Период проверки 24х7
    normal_check_interval           1               ; Интервал нормальной проверки
    retry_check_interval            1               ; Интервал повторной проверки
    max_check_attempts              1               ; Количество проверок статуса отличного от ОК, 
                                                    ;  перед тем как будет создано оповещение
    notification_period             24x7            ; Период оповещения 24х7
    notification_options            w,u,c,r         ; WARNING UNKNOWN CRITICAL RECOVERY
    contact_groups                  admins          ; Кому рассылать оповещения
    register                        0               ; Не регистрировать в Нагиосе, т.к. это только шаблон
    }
" >> /etc/nagios3/conf.d/03-service_template.cfg
6) Создаем сервис PING, в который будут включены все хосты, и будут они просто пинговаться:
echo "
#Данный сервис пингует все хосты, которые включены в мониторинг
#Сначала сам сервис:
define service {
    hostgroup_name                  PING-CHECK                     ; Название сервиса
    service_description             PING                           ; Описание сервиса
    check_command                   check_ping!200.0,40%!400.0,80% ; Команда проверки
    use                             service_shablon                ; Использовать шаблон service_shablon
}
#А теперь группа хостов, которая будет пинговаться:
#Здесь есть 1 момент, когда hostgroup_name сервиса и группы хостов совпадает
#Таким образов в данной группе хосты будут мониторится по описанному выше сервису
define hostgroup {
    hostgroup_name  PING-CHECK  ; Название группы
    alias           ping all    ; Описание группы
    members         *           ; Состав группы * - значит все хосты
    }
" >> /etc/nagios3/conf.d/04-ping_all_hosts.cfg
7) Ну а теперь зададим наш хост:
echo "
#Здесь задается хост
define host{
    host_name               my_host1                  ; Имя хоста. Лучше без пробелов и по английски
    alias                   my host number 1          ; Описание хоста
    use                     host-shablon              ; Использовать шаблон              
    address                 212.193.163.6             ; IP Адрес хоста (который автоматически будет добавлен в сервис пинг)
    }
#Определим группу хостов. В дальнейшем, когда в мониторинге будет много хостов - это будет помогать
define hostgroup {
    hostgroup_name          my_hosts_group_1          ; Имя группы 
    alias                   Group of hosts 1          ; Описание
    members                 my_host1                  ; Члены группы (по полю host_name). 
                                                      ; Если членов несколько, то через запятую my_host1,my_host2 и т.д.
    }
" >> /etc/nagios3/conf.d/20-my_host_01.cfg
8) Хост у нас уже есть, а теперь запишем принтер.
Будем мониторить состояние картриджей в принтере Kyocera 6026 (Kyocera 6126,2035,2135).
Мониторинг будет осуществляться по SNMP:
echo "
#Сетевой принтер определяется для нагиоса как обычный хост
define host {
 host_name         Printer 6026 Ur Otdel
 alias             Printer in Ur office
 address           10.77.0.11
 use               host-shablon
 }
#Можно включить его в группу с предыдущим хостом
define hostgroup {
    hostgroup_name          my_hosts_group_1          ; Имя группы 
    alias                   Group of hosts 1          ; Описание
    members                 Printer 6026 Ur Otdel     ; Члены группы (по полю host_name). 
    }
#Проверка черного картриджа 
#WARNING 443 страницы
#CRITICAL 100 страниц
define service{
        use                     service_shablon       ; Шаблон сервиса
        host_name               Printer 6026 Ur Otdel ; Имя хоста (по полю host_name). 
        service_description     Black Cartridge       ; Описание сервиса
        check_command           check_snmp01!-C public -o .1.3.6.1.2.1.43.11.1.1.9.1.4 -w 433: -c 100: ; SNMP запрос
        }
#Проверка синего картриджа
#WARNING 443 страницы
#CRITICAL 100 страниц
define service{
        use                     service_shablon
        host_name               Printer 6026 Ur Otdel
        service_description     Blue Cartridge
        check_command           check_snmp01!-C public -o .1.3.6.1.2.1.43.11.1.1.9.1.1 -w 433: -c 100:
        }
#Проверка красного картриджа
#WARNING 443 страницы
#CRITICAL 100 страниц
define service{
        use                     service_shablon
        host_name               Printer 6026 Ur Otdel
        service_description     Red Cartridge
        check_command           check_snmp01!-C public -o .1.3.6.1.2.1.43.11.1.1.9.1.2 -w 433: -c 100:
        }
#Проверка желтого картриджа
#WARNING 443 страницы
#CRITICAL 100 страниц
define service{
        use                     service_shablon
        host_name               Printer 6026 Ur Otdel
        service_description     Yellow Cartridge
        check_command           check_snmp01!-C public -o .1.3.6.1.2.1.43.11.1.1.9.1.3 -w 433: -c 100:
        }
" >> /etc/nagios3/conf.d/30-my_printer_01.cfg


Вот собственно и все.
Осталось перезагрузить Нагиос:
/etc/init.d/nagios3 restart
Теперь у нас должны появится 2 хоста в мониторинге, объединенные в одну группы.
При этом у принтера будет проверка состояния картриджей, и оба будут пинговаться.

среда, 3 июня 2015 г.

Интернет шлюз на базе Squid3 работающего в прозрачном режиме (с ssl-bump) с авторизацией по ip + HAVP + ClamAV + Sams2 + Webmin + NTLM + iptables

Здравствуйте друзья, сегодня я расскажу как сделать прозрачный шлюз на основе кэширующего прокси сервера Squid3.

Суть задачи:
1) Обеспечить доступ в интернет для 150+ компьютеров организации.
2) Сделать это максимально просто для конечных пользователей.
3) Обеспечить максимально безопасный доступ в интернет. Именно поэтому весь трафик будет проверяться на вирусы.
4) Так как ранее применялась NTLM авторизация - выполнить гибридную систему NTLM+ip авторизация, чтобы в дальнейшем всех пользователей перевести на новую систему с ip авторизацией.

Итак, я написал скрипт, с помощью которого развернуть полноценную систему доступа в интернет можно будет за очень короткое время.
Данная конфигурация собиралась под Debian 8 x64.

Сервер имеет 2 сетевые карты
eth0 - подключена к интернету и имеет адрес 10.10.0.2/30
eth1 - подключена к локальной сети и имеет адрес 192.168.0.1/24

Итак, для начала нужно настроить сетевые интерфейсы таким образом, чтобы маршрут по умолчанию (шлюз) был на интерфейсе eth0:
приводим к следующему виду: cat /etc/network/interfaces
allow-hotplug eth0
allow-hotplug eth1
iface eth0 inet static
        address 10.10.0.2
        netmask 255.255.255.252
        network 10.10.0.0
        broadcast 10.10.0.3
        gateway 10.10.0.1
      
iface eth1 inet static
        address 192.168.0.1
        netmask 255.255.255.0
        network 192.168.0.0
        broadcast 192.168.0.254
        dns-nameservers 8.8.8.8
        dns-search domain.ru
Далее все команды будем выполнять из под рута. Переходим в режим суперпользователя с помощью команды su и вводим пароль рута:
$ su
Password:
Теперь несколько констант, которые будут упрощать дальнейший код. Здесь необходимо указать путь до вашего FPT сервера, если вы хотите чтобы скрипт автоматически скачивал все нужные в процессе файлы, можете пропустить этот пункт и создавать необходимые файлы вручную:
FTP_PATH='ftp://ftp_login:ftp_passwd@youre.ftp.server/Path_to_configs'
ADMIN_LOGIN_FOR_DC_JOIN='administrator'
ADMIN_DC_PSW='administrator_psw'
IP_ADDRESS_OF_PROXY='ip_of_youre_proxy'
IP_OF_DC='ip_of_domain_controller'
DOMAIN_SM='domain.ru'
NTP_SERVER='ntp_server_domain_name_or_ip'
DOMAIN='DOMAIN'
DOMAIN_RU='DOMAIN.RU'
LOC_HOST='newproxy'
LOC_HOST_FULL='newproxy.domain.ru'
SAMSDB_PASS='sams_pass'
Сами файлы конфигурации можно скачать ЗДЕСЬ
Добавляем маршруты. Предпологается, что в локальной сети несколько подсетей, а именно 192.168.10.0/24 и 192.168.20.0/24. Нужно чтобы они добавлялись каждый раз при загрузке Debian:
echo '#! /bin/sh
route add -net 192.168.10.0 netmask 255.255.255.0 gw 192.168.0.1
route add -net 192.168.20.0 netmask 255.255.255.0 gw 192.168.0.1
exit 0' > /etc/network/if-up.d/routes_add

echo '#! /bin/sh
route del -net 192.168.10.0 netmask 255.255.255.0 gw 192.168.0.1
route del -net 192.168.20.0 netmask 255.255.255.0 gw 192.168.0.1
exit 0' > /etc/network/if-down.d/routes_delete

chmod +x /etc/network/if-up.d/routes_add
chmod +x /etc/network/if-down.d/routes_delete
/etc/init.d/networking restart
Обновляемся:
apt-get -q -y update
apt-get -q -y upgrade
apt-get -q -y dist-upgrade
Устанавливаем все необходимое:
apt-get -q -y install mc ntp squid3 winbind samba krb5-user havp clamav ssl-cert libpcre3 libpcre3-dev apache2 apache2-doc  
apt-get -q -y install mysql-server mysql-client libmysqlclient-dev apache2 apache2-mpm-prefork apache2-doc php5 libapache2-mod-php5 
apt-get -q -y install php5-cli php5-common php5-mysql php5-gd php5-ldap php-fpdf mysql-server mysql-client libmysqlclient15off 
apt-get -q -y install libpcre3 libpcre3-dev make php5 php5-cli php5-common php5-dev php5-mcrypt php5-imagick php5-mysql 
apt-get -q -y install php5-gd php5-ldap php-fpdf libapache2-mod-php5 apache2-utils
apt-get -q -y install make g++ libtool build-essential autoconf automake ssl-cert apache2 apache2-doc apache2-utils 
apt-get -q -y install mysql-server mysql-client libmysqlclient-dev libpcre3 libpcre3-dev php5 php5-cli php5-common 
apt-get -q -y install php5-dev php5-mcrypt php5-imagick php5-mysql php5-gd php5-ldap php-fpdf libapache2-mod-php5 libldap2-dev
Устанавливаем Webmin, с помощью которого будем контролировать iptables (и не только):
echo '
deb http://download.webmin.com/download/repository sarge contrib
deb http://webmin.mirror.somersettechsolutions.co.uk/repository sarge contrib
' >> /etc/apt/sources.list
mkdir ~/downloads
cd ~/downloads
wget http://www.webmin.com/jcameron-key.asc
apt-key add jcameron-key.asc
apt-get update
apt-get install perl libnet-ssleay-perl openssl libauthen-pam-perl libpam-runtime libio-pty-perl apt-show-versions python
apt-get install webmin
Предварительные настройки hosts:
mv /etc/hosts /etc/hosts.BACK
echo "
127.0.0.1       localhost
$IP_ADDRESS_OF_PROXY      $LOC_HOST_FULL      $LOC_HOST
" >> /etc/hosts

mv /etc/resolv.conf /etc/resolv.conf.BACK
echo "
search $DOMAIN_SM
nameserver $IP_OF_DC
" >> /etc/resolv.conf
Включаем маршрутизацию пакетов, чтобы наш прокси выполнял функции маршрутизатора:
echo "
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
" >> /etc/sysctl.conf
echo "1" > /proc/sys/net/ipv4/ip_forward
Настраиваем логирование всего, что делает в командной строке пользователь (в том числе root), лог будет храниться в /var/log/commands.log (полезная штука):
mkdir ~/downloads
cd ~/downloads
wget $FTP_PATH/bash_logging/string.txt
cat string.txt >> /etc/bash.bashrc
touch /etc/rsyslog.d/bash.conf
touch /var/log/commands.log
echo "local6.*    /var/log/commands.log" >> /etc/rsyslog.d/bash.conf
/etc/init.d/rsyslog restart
Теперь все команды будут вписываться в лог после переподключения
Если вы вручную вводите данные, и не пользуйтесь теми файлами, что я предоставил вначале, то для вас содержимое /etc/bash.bashrc должно дополнится следующей строчкой:
export PROMPT_COMMAND='RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" ) [$RETRN_VAL]"'
Настраиваем временную зону (в моем случае это Екатеринбург, у вас вероятнее всего своя):
cp /usr/share/zoneinfo/Asia/Yekaterinburg /etc/localtime
Настраиваем NTP:
mv /etc/ntp.conf /etc/ntp.conf.BACK
echo "
driftfile /var/lib/ntp/ntp.drift
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict 127.0.0.1
restrict ::1
#server
server $NTP_SERVER
" >> /etc/ntp.conf

/etc/init.d/ntp restart
/etc/init.d/rsyslog restart
Настраиваем Samba:
mv /etc/samba/smb.conf /etc/samba/smb.conf.BACK
echo "
[global]
        workgroup = $DOMAIN
        netbios name = $LOC_HOST
        server string = $DOMAIN Proxy
        log file = /var/log/samba/log.%m
        max log size = 50
        log level = 3
        #hosts allow = 192.168.0.0/24 127.0.0.1
        #map to guest = bad user
        realm = $DOMAIN_SM
        nt acl support = yes
        nt status support = yes

        security = ads
        auth methods = winbind
        password server = $IP_OF_DC
        encrypt passwords = yes
        winbind separator = +
        winbind uid = 10000-20000
        winbind gid = 10000-20000
        winbind enum groups = yes
        winbind enum users = yes
        socket options = TCP_NODELAY SO_RCVBUF=16384 SO_SNDBUF=16384
        #interfaces = 192.168.0.248/24
        local master = no
        os level = 0
        domain master = no
        preferred master = no
        domain logons = no
        name resolve order = host lmhosts bcast
        dns proxy = no
" >> /etc/samba/smb.conf
Настраиваем kerberos
mv /etc/krb5.conf /etc/krb5.conf.BACK
echo "
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5dc.log
admin_server = FILE:/var/log/kerberos/ksadmind.log
[libdefaults]
default_realm = $DOMAIN_RU
[realms]
$DOMAIN_RU = {
kdc=$IP_OF_DC
default_domain=$DOMAIN_SM
admin_server=$IP_OF_DC
}
[domain_realm]
$DOMAIN_SM = $DOMAIN_RU
.$DOMAIN_SM = $DOMAIN_RU
" >> /etc/krb5.conf
Настраиваем nsswitch
mv /etc/nsswitch.conf /etc/nsswitch.conf.BACK
echo "
passwd:         files winbind compat
group:          files winbind compat
shadow:         files winbind compat
gshadow:        files
hosts:          files dns
networks:       files
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
netgroup:       nis
" >> /etc/nsswitch.conf
Настройки winbindd_priv. Суть в том, что по умолчанию squid не имеет доступа к получаемым NTLM ключам связки winbind+samba.
gpasswd -a proxy winbindd_priv
chown root:proxy -R /var/lib/samba/winbindd_privileged
sed -i -e '/chgrp winbindd_priv $PIDDIR\/winbindd_privileged\/ || return 1/ c\chgrp proxy $PIDDIR\/winbindd_privileged\/ || return 1' /etc/init.d/winbind

/etc/init.d/samba restart
/etc/init.d/winbind restart
Предварительная настройка Squid3. Предположительно у нас есть пользователи, доступ которых в интернет осуществляется без авторизации. Например это руководители, которых мы не хотим утруждать вводом всяческих логинов и паролей. Это действительно только для какого-то типа авторизации, в данном случае NTLM. В дальнейшем, когда будет выполнен переход на ip авторизацию - эти настройки потеряют смысл.
ln -s /usr/sbin/squid3 /usr/sbin/squid
touch /etc/squid3/01-superusers
touch /etc/squid3/02-allowedsites
touch /etc/squid3/03-our_network
Для пользователей, доступ которым на прокси будет разрешен без авторизации:
echo "
192.168.0.10  # Boss IP 1
192.168.10.10 # Boss IP 2
192.168.20.20 # Boss IP 3
" >> /etc/squid3/01-superusers
Сайты, на которые будет разрешен доступ без авторизации для всех из локальной сети:
echo "
#local net----------------------
$DOMAIN_SM
#cloud.mail.ru----------------------
.datacloudmail.ru
cloud.radar.imgsmail.ru
cloud.mail.ru
headline.radar.imgsmail.ru
portal.mail.ru
cloclo1.cloud.mail
#mail.yandex.ru----------------------
mail.yandex.ru
#google.com----------------------
.google.com
" >> /etc/squid3/02-allowedsites
Локальная сеть, из которой будет разрешен доступ на сайты без авторизации:
echo "
192.168.0.0/24   # LAN-1
192.168.10.0/24  # LAN-2
192.168.20.0/24  # LAN-3
" >> /etc/squid3/03-our_network
Так как изначально Squid3 не поддерживает прозрачный режим работы с динамической генерацией сертификатов, нам необходимо его "пересобрать". В Debian это делается относительно просто:
cd /usr/src
mv /etc/squid3/squid.conf /etc/squid3/squid.conf.BACK
apt-get source squid3
apt-get build-dep squid3
apt-get build-dep squid
apt-get build-dep openssh
apt-get build-dep openssl
apt-get install devscripts build-essential fakeroot
cd squid3-*
sed -i -e 's/--enable-inline/--enable-inline --enable-ssl/' ./debian/rules
./configure
debuild -us -uc -b
dpkg -i -y squid3_3*amd64.deb  squid3-common*all.deb squid3-dbg*amd64.deb
Дальше нужно заменить конфиг squid-а на ранее подготовленный:
cd ~/downloads
wget $FTP_PATH/configs/2_squid_TRANSPERENT+NTLM_conf.txt
cat 2_squid_TRANSPERENT+NTLM_conf.txt > /etc/squid3/squid.conf
Если вы делаете это вручную, то основная задача настройки заключается в следующем. Мы объявляем 2 порта, которые будут работать в прозрачном режиме, и 1 который будет работать в режиме авторизации:
http_port 3128
http_port 3126 intercept
https_port 3127 intercept ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/etc/squid3/openssl/public.pem key=/etc/squ$
always_direct allow all
ssl_bump allow all
##Uncommented 2 lines below, if you allow all ssl connectios, even dont verify sertificate.
#sslproxy_cert_error allow all
#sslproxy_flags DONT_VERIFY_PEER
Так как прокси работает в прозрачном режиме, нам необходимо работать с сертификатами. В таком режиме пользователь устанавливает SSL соединение с прокси, а проси непосредственно с запрашиваемым ресурсом. Появляется возможность контролировать весь трафик внутри зашифрованного соединения. Но появляется и проблема, связанная с постоянным предупреждением пользователя о том, что сертификат не действителен. Нам в дальнейшем нужно будет добавить всем пользователям сертификат от прокси в доверенные:
mkdir /etc/squid3/openssl
cd /etc/squid3/openssl
openssl genrsa 4096 > private.pem
openssl req -new -x509 -days 3650 -key private.pem -out public.pem
openssl x509 -in public.pem -outform DER -out squid_user.der
После чего у нас в папке появится 3 файла:
private.pem - сертификат
public.pem - публичный ключ
squid_user.der - сертификат для конечных пользователей, который нужно установить на все клиентские машины в "Доверенные корневые центры сертификации".
Создадим небольшую напоминалку в данной папке:
echo '
Так как в данном случае мы используем self-signed сертификат, любые посещения https сайтов через прокси 
будут показывать пользователям ошибку сертификата. 
Причина ошибки — Issuer нашего сертификата не находится в списке Trusted CA в браузере.
Что бы ошибок не было, выполняем следующее действие.

Теперь полученный файл squid_user.der надо импортировать в клиентский браузер.
Для Internet Explorer:
Tools->Internet Options->Content->Certificates
Выбираем закладку «Trusted Root Certificate Authorities» (Доыеренные корневые центры сертификации)
Жмём Import, выбираем файл squid.der и завершаем импорт.

Для Firefox:
Tools->Options->Advanced->Encryption->View Certificates
Выбираем закладку «Authorities»
Жмём Import, выбираем файл squid.der и завершаем импорт.

Ну вот в общем то всё. В зависимости от ваших фантазий, теперь у вас есть возможность в https трафике 
запретить делать POST запросы, скачивать большие файлы, закрыть доступ к определённым файлам/папкам. 
Так же можно запретить доступ на сайты, сертификаты которых выданы не доверенными CA. Ну и возможность проверять на вирусы.
' > readme
Установка Sams2
cd ~/downloads
wget https://launchpad.net/ubuntu/+archive/primary/+files/libzip1_0.9.3-1_amd64.deb
wget http://launchpadlibrarian.net/94808408/libmysqlclient16_5.1.58-1ubuntu5_amd64.deb
dpkg -i libzip1_0.9.3-1_amd64.deb
dpkg -i libmysqlclient16_5.1.58-1ubuntu5_amd64.deb

wget http://nixdev.net/release/sams/devel/packages/debian/sams2_2.0.0-rc2_amd64.deb
dpkg -i sams2_2.0.0-rc2_amd64.deb
wget http://nixdev.net/release/sams/devel/packages/debian/sams2-doc_2.0.0-rc2_all.deb
wget http://nixdev.net/release/sams/devel/packages/debian/sams2-web_2.0.0-rc2_all.deb
dpkg -i sams2-doc_2.0.0-rc2_all.deb && dpkg -i sams2-web_2.0.0-rc2_all.deb
Настройка Sams2
cp /etc/sams2.conf /etc/sams2.conf.BACK
sed -i -e 's/DB_USER=/DB_USER=sams/' /etc/sams2.conf
sed -i -e 's/DB_PASSWORD=/DB_PASSWORD='$SAMSDB_PASS'/' /etc/sams2.conf
sed -i -e 's/squid/squid3/' /etc/sams2.conf
sed -i -e '/SHUTDOWNCOMMAND=shutdown -h now/ c\SHUTDOWNCOMMAND=shutdown -r now' /etc/sams2.conf
sed -i -e 's|SQUIDCACHEDIR=/usr/local/apache2|SQUIDCACHEDIR=/var/spool/squid3|' /etc/sams2.conf
sed -i -e 's/SAMS_ENABLE=false/SAMS_ENABLE=true/' /etc/init.d/sams2
Создаем линки в апаче для самса
echo '
Alias /sams2 /usr/share/sams2
<Directory "/usr/share/sams2">
    Options Indexes FollowSymlinks
    AllowOverride None
    Require all granted
    AddDefaultCharset off
</Directory>
<Location "/sams2">
    Options Indexes
    Order allow,deny
    Allow from all
</Location>
' > /etc/apache2/conf-available/sams2.conf

echo '
Alias /sams2/doc /usr/share/doc/sams2-doc/
 <Directory "/usr/share/doc/sams2-doc/">
    Options Indexes FollowSymlinks
    AllowOverride None
    Require all granted
    AddDefaultCharset off
</Directory>
 <Location "/sams2/doc">
    Options Indexes
    Order allow,deny
    Allow from all
</Location>
 ' > /etc/apache2/conf-available/doc4sams2.conf
 
ln -s ../conf-available/sams2.conf /etc/apache2/conf-enabled/
ln -s ../conf-available/doc4sams2.conf /etc/apache2/conf-enabled/
chown -R www-data:www-data /usr/share/sams2/data/

/etc/init.d/apache2 restart
/etc/init.d/squid3 restart
/etc/init.d/sams2 restart
Настройки HAVP, который будет проверять весь траффик на вирусы
mv /etc/havp/havp.config /etc/havp/havp.config.BACK
echo '
USER havp
GROUP havp
DAEMON true
PIDFILE /var/run/havp/havp.pid
SERVERNUMBER 10
MAXSERVERS 100
ACCESSLOG /var/log/havp/access.log
ERRORLOG /var/log/havp/error.log
USESYSLOG false
LOG_OKS false
LOGLEVEL 1
SCANTEMPFILE /var/spool/havp/havp-XXXXXX
TEMPDIR /var/tmp
DBRELOAD 60
FORWARDED_IP true
PORT 8081
BIND_ADDRESS 127.0.0.1
TEMPLATEPATH /etc/havp/templates/ru
WHITELISTFIRST true
WHITELIST /etc/havp/whitelist
BLACKLIST /etc/havp/blacklist
FAILSCANERROR false
SCANNERTIMEOUT 10
RANGE true
SCANIMAGES false
MAXSCANSIZE 5000000
STREAMUSERAGENT Player Winamp iTunes QuickTime Audio RMA/ MAD/ Foobar2000 XMMS
IGNOREVIRUS Oversized. Encrypted. Phishing
ENABLECLAMLIB true
CLAMDBDIR /var/lib/clamav
CLAMBLOCKBROKEN false
CLAMBLOCKENCRYPTED false
CLAMBLOCKMAX false
CLAMMAXFILES 500
CLAMMAXFILESIZE 10
CLAMMAXRECURSION 8
ENABLEFPROT false
ENABLEAVG false
ENABLEAVESERVER false
ENABLESOPHIE false
ENABLETROPHIE false
ENABLENOD32 false
ENABLEAVAST false
ENABLEARCAVIR false
ENABLEDRWEB false

' > /etc/havp/havp.config

echo '
*.youtube.com*
*.$DOMAIN_SM*
*.apple.com*
' >> /etc/havp/whitelist
Вводим прокси в домен
net join -I $IP_OF_DC -U $ADMIN_LOGIN_FOR_DC_JOIN%$ADMIN_DC_PSW
Проверка, должно вывести все что видит в домене
wbinfo -g
wbinfo -u
wbinfo -t
Добавляем необходимые настройки в MYSQL
mysql -u root -p
GRANT ALL ON squidctrl.* TO 'sams'@localhost IDENTIFIED BY "SAMSDB_PASS";
GRANT ALL ON squidlog.* TO 'sams'@localhost IDENTIFIED BY "SAMSDB_PASS";
GRANT ALL ON sams2db.* TO 'sams'@localhost IDENTIFIED BY "SAMSDB_PASS";
flush privileges;
Ну и IPTABLES.
Iptables это очень мощный инструмент для работы с сетью, чтобы описать все возможности нужна отдельная статья.
В нашем случае все более тривиально, нам нужен редирект с 80 и 443 портов на 3126 и 3127 порты соответственно + некоторые базовые настройки
#-------------УДАЛЯЕМ ВСЕ НАСТРОЙКИ----------------
IPTABLES="/sbin/iptables"
$IPTABLES -P INPUT ACCEPT
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
$IPTABLES -t nat -P OUTPUT ACCEPT
$IPTABLES -t mangle -P PREROUTING ACCEPT
$IPTABLES -t mangle -P OUTPUT ACCEPT
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
$IPTABLES -X
$IPTABLES -t nat -X
$IPTABLES -t mangle -X
###############################################################################
#-------------Настраиваем----------------
# 1.1 Настройки сети 
INET_IP="10.10.0.2/30"
INET_IFACE="eth0"
INET_BROADCAST="255.255.255.255"
# локалка
LAN_IP="192.168.0.0/24"
LANN="192.168.0.1"
LAN_IP_RANGE="192.168.10.0/24"
LAN_IP_RANGE2="192.168.20.0/24"
LAN_IFACE="eth1"
# локалхост
LO_IFACE="lo"
LO_IP="127.0.0.1"
#Открытые порты (через пробел)
open_tcp_ports="22 80 443 3126 3127 3128 10000"
open_udp_ports="123"
open_icmp_state="8 11"
###########################################################################
# 2. Module loading.
/sbin/depmod -a
/sbin/modprobe ip_tables
/sbin/modprobe ip_conntrack
/sbin/modprobe iptable_filter
/sbin/modprobe iptable_mangle
/sbin/modprobe iptable_nat
/sbin/modprobe ipt_LOG
/sbin/modprobe ipt_limit
/sbin/modprobe ipt_state
###########################################################################
# 3.1 Включаем режим роутера
echo "1" > /proc/sys/net/ipv4/ip_forward
# ----------------------Правила----------------------------------
# 4.1 ТАБЛИЦА Filter
#Умолчания
$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT DROP
$IPTABLES -P FORWARD DROP
#Создаем цепочки
$IPTABLES -N bad_tcp_packets
                $IPTABLES -A bad_tcp_packets -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j REJECT --reject-with tcp-reset
                $IPTABLES -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
                $IPTABLES -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j DROP

$IPTABLES -N allowed
                $IPTABLES -A allowed -p TCP --syn -j ACCEPT
                $IPTABLES -A allowed -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
                $IPTABLES -A allowed -p TCP -j DROP

$IPTABLES -N tcp_packets
                for open_tcp in $open_tcp_ports
                        do
                        $IPTABLES -A tcp_packets -p TCP --dport $open_tcp -j allowed
                done

$IPTABLES -N udp_packets
                for open_udp in $open_udp_ports
                        do
                        $IPTABLES -A udp_packets -p UDP --dport $open_udp -j allowed
                done

$IPTABLES -N icmp_packets
                for open_icmp in $open_icmp_state
                        do
                        $IPTABLES -A icmp_packets -p ICMP --icmp-type $open_icmp -j ACCEPT
                done

# 4.1.4 ТАБЛИЦА INPUT
# Подключения к прокси
# Сначала пропускаем уже ранее установленые соединения
$IPTABLES -A INPUT -p tcp -j bad_tcp_packets
$IPTABLES -A INPUT -i $INET_IFACE -d $INET_IP -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -i $LAN_IFACE -d $LAN_IP -m state --state ESTABLISHED,RELATED -j ACCEPT
# пропускаем соединения к прокси от локалки и лупбак
$IPTABLES -A INPUT -i $LAN_IFACE -s $LAN_IP_RANGE -j ACCEPT
$IPTABLES -A INPUT -i $LAN_IFACE -s $LAN_IP_RANGE2 -j ACCEPT
$IPTABLES -A INPUT -i $LO_IFACE -s $LO_IP -j ACCEPT
$IPTABLES -A INPUT -i $LO_IFACE -s $LAN_IP -j ACCEPT
$IPTABLES -A INPUT -i $LO_IFACE -s $INET_IP -j ACCEPT
# фильтруем открытые порты для разных протоколов
$IPTABLES -A INPUT -p TCP -i $LAN_IFACE -s $LAN_IP_RANGE  -j tcp_packets
$IPTABLES -A INPUT -p TCP -i $LAN_IFACE -s $LAN_IP_RANGE2  -j tcp_packets
$IPTABLES -A INPUT -p UDP -i $LAN_IFACE -s $LAN_IP_RANGE -j udp_packets
$IPTABLES -A INPUT -p UDP -i $LAN_IFACE -s $LAN_IP_RANGE2 -j udp_packets
$IPTABLES -A INPUT -p ICMP -i $LAN_IFACE -s $LAN_IP_RANGE -j icmp_packets
$IPTABLES -A INPUT -p ICMP -i $LAN_IFACE -s $LAN_IP_RANGE2 -j icmp_packets
$IPTABLES -A INPUT -i $INET_IFACE -d 224.0.0.0/8 -j DROP
$IPTABLES -A INPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level debug --log-prefix "IPT INPUT packet died: "

# 4.1.5 ТАБЛИЦА FORWARD
$IPTABLES -A FORWARD -p tcp -j bad_tcp_packets
#$IPTABLES -A FORWARD -i $LAN_IFACE -j ACCEPT
$IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level debug --log-prefix "IPT FORWARD packet died: "

# 4.1.6 ТАБЛИЦА OUTPUT
$IPTABLES -A OUTPUT -p tcp -j bad_tcp_packets
$IPTABLES -A OUTPUT -s $LO_IP -j ACCEPT
$IPTABLES -A OUTPUT -s $LAN_IP -j ACCEPT
$IPTABLES -A OUTPUT -s $INET_IP -j ACCEPT
$IPTABLES -A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level debug --log-prefix "IPT OUTPUT packet died: "

# 4.2 ТАБЛИЦА nat
$IPTABLES -A PREROUTING ! -d $LAN_IP -i eth1 -p tcp -m tcp --dport 80 -j DNAT --to-destination $LANN:3126
$IPTABLES -A PREROUTING ! -d $LAN_IP -i eth1 -p tcp -m tcp --dport 443 -j DNAT --to-destination $LANN:3127
#$IPTABLES -A POSTROUTING -o eth0 -j MASQUERADE
Вот собственно и все. Теперь можно зайти на http://proxy_ip/sams2/ и настроить самс под свои нужды, логин по умолчанию admin, пароль qwerty. Так же доступен Webmin по адресу https://proxy_ip:10000 логин и пароль по умолчанию от учетной записи root сервера.
Полный скрипт для развертывания данной конфигурации за несколько минут можно скачать ТУТ