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

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


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

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


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

Hostname     IP адрес       Условное наименование
linotp       10.1.0.114     Сервер LinOTP
radius-p     10.1.0.122     Сервер RADIUS
client       10.1.0.113     Клиент

На Сервере 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.

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/freeradius/users добавьте строку DEFAULT Auth-type := perl.

В файле /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 или текстовых файлов.

4.2.1. Формирование базы пользовательских УЗ
При создании useridresolver во время первоначальной инициализации сервера 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' ;

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

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

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

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

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

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

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

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

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

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

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

Настройку Клиента, т.е. сервер на котором пользователи будут аутентифицироваться с помощью OTP я рассмотрю в следующей статье. Надеюсь, кому-то изложенный материал придется полезным.

© Habrahabr.ru