Information Security
10 November 2015

LinOTP+RADIUS. Аутентификация с помощью одноразовых паролей

From Sandbox

1. Основные сведения


В данной инструкции описывается процесс интеграции LinOTP и FreeRadius на машинах под управлением CentOS а также настройка аутентификации пользователей SSH по ОТР, сгенерированному с помощью программного обеспечения Google Authenticator (или любого, использующий аналогичный алгоритм).

2. Исходные данные


Данная инструкция подразумевает, что у нас имеются в наличии три машины под управлением ОС CentOS 6.7. Они находятся в одной подсети и между ними настроена сетевая связность. Имена и адреса машин приведены в таблице ниже.

Hostname     IP адрес       Условное наименование          Операционная система
linotp       10.1.0.114     Сервер LinOTP                   CentOS 6.7
radius-p     10.1.0.122     Сервер RADIUS                   CentOS 6.7
client       10.1.0.113     Клиент                          CentOS 6.7
admin-PC     10.1.0.120     Рабочая станция (АРМ)            Windows 7


На Сервере LinOTP развернуто ПО linotp по инструкции с оффициального сайта (документ “LinOTP Server Installation”). Версия ПО linotp 2.7.2.

На Сервере RADIUS развернуто ПО freeradius сервер. Подробно описывать установку пакетов я не буду, т.к. она выполняется тривиально с помощью менеджера yum. Полный перечень необходимых для установки пакетов приведен ниже:

  • freeradius-2.2.6-6.el6_7.i686;
  • freeradius-perl-2.2.6-6.el6_7.i686;
  • perl-devel-5.10.1-141.el6.i686;
  • perl-Pod-Escapes-1.04-141.el6.i686;
  • perl-version-0.77-141.el6.i686;
  • perl-Test-Harness-3.17-141.el6.i686;
  • perl-ExtUtils-ParseXS-2.2003.0-141.el6.i686;
  • perl-Digest-SHA-5.47-141.el6.i686;
  • perl-libs-5.10.1-141.el6.i686;
  • perl-Module-Pluggable-3.90-141.el6.i686;
  • perl-5.10.1-141.el6.i686;
  • perl-ExtUtils-MakeMaker-6.55-141.el6.i686;
  • perl-DBI-1.609-4.el6.i686;
  • perl-CPAN-1.9402-141.el6.i686;
  • perl-Pod-Simple-3.13-141.el6.i686.

Машина Клиент является клиентом RADIUS, на котором будет происходить аутентификация по ОТР. Для поддержки RADIUS аутентификации на машине установлен пакет pam_radius-1.3.17-2.el5.
Машина АРМ под управлением Windows имитирует рабочую станцию администратора, который будет получать доступ к машине Клиент по SSH с использованием ОТР. На данной машине стоит Windows 7, клиент SSH putty и программное обеспечение GAUTH – генератор ОТР.

3. Что мы получим в итоге


Целью данной работы является настройка аутентификации на Клиенте через Сервер RADIUS при помощи ОТР (генерящегося на Сервере LinOTP).

Сервер RADIUS в данной конфигурации не хранит в своей базе данных учетные записи пользователей – эта задача возложена на Сервер LinOTP. Сервер RADIUS хранит базу NAS, т.е. перечень серверов и сетевого оборудования, пользователей которого он аутентифицирует. В данной конфигурации для этого используется текстовый файл конфигурации /etc/raddb/clients.conf.

Сервер LinOTP в данной конфигурации служит для управления токенами пользователей. Информация о пользователях и их токенах хранится в базе данных MySQL, развернутой также на сервере LinOTP. Само по себе ПО linotp не располагает функционалом управления УЗ пользователей, таким образом добавлять пользователей и их данные в таблицу MySQL нужно вручную.

4. Этапы настройки


4.1. Порядок настройки RADIUS


Настройки, описанные в данном разделе производятся на Сервере RADIUS, т.е. на машине radius-p.

4.1.1. Установка дополнительных модулей Perl



# perl -MCPAN -e shell
cpan[1]> install LWP
cpan[1]> install LWP:UserAgent
cpan[1]> install LWP:UserAgent:Determined
cpan[1]> install LWP::Protocol::https

Некоторые из устанавливаемых модулей могут выдать при установке ошибку следующего вида:

Running make install
  make test had returned bad status, won't install without force
Failed during this command:
 MSCHILLI/LWP-Protocol-https-6.06.tar.gz      : make_test NO


В таком случае следует применить команду «force install», например:

cpan[1]> force install LWP::Protocol::https

Об успешной установке будет говорить сообщение вида:

Failed during this command:
 MSCHILLI/LWP-Protocol-https-6.06.tar.gz      : make_test FAILED but failure ignored because 'force' in effect

4.1.2. Настройка Radius


Добавить NAS клиента radius в файле /etc/raddb/clients.conf. В нашем случае это:

client 10.1.0.113 {
        secret          = testing123
        shortname       = client
}


В файл /etc/raddb/users добавьте строку
DEFAULT Auth-type := perl

*** Однако, это не самый лучший вариант. В данном случае вы принудительно устанавливаете тип аутентификации, что лишает radius возможности одновременно с пользователями, аутентифицируемыми через linOTP аутентифицировать пользователей, учетные записи которых, например, хранятся в mysql (и сделаны соответствующие настройки в RADIUS).
Я на практике как раз столкнулся с такой ситуацией. В этом случае данную директиву (DEFAULT Auth-type := perl) не надо прописывать, а вместо этого в файле /etc/raddb/sites-enabled/default в секции authorize добавил условие:
if (Sql-Group == sql) {pap}
         else {update control {
                                Auth-Type := perl
                                }
                }

Это условие (написанное на встроенном в radius языке unlang) включает тип аутентификации «perl» только для тех пользователей, которые не входят в группу пользователей, аутентифицируемых через sql.


В файле /etc/freeradius/modules/perl модифицируйте следующую директиву:

perl {
         module = <путь к файлу linotp_radius.pm>


В файле /etc/freeradius/sites-enabled/default модифицируйте раздел “authenticate” следующим образом:

authenticate{
perl


4.1.3. Создание файла – коннектора linotp_radius.pm


Создайте файл linotp_radius.pm следующего содержания:

#________START______
use strict;
use LWP;
use LWP::UserAgent::Determined;

# use ...
# This is very important ! Without this script will not get the filled  hashesh from main.
use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK $URL);
use Data::Dumper;

$ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0;
$URL = "https://<IP адрес сервера LinOTP>/validate/simplecheck";

# This is hash wich hold original request from radius
#my %RAD_REQUEST;
# In this hash you add values that will be returned to NAS.
#my %RAD_REPLY;
#This is for check items
#my %RAD_CHECK;

#
# This the remapping of return values
#
       use constant    RLM_MODULE_REJECT=>    0;#  /* immediately reject the request */
       use constant    RLM_MODULE_FAIL=>      1;#  /* module failed, don't reply */
       use constant    RLM_MODULE_OK=>        2;#  /* the module is OK, continue */
       use constant    RLM_MODULE_HANDLED=>   3;#  /* the module handled the request, so stop. */
       use constant    RLM_MODULE_INVALID=>   4;#  /* the module considers the request invalid. */
       use constant    RLM_MODULE_USERLOCK=>  5;#  /* reject the request (user is locked out) */
       use constant    RLM_MODULE_NOTFOUND=>  6;#  /* user not found */
       use constant    RLM_MODULE_NOOP=>      7;#  /* module succeeded without doing anything */
       use constant    RLM_MODULE_UPDATED=>   8;#  /* OK (pairs modified) */
       use constant    RLM_MODULE_NUMCODES=>  9;#  /* How many return codes there are */

# Function to handle authorize
sub authorize {
       # For debugging purposes only
#       &log_request_attributes;

       # Here's where your authorization code comes
       # You can call another function from here:
       &test_call;

       return RLM_MODULE_OK;
}

# Function to handle authenticate
sub authenticate {
       # For debugging purposes only
#       &log_request_attributes;

    
    my $ua = LWP::UserAgent->new();
    my $req = HTTP::Request->new(GET => $URL . "?user=" .
        $RAD_REQUEST{'User-Name'} . "&pass=" . 
        $RAD_REQUEST{'User-Password'} );
    my $response = $ua->request( $req );

    die "Error at $URL\n ", $response->status_line, "\n Aborting"
      unless $response->is_success;
      
    if($response->content =~ m/:\-\)/i) {
               return RLM_MODULE_OK;
      } else {
        $RAD_REPLY{'Reply-Message'} = "LinOTP server denied access!";
               return RLM_MODULE_REJECT;
    }
}

# Function to handle preacct
sub preacct {
       # For debugging purposes only
#       &log_request_attributes;

       return RLM_MODULE_OK;
}

# Function to handle accounting
sub accounting {
       # For debugging purposes only
#       &log_request_attributes;

       # You can call another subroutine from here
       &test_call;

       return RLM_MODULE_OK;
}

# Function to handle checksimul
sub checksimul {
       # For debugging purposes only
#       &log_request_attributes;

       return RLM_MODULE_OK;
}

# Function to handle pre_proxy
sub pre_proxy {
       # For debugging purposes only
#       &log_request_attributes;

       return RLM_MODULE_OK;
}

# Function to handle post_proxy
sub post_proxy {
       # For debugging purposes only
#       &log_request_attributes;

       return RLM_MODULE_OK;
}

# Function to handle post_auth
sub post_auth {
       # For debugging purposes only
#       &log_request_attributes;

       return RLM_MODULE_OK;
}

# Function to handle xlat
sub xlat {
       # For debugging purposes only
#       &log_request_attributes;

       # Loads some external perl and evaluate it
       my ($filename,$a,$b,$c,$d) = @_;
       &radiusd::radlog(1, "From xlat $filename ");
       &radiusd::radlog(1,"From xlat $a $b $c $d ");
       local *FH;
       open FH, $filename or die "open '$filename' $!";
       local($/) = undef;
       my $sub = <FH>;
       close FH;
       my $eval = qq{ sub handler{ $sub;} };
       eval $eval;
       eval {main->handler;};
}

# Function to handle detach
sub detach {
       # For debugging purposes only
#       &log_request_attributes;

       # Do some logging.
       &radiusd::radlog(0,"rlm_perl::Detaching. Reloading. Done.");
} 

#
# Some functions that can be called from other functions
#

sub test_call {
       # Some code goes here
}

sub log_request_attributes {
       # This shouldn't be done in production environments!
       # This is only meant for debugging!
       for (keys %RAD_REQUEST) {
               &radiusd::radlog(1, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
       }
}

1;
#___________END

Данный файл был помещен в папку /etc/raddb/, однако можно разместить его и в другом месте, главное помнить, что путь к этому файлу должен быть прописан в /etc/freeradius/modules/perl (упоминалось выше).

На данном этапе radius должен успешно стартовать командой /usr/sbin/radiusd –X и при попытке подключиться к Клиенту radius отправлять запрос на сервер LinOTP. Это можно проверить, подключившись к Клиенту по ssh и одновременно прослушивая трафик на сервере LinOTP с помощью TCPDUMP. Вы должны увидеть пакеты по порту 443 ТСР.

4.2. Порядок настройки LinOTP


LinOTP настраивался согласно официальной документации разработчика (см. документ LinOTP – QuickStart Guide).

LinOTP сам по себе не располагает функционалом для создания пользователей, он способен только читать информацию об УЗ (учетных записях) пользователей из LDAP, MySQL или текстовых файлов. Данная инструкция подразумевает, что в качестве базы УЗ мы используем MySQL, развернутый на том же сервер, что и LinOTP.

4.2.1. Формирование базы пользовательских УЗ


При создании useridresolver (это как раз указание, своего рода «коннектор» к базе УЗ, откуда LinOTP будет брать данные о пользователях) во время первоначальной инициализации сервера LinOTP нужно указать в маппинге таблицы SQL с пользовательскими данными поле для хранения пароля “password”. Этот пароль нужен только для доступа пользователя к порталу self service. Если этим порталом пользоваться не планируем, то это можно не делать.

Если на этапе инициализации это сделано не было, то можно уже позднее добавить соотв. колонку в таблицу. Пример:
ALTER TABLE usertable ADD password VARCHAR(200);

Далее генерим хеш пароля для пользователя командой:

/opt/LINOTP/bin/linotp-create-sqlidresolver-user -u <имя пользователя> -i <его поле id в базе> -p <желаемый пароль>


На выходе получаем строку вида:

user7;2;{SSHA512}3QWyB5u2TkPLDc2mjIJxtcCLOVHSjLS/MyYjPGNIENchm5riFHF3M9CW7csaxADAFml8WkH/Whd1A047nUAaeWk2dFZod1ZhYzN3cTRNUkpRdlhrLmVsdFVSeUNIaUdWVEZVT1ZycW5BSlVXajVHbHVvckhHSmNYOWp5M3FQLi8=;


Все, что выделено желтым – это полученный хеш пароля. Далее добавляем его в соответствующее поле таблицы:

update usertable set password = <хеш, полученный с помощью> where user = 'user7' ;

В моем примере конфигурация useridresolver выглядит так:


Рисунок 1 — Пример конфигурации useridresolver

Особое внимание стоит обратить на поле «Attribute mapping», которое на рисунке не видно полностью. Вот его содержимое:

{ "userid" : "id", "username": "user",  "email" : "mail", "surname" : "sn", "givenname" : "givenName", "password" : "password"}

А ниже приведена таблица MySQL (находящаяся на сервере LinOTP), в которой хранятся УЗ пользователей и с которой настраивается маппинг:

mysql> select * from usertable;
+----+-------+------------------------+--------+-----------+-----------+
| id | user  | mail                   | sn     | givenName |password  |
+----+-------+------------------------+--------+-----------+-----------+
|  1 | user6 | user6@localdomain.mail | ivan   | petrov    |<   хэш   >|
|  2 | user7 | user7@localdomain.mail | sergey | fedorov   |<   хэш    >|
+----+-------+------------------------+--------+-----------+-----------+


Полагаю, дополнительные комментарии излишни. Поле «userid» в useridresolver мапится в поле «id» в БД MySQL. Поле «username» в useridresolver — в поле «user» в БД MySQL и т.д.
И вот, в итоге, что мы видим в интерфейсе LinOTP, перейдя во вкладку «User view»:


Рисунок 2 — Отображение пользователей в интерфейсе LinOTP

4.2.2. Настройка политик LinOTP


Политики являются ключевым элементом LinOTP и позволяют гибко настраивать варианты аутентификации, типы и ограничения по кол-ву токенов и т.п.

Для демонстрации работы LinOTP будет достаточно следующего набора политик:

image
Рисунок 3 — Политики назначения токенов

Данная политика определяет максимальное кол-во токенов, которые могут быть выпущены для пользователя – параметр “maxtoken”.

image
Рисунок 4 — Политики портала самообслуживания

Данная политика регламентирует типы токенов и операции над ними, которые пользователи могут самостоятельно выполнять через портал самообслуживания.

image
Рисунок 5 – Системные политики

Необходимое разрешение на чтение и модификацию системных настроек администратором (имеет смысл задавать дополнительные настройки в scope=system для ограничения администраторов).

image
Рисунок 6 – Политики аутентификации

Данная политика требует ввод ОТР-PIN и OTP при аутентификации на целевой системе(Клиенте). За это отвечает параметр otppin=0. При установке его в значение «2» аутентификация будет проходить только по OTP. При установке его в значение «1» аутентификация будет проходить по комбинации Пароль пользователя+OTP (пароль тот же самый, что и для доступа на портал самообслуживания).

Пример: параметр otppin=0; пользователь user3 имеет токен с OTP-PIN = 123456 и сгенеренным ОТР=234567. В таком случае пароль пользователя user3 при доступе по ssh к Клиенту будет иметь вид <OTP-PIN> т.е. 123456234567.

4.2.3. Создание токенов для пользователей


После того, как LinOTP подключен к базе пользовательских УЗ (настроен useridresolver) и создан минимальный свод политик можно переходить к созданию токенов для пользователей.
Перейдите во вкладку «User view», выберите пользователя для которого необходимо создать токен. В меню слева нажмите кнопку «Enroll». Перед вами откроется окно с настройками нового токена.


Рисунок 8 – Создание токена

Token type – тип токена. Поддерживается множество разных токенов. Для поддержки google authenticator (который будем использовать в этом примере) выберите “HMAC time based”. Этот тип токена будет генерировать одноразовые пароли (ОТР), привязанные к текущему времени (иногда называются ТОТР). Отсюда следует требование синхронизации времени на всех наших машинах – Сервере RADIUS, Сервере LinOTP и Клиенте.
Token Seed – можно попросить сгенерить случайную последовательность (что и было сделано в данном примере) – «generate random seed» или задать свою «enter seed».
Token settings – для поддержки Google authenticator просто поставьте галочку «Google authenticator compliant», в противном случае вы сможете самостоятельно задать длину генерируемого токеном ОТР — «OTP digits», используемый алгоритм — «Hash algorithm», временное окно в котором будет действовать данный ОТР – «Time step».
Произвольное описание токена – «Description» — можно вообще оставить пустым, оно ни на что не влияет.
Token Pin – пин-код данного токена. Не менее 6 цифр для Google authenticator.
После ввода всех необходимых данных нажмите кнопку «Enroll». Токен создан.

Перед вами появится окно с информацией о токене: QR-код токена для считывания приложением Google Authenticator, установленном на мобильном устройстве и строкой, содержащей секрет, необходимый, например, для импорта токена в программный клиент GAUTH работающий под Windows.
Скопируйте строку вида «otpauth://totp/TOTP….» в нижней части окна и сохраните. Она понадобится на этапе настройки клиента ОТР на Windows.


Рисунок 9 – Созданный токен

После этого окно можно закрыть. Данный токен появится в списке токенов на вкладке «Token view» в интерфейсе LinOTP.

4.3. Порядок настройки Клиента


4.3.1. Установка необходимого ПО


Для поддержки Клиентом аутентификации через RADIUS необходимо на Клиенте (10.1.0.113 в примере) установить пакет libpam-radius-auth. Выполняется это также с помощью менеджера yum.

4.3.2.Настройка аутентификации Клиента через RADIUS


Необходимо настроить аутентификацию через radius сервер. Для этого отредактировать строку «other-server other-secret 3» в файле /etc/pam_radius_auth.conf. Заменить «other server» на IP адрес Сервера RADIUS, т.е. 10.1.0.122, а «other-secret» на секрет, заданный в разделе 4.1.2, т.е. «testing123». В итоге получится следующее:

10.1.0.122      testing123             3

Отредактируйте файл /etc/pam.d/sshd. Добавьте в него строку:

sufficient     /lib/security/pam_radius_auth.so

над строками

# Standard Un*x authentication.
@include common-auth

Также необходимо создать на Клиенте учетную запись пользователя с тем же именем, что и пользователь на LinOTP для которого создавался токен. В данном примере – user7.

useradd –m –d /home/user7 user7


4.3.3. Порядок настройки АРМ


На АРМ нужно установить генератор ОТР, например GAUTH. Добыть данное ПО можно здесь. Устанавливается из файла-инсталлятора, содержащегося в скачанном архиве. Процесс установки тривиален.
После установки рекомендуется поправить ключ в реестре как описывается ниже. Без этой правки софт работать будет, но при запуске будет выдавать ошибку, что не радует глаз. Это баг GAUTH, описанный в FAQ по данному ПО.
Необходимо добавить параметр типа String с именем key в следующую ветку:
HKEY_CURRENT_USER\Software\GoogleAuth\GoogleAuth\1.0.0.
После этого запускаем клиента GAUTH через ПУСК – программы – Google Auth. В трее появится соответствующая иконка. Нажимаем по ней правой клавишей мышки и выбираем «Modify key». В открывшееся окно вставляем секрет из сохраненной на этапе создания токена строки.
Пример строки: otpauth://totp/TOTP00013B2A?secret=APY*****JH&counter=0
В данном случае секрет, который необходимо вставить будет:
APY*****JH
Нажимаете «Save key».


Рисунок 10 – Импорт секрета в GAUTH

Теперь при наведении мышки на иконку в трее будет появляться шестизначный ОТР и в скобках возле него время, которое данный ОТР будет валидным.


Рисунок 11 – Сгенеренный ОТР

5. Проверка работы аутентификации с использованием ОТР


Запускаем на АРМ клиента SSH “putty”. Подключаемся к Клиенту (в примере — 10.1.0.113). Указываем имя пользователя, для которого создан токен (в примере – user7), в качестве пароля вводим <OTP-PIN> без пробелов и тире, одним словом (например, 123456665494, где 123456 — это PIN, а 665494 — сгенеренный ОТР).
Если все сделано правильно, часы на всех серверах и АРМ синхронизированы, то доступ пользователя user7 должен быть авторизован.

+7
12.9k 73
Leave a comment
Top of the day