Возникла задача обновить сервера LinOTP, которые используются в качестве сервиса 2х факторной авторизации. В текущей конфигурации сервиса, в качестве бекенда используется pgpool+postgresql, и новой реализации решил отказаться от этого, так как жизнь с pgpool накладывает дополнительный overhead.
(Схема нового кластера)
Установка PostgreSQL
Настройку на серверах начнем с самого нижнего слоя, установим и подготовим к использованию базу. Включаем модуль с Postgresql версией 13, и ставим пакеты:
[All Servers]# yum module enable -y postgresql:13
[All Servers]# yum install postgresql -y postgresql-server
Проинициализируем базу данных:
[All Servers]# /usr/bin/postgresql-setup --initdb
* Initializing database in '/var/lib/pgsql/data'
* Initialized, logs are in /var/lib/pgsql/initdb_postgresql.log
Запускаем базу, и ставим сервис в автозагрузку:
[All Servers]# systemctl enable --now postgresql
На данном этапе нам нужно отрыть доступ между двумя инстансами бд, для реализации задачи по копированию базы на вторичный инстанс. Итак, для начала отредактируем файл конфигурации - postgresql.conf:
[All Servers]# vi /var/lib/pgsql/data/postgresql.conf
--
# Адрес, с которого слушает сервис
listen_addresses = 'localhost,<NODE IP>'
# Порт, на котором слушает сервис
port = 5432
Теперь создаем базу данных и пользователя с правами на эту базу:
[All Servers]# sudo -u postgres psql
# Задаем механизм шифрования паролей
postgres=# set password_encryption = 'scram-sha-256';
# Создаем базу и пользователя
postgres=# create database linotp_db;
postgres=# create user linotp_user with encrypted password 'pass';
# Даем права пользователю на базу
postgres=# grant all privileges on database linotp_db to linotp_user;
Разрешим доступ к базе, для наших серверов.
[All Servers]# vi /var/lib/pgsql/data/pg_hba.conf
---
# TYPE DATABASE USER ADDRESS METHOD
host linotp_db linotp_user 127.0.0.1/32 scram-sha-256
host linotp_db linotp_user 192.168.110.11/32 scram-sha-256
host linotp_db linotp_user 192.168.110.12/32 scram-sha-256
Перезапускаем сервис с базой:
[All Servers]# systemctl restart postgresql
На фаерволе не забываем открыть порт:
[All Servers]# firewall-cmd --add-port=5432/tcp --permanent
[All Servers]# firewall-cmd --reload
Для проверки корректности настроек можно попробовать подключиться к ноде:
[All Servers]# psql -h 192.168.110.12 -U linotp_user linotp_db -p 5432
Как я ранее говорил, в этой конфигурации я решил отказаться от использования pgpool-кластера, и использовать базовые инструменты по бекапированию/восстановлению базы. Хоть и получается костыль, но рациональней так =) Напишем скриптец, который будет снимать бекап с основного сервера и восстанавливать его в реплику.
#!/bin/bash
DB_SRV="<IP-АДРЕС ГЛАВНОГО СЕРВЕРА>"
DB_PORT="5432"
DB_USER="linotp_user"
DB_NAME="linotp_db"
BACKUP_PATH="/var/lib/pgsql/backups"
BACKUP_NAME="$(date +%d%m%Y%H).dump.tar"
function restoreDump() {
echo "---"
echo "Restore fresh database dump..."
psql -h 127.0.0.1 -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$BACKUP_PATH/$BACKUP_NAME"
}
function backupDatabase() {
echo "---"
echo "Create new backup..."
if [[ ! -f "$BACKUP_PATH/$BACKUP_NAME" ]]; then
pg_dump -U $DB_USER -h $DB_SRV -p $DB_PORT $DB_NAME > "$BACKUP_PATH/$BACKUP_NAME" && echo "Backup was been created!"
restoreDump
else
echo "Backup is already up to date!"
fi
}
nc -z -w5 $DB_SRV $DB_PORT < /dev/null
if [ $? = 0 ]; then
backupDatabase
else
echo "Problem connect to $DB_SRV port $DB_PORT"
fi
В начале скрипта перечисляется блок с переменными, в которых обозначаем данные подключения к базе. Следом идет две функции restoreDump()
и backupDatabase()
, как понятно из названия одна восстанавливает бекап, другая снимает бекап с мастер сервера.
И далее описывает entry-часть скрипта, мы проверяем доступность основного сервера, если он доступен запускаем функцию по бекапу базы. Если недоступен, выходим из сценария.
Для подключения к базе, как к локальной так и удаленной, каждый раз нужно вводить пароль. Добавим запись в .pgpass
, на второй базе для доступа без пароля.
В домашнем каталоге пользователя, создаем файл .pgpass
:
[postgres@lotp02 ~]$ vim .pgpass
---
127.0.0.1:5432:linotp_db:linotp_user:<PASSWORD>
192.168.110.11:5432:linotp_db:linotp_user:<PASSWORD>
Даем права на чтение/запись только владельцу:
[postgres@lotp02 ~]$ chmod 600 .pgpass
Ну и теперь можем запустить скриптец, для проверки:
[postgres@lotp02 ~]$ bash scripts/backup_db.sh
Connection to lotp01.office.local (192.168.110.11) 5432 port [tcp/postgres] succeeded!
---
Create new backup...
Backup is already up to date!
Отлично, теперь для выполнения сценария не нужно будет указывать пароль для пользователя.
Теперь остается только добавить скрипт на исполнение в cron, в файл /etc/crontab
добавляем строчку:
0 */5 * * * postgres bash -c '/var/lib/pgsql/scripts/backup_db.sh'
(Каждые 5 часов будет запускаться скриптец)
Установка LinOTP сервиса
На данный момент в rpm-репозитории LinOTP есть пакеты поддерживаемые только на CentOS7. Мы же используем 8 версию центоси, и будет собирать сервис из пакетов. (Установка выполняется на всех серверах)
В первую очередь установим все записимости, сервис написан на второй версии python. Ставим необходимые для сборки пакеты.
[All Servers]# yum install epel-release
[All Servers]# yum install -y python2 python2-virtualenv python2-wheel git openssl-devel swig openldap-devel libsodium gcc
Создаем новое виртуальное окружение, куда в дальшейшем поставим зависимости.:
[All Servers]# cd /srv/
[All Servers - srv]# virtualenv-2 linotp
Переключаемся в окружение, и ставим пакеты.:
[All Servers - srv]# source /srv/linotp/bin/activate
(linotp) [All Servers - srv]# pip install --upgrade pip pip-tools
(linotp) [All Servers - srv]# pip install pillow pillow-pil m2crypto psycopg2-binary
Теперь клонируем репозиторий LinOTP.
[All Servers - srv]# cd /srv/linotp
(linotp) [All Servers - linotp]# git clone https://github.com/LinOTP/LinOTP
В корне репозитория нужно переключится на стабильную ветку. Для начала находим последний релиз, командой:
(linotp) [All Servers - linotp]# cd LinOTP/
(linotp) [All Servers - LinOTP]# git tag | grep release/2 | sort --version-sort
---
release/2.12.2
release/2.12.3
release/2.12.4
release/2.12.5
release/2.12.6
Из вывода видно, что последний релиз под номером release/2.12.6
. Переключаемся на него:
(linotp) [All Servers - LinOTP]# git checkout release/2.12.6
Можно приступить к сборке. Собирать будем два пакета - LinOTP
, LinOTPAdmiCLI
. Что бы собрать пакет совместимый с python2, сборку реализуем при помощи pip.
Собираем основную прилу, для этого переходим в исходники - linotpd
(/srv/linotp/LinOTP/linotpd/src). И собираем wheel-пакет:
(linotp) [All Servers - LinOTP]# cd linotpd/src/
(linotp) [All Servers - src]# python2 setup.py bdist_wheel
Полученный пакет устанавливаем через pip:
(linotp) [All Servers - src]# pip install dist/LinOTP-2.12.6-py2-none-any.whl
Если на этом этапе никаких ошибок не было поймано, собираем пакет c admin cli. Переходим в исходники - /srv/linotp/LinOTP/adminclient/LinOTPAdminClientCLI/src
, и аналогично собираем wheel-пакет:
(linotp) [All Servers - src]# cd ../../adminclient/LinOTPAdminClientCLI/src/
(linotp) [All Servers - src]# python2 setup.py bdist_wheel
И теперь через pip устанавливаем пакет:
(linotp) [All Servers - src]# pip install dist/LinOTPAdminClientCLI-2.12.6-py2-none-any.whl
Установка завершена, но для работы сервиса нужно добавить еще нескольно настроек. Создать каталоги под конфигурации и логи, добавить пользователя для сервиса. Создаем пользователя:
(linotp) [All Servers - src]# useradd -d /srv/linotp -U -r -s /sbin/nologin linotp
Создаем каталог под конфигурационные файлы сервиса:
(linotp) [All Servers - src]# mkdir -pv /etc/linotp2/data
(linotp) [All Servers - src]# mkdir -pv /var/log/linotp
Копируем семпл файла конфигурации в каталог /etc:
(linotp) [All Servers - src]# cp /srv/linotp/etc/linotp2/linotp.ini.example /etc/linotp2/linotp.ini
Генерим файл-секрет, которым будем шифровать токены в базе данных:
(linotp) [All Servers - src]# linotp-create-enckey -f /etc/linotp2/linotp.ini
Созданный файл (/etc/linotp2/encKey), после установки сервиса, нужно разместить на втором сервере. Иначе вторая нода не сможет прочитать токены, и нечего не будет работать.
Ну и меняем овнера на созданных каталогах:
(linotp) [All Servers - src]# chown -R linotp:linotp /etc/linotp2/
(linotp) [All Servers - src]# chown -R linotp:linotp /var/log/linotp/
(linotp) [All Servers - src]# chown -R linotp /srv/linotp/
Настройка LinOTP Сервиса
Для подключения к базе Postgresql отредактируем конфиг, пропишем настройки подключения к базе:
[root@lotp01 bin]# vim /etc/linotp2/linotp.ini
---
sqlalchemy.url = postgresql+psycopg2://linotp_user:<PASS>@127.0.0.1:5432/linotp_db
Создаем схемы в базе данных:
(linotp) [root@lotp01 LinOTP]# su linotp -s /bin/bash -c 'paster setup-app /etc/linotp2/linotp.ini'
Running setup_app() from linotp.websetup
Настройка Apache2
В качестве вебсервера будем использовать apache, для начала установим его и дополнительные модули (Логичным образом, установка выполняется на всех нодах):
[All Servers]# yum install -y httpd httpd-devel mod_ssl
Для взаимодействия апача с нашим приложением, нужно установить wsgi. Переключаемся в окружение и ставим через pip:
(linotp) [All Servers]# pip install mod_wsgi
Установленный модуль линкуем в каталог с модулями апача:
(linotp) [All Servers]# ln -s /srv/linotp/lib/python2.7/site-packages/mod_wsgi/server/mod_wsgi-py27.so /etc/httpd/modules/
Теперь нужно заставить апач, подгружать этот модуль. Создамим отдельный конфигурационный файл:
(linotp) [[All Servers]# echo "LoadModule wsgi_module modules/mod_wsgi-py27.so" >> /etc/httpd/conf.modules.d/99-wsgi.conf
Для загрузки нашего приложения апач будет использовать python-скрипт. Этот скрипт уже есть в семплах, поэтому просто копируем его:
[All Servers]# cp /srv/linotp/etc/linotp2/linotpapp.wsgi /etc/linotp2/
[All Servers]# chown -R linotp:apache /etc/linotp2/linotpapp.wsgi
Ну и копируем конфиг для самого апача:
[All Servers]# cp /srv/linotp/LinOTP/linotpd/src/config/apache2.4-example/linotp2.conf /etc/httpd/conf.d/
Для корректной работы веб-сервера, нужно внести некоторые изменения в скопированный конфиг.
[All Servers]# vi /etc/httpd/conf.d/linotp2.conf
---
## Закомментируем или удаляем инклуд
#Include /etc/linotp2/apache-servername.conf
## Прописываем хостнеймы, запросы на которые будет обрабатывать этот вебсервер
ServerName lotp.office.local
ServerName lotp01.office.local
## Указываем путь к установочному каталогу
WSGIDaemonProcess linotp processes=1 threads=15 display-name=%{GROUP} user=linotp python-home=/srv/linotp
## Меняем пути до логов:
ErrorLog /var/log/httpd/error.log
CustomLog /var/log/httpd/access.log LinOTP2
## Меняем пути к сертификатам:
SSLCertificateFile /etc/linotp2/linotpserver.pem
SSLCertificateKeyFile /etc/linotp2/linotpserver.key
Для доступа к админку сервиса используется basic auth, в семпле конфига он уже настроен. Нам остается только забиндить пароль для пользователя admin:
[All Servers]# htdigest -c /etc/linotp2/admins 'LinOTP2 admin area' admin
[All Servers]# chown -R linotp:apache /etc/linotp2/admins
Далее просто указываем пароль для пользователя.
Для теста, что бы запустить и проверить сервис, я сгенерю самоподписной сертификат и ключ. Далее уже приктурим сертификат подписанный нашим локальным ca.
[All Servers]# openssl req -subj '/CN=192.168.110.11' -new -newkey rsa:2048 -sha256 -days 3650 -nodes -x509 -keyout /etc/linotp2/linotpserver.key -out /etc/linotp2/linotpserver.pem
Что бы у апача был доступ на чтение linotpapp.wsgi
, добавил пользователя - apache
в группу - linotp
:
(linotp) [All Servers]# usermod -aG linotp apache
И сново рекурсивно сделал владельцем пользователя - linotp
в каталоге - /srv/linotp/
, так как модуль wsgi был установлен из под рута.:
(linotp) [All Servers]# chown -R linotp /srv/linotp/
Запускаем сервис:
[All Servers]# systemctl enable --now httpd
Ну и не забываем открыть порт на фаерволе:
[All Servers]# firewall-cmd --add-port=443/tcp --permanent
[All Servers]# firewall-cmd --reload
Настройка Radius
Для работы радиус сервера, нужно установить пакеты самого радиус сервера и его перловые дополнения.
[All Servers]# yum install freeradius freeradius-perl perl-Config-IniFiles perl-Try-Tiny perl-LWP-Protocol-https
Добавляем клиента, который будет кидать запросы с авторизацией пользователей через радиус. В моем случаи клиентом будет выступать Cisco Identity Services:
[All Servers]# mv /etc/raddb/clients.conf /etc/raddb/clients.conf.df
[All Servers]# vi /etc/raddb/clients.conf
---
client ise0 {
ipaddr = 192.168.110.20 # Active ISE node
netmask = 32
secret = 'PASSWORD' #shared secret
}
Отредактируем файл - /etc/raddb/users
, в котором описывается конфигурация как авторизовать и аутентифицироваться каждый запрос пользователя. В моем случаи этот радиус сервер, будет использоваться только для LinOTP. Поэтому прописываем:
[All Servers]$ mv /etc/raddb/users /etc/raddb/users.df
[All Servers]# echo "DEFAULT Auth-Type := perl" >> /etc/raddb/users
Создаем конфиг с описанием нашего модуля.:
[All Servers]# mv /etc/raddb/mods-available/perl /etc/raddb/mods-available/perl.default
[All Servers]# vi /etc/raddb/mods-available/perl
---
perl {
filename = /usr/lib/linotp/radius_linotp.pm
}
Далее идем поссылке, и качаем сам модуль тут. Скачанный модуль закидываем в каталог - /usr/lib/linotp/
.
[All Servers]# ls -lah /usr/lib/linotp/radius_linotp.pm
-rw-r-----. 1 root root 12K Mar 27 15:29 /usr/lib/linotp/radius_linotp.pm
Также мы должны дать права на чтение файла для пользователя - radiusd.:
[All Servers]# chown -R radiusd /usr/lib/linotp/
[All Servers]# chmod 750 /usr/lib/linotp/radius_linotp.pm
Для работы этого модуля, нужно создать файл конфигурации realm.
[All Servers]# vi /etc/linotp2/rlm_perl.ini
---
[Default]
#IP of the linotp server
URL=https://lotp.office.local/validate/simplecheck
#optional: limits search for user to this realm
REALM=MyOffice
#optional: only use this UserIdResolver
#RESCONF=flat_file
#optional: comment out if everything seems to work fine
Debug=False
#optional: use this, if you have selfsigned certificates, otherwise comment out
SSL_CHECK=False
В этой конфиге указываем свои значения в переменных. Url-адрес linotp сервиса и опциональные директивы.
Ну и создаем виртуальный хост радиус-сервера:
[All Servers]# vi /etc/raddb/sites-available/linotp
--
server linotp {
listen {
ipaddr = *
port = 0
type = auth
}
authorize {
preprocess
IPASS
suffix
ntdomain
files
expiration
logintime
pap
update control {
Auth-Type := Perl
}
}
authenticate {
Auth-Type Perl {
perl
}
}
}
С конфигурацией на этом все, остается только включить конфиги создав симлинки, и запустить сервис.
Удаляю дефолтные файлы:
[All Servers]# rm -rf /etc/raddb/sites-enabled/{default,inner-tunnel}
[All Servers]# rm -rf /etc/raddb/mods-enabled/eap
И линкую новую конфигурацию:
[All Servers]# cd /etc/raddb/sites-enabled/
[All Servers - sites-enabled]# ln -s ../sites-available/linotp /etc/raddb/sites-enabled
[All Servers - sites-enabled]# cd /etc/raddb/mods-enabled
[All Servers - mods-enabled]# ln -s ../mods-available/perl /etc/raddb/mods-enabled
Открываем порты фаерлове,
[All Servers]# firewall-cmd --add-port={1812,1645}/udp --permanent
success
[All Servers]# firewall-cmd --reload
Ну и ранним сервис:
[All Servers]# systemctl enable --now radiusd.service
Настройка keepalived
В заключении устанавливаем keepalived:
[All Servers]# yum install keepalived
Переименовываем дефолтный конфиг, и создаем свою конфигу:
[root@lotp01 ~]# mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.df
[root@lotp01 ~]# vi /etc/keepalived/keepalived.conf
---
global_defs {
enable_script_security
}
vrrp_script postgresql_check {
script "/usr/bin/systemctl is-active postgresql >/dev/null && echo 1 || echo 0"
interval 5
user postgres
}
vrrp_script radius_check {
script "/usr/bin/systemctl is-active radiusd >/dev/null && echo 1 || echo 0"
interval 5
user radiusd
}
vrrp_script httpd_check {
script "/usr/bin/systemctl is-active httpd >/dev/null && echo 1 || echo 0"
interval 5
user apache
}
vrrp_instance linotp-cluster {
state MASTER
interface ens192
virtual_router_id 254
priority 100
advert_int 2
authentication {
auth_type PASS
auth_pass PASSWORD
}
virtual_ipaddress {
192.168.110.10
}
track_script {
postgresql_check
radius_check
httpd_check
}
}
Наш конфиг можно разделить на контексты,
global_defs
- задает глобальные настройки для keepalived. В нашей конфиге мы только разрешаем испольвание скриптов.vrrp_script
- в этом контексте указывается параметры проверки. У меня используется 3 проверки -postgresql_check
,radius_check
,httpd_check
с одинаковыми параметрами. Если выполнения скрипта заканчивается с exit кодом не равному - 0, то значит у нас проблемы в системе и ip-адрес будет назначен на второй ноде. Ну и далее как понятно из названия, мы указываем интервал равный пяти секунд, и пользователя из под которого будет выполняться проверка.vrrp_instance
- этот контекст описывает параметры, самого инстанса.state
- указывает на первоначальный режим работы ноды. У нас первая нода - Master, вторая в роли Backup;interface
- тут в значении указывается название интерфейса, на котором будет висеть кластерный ip.virtual_router_id
- виртуальный VRRP идентификатор, на всех участниках кластера должен быть одинаковым.priority
- приоритет ноды, у мастера должен быть самый наивысший приоритерadvert_int
- здесь указывается интервал в секундах, в течении которого мастер должен сообшить о себе другим нодам кластера. Если в течении этого времени, мастером не будет отправлен запрос, в кластере будет переизбран новый мастер.preempt_delay
- также указывается интервал, после которого нода с самым высоким приоритетом опять назначит себе адрес.authentication
- в этом контексте описываются настройки авторизации.virtual_ipaddress
- тут описываем настройки для кластерного ip-адреса.track_script
- ну и здесь указываются проверки работоспособности наших сервисов.
Конфиг для второй ноды, аналогичен конфигурации на первом серваке, за исключением паре параметров. Изменения коснулись только,
state
- тут в значении -> Backuppriority
- здесь в значении указываем более низкий приоритет (50, например)
Перед запуском, нужно открыть порт на фаерволе.
[All Servers]# firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
[All Servers]# firewall-cmd --reload
Ранним сервис:
[All Servers]# systemctl enable --now keepalived
Для проверки корректности работы сервиса, может поотрубать сервисы, что-бы убедится как отработает вторая нода. Например, на первой ноде я выключаю апач и бегом иду смотреть логи.
Mar 27 17:49:04 lotp01.office.local Keepalived_vrrp[15493]: Script `httpd_check` now returning 3
Mar 27 17:49:04 lotp01.office.local Keepalived_vrrp[15493]: VRRP_Script(httpd_check) failed (exited with status 3)
Mar 27 17:49:04 lotp01.office.local Keepalived_vrrp[15493]: (linotp-cluster) Entering FAULT STATE
Mar 27 17:49:04 lotp01.office.local Keepalived_vrrp[15493]: (linotp-cluster) sent 0 priority
Mar 27 17:49:04 lotp01.office.local Keepalived_vrrp[15493]: (linotp-cluster) removing VIPs.
Из этого журнала можно сделать вывод, что проверка httpd_check
вернула код выхода - 3, и мастер ушел в аварийное состояние. Далее у ноды выставился нулевой приоритер и vip адрес был удален.
Отлично, настройка “backend-ов” реализована. Все последующие настройки уже будет выполнятся непосредственно в UI-менеджменте сервиса. Также предстоит причесать настройки, прикрутить общую авторизацию к админке и к порталу самообслуживания, настроить ssl-сертификаты. И все это будет основой для написания следующей заметки.