суббота, 12 марта 2016 г.

Установка FREERADIUS в CentOS7 и настройка приема аккаутинговой информации от маршрутизатора Cisco VoIP.

Начинаем:
# yum install freeradius freeradius-mysql freeradius-utils
# yum install net-tools
Запускаем, добавляем в автозагрузку и проверяем статус сервиса:
# systemctl start radiusd
# systemctl enable radiusd
# systemctl status radiusd.service
Сервис freeradius должен по умолчанию слушать порты 1812 и 1813. Проверяем:
# netstat -ltupn
udp        0      0 127.0.0.1:18120   0.0.0.0:*        2316/radiusd
udp        0      0 0.0.0.0:1812      0.0.0.0:*        2316/radiusd
udp        0      0 0.0.0.0:1813      0.0.0.0:*        2316/radiusd
udp        0      0 0.0.0.0:33584     0.0.0.0:*        2316/radiusd
udp6       0      0 :::1812           :::*             2316/radiusd
udp6       0      0 :::1813           :::*             2316/radiusd
Порт 1812 прослушивается на предмет обработки авторизационных пакетов, а порт 1813 прослушивается на предмет сбора аккаутинговой информации.

Открываем порты 1812 и 1813 в файле iptables
# nano /etc/sysconfig/iptables
Добавляем в начало:
-A INPUT -p tcp -m tcp --dport 1812 -j ACCEPT
-A INPUT -p udp -m udp --dport 1812 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 1813 -j ACCEPT
-A INPUT -p udp -m udp --dport 1813 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 3306 -j ACCEPT 
Последняя строчка вписывается для того что бы freeradius мог обратиться к базе MySQL по сети.
Рестрартуем iptables
# systemctl restart iptables.service

Применяем утилиту radtest для проверки работы системы авторизации в FREERADIUS. Пытаемся авторизоваться с именем пользователя radius, паролем setupRadius, используя секрет radius-а - testing123 и порт 1812, установленный у сервиса авторизации по умолчанию.
# radtest radius setupRadius localhost 1812 testing123
Sending Access-Request Id 95 from 0.0.0.0:44171 to 127.0.0.1:1812
        User-Name = 'radius'
        User-Password = 'setupRadius'
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 1812
        Message-Authenticator = 0x00
Received Access-Reject Id 95 from 127.0.0.1:1812 to 127.0.0.1:44171 length 20
(0) -: Expected Access-Accept got Access-Reject
Видим, что авторизация не удалась (получен ответ Access-Reject), но служба Freeradius работает.
Создаем пользователя radius с паролем setupRadius в сервисе FreeRadius
# nano /etc/raddb/users
В верхней части файла создаем пользователя вот таким блоком:
radius  Cleartext-Password := "setupRadius"
       Service-Type = Framed-User,
       Framed-Protocol = PPP,
       Framed-IP-Address = 172.16.3.33,
       Framed-IP-Netmask = 255.255.255.0,
       Reply-Message := "Hello, %{User-Name}"
Рестартуем сервис:
# systemctl restart radiusd.service
теперь авторизация проходит успешно:
# radtest radius setupRadius localhost 1812 testing123
Sending Access-Request Id 83 from 0.0.0.0:46052 to 127.0.0.1:1812
        User-Name = 'radius'
        User-Password = 'setupRadius'
        NAS-IP-Address = 127.0.0.1
        NAS-Port = 1812
        Message-Authenticator = 0x00
Received Access-Accept Id 83 from 127.0.0.1:1812 to 127.0.0.1:46052 length 59
        Service-Type = Framed-User
        Framed-Protocol = PPP
        Framed-IP-Address = 172.16.3.33
        Framed-IP-Netmask = 255.255.255.0
        Reply-Message = 'Hello, radius'

Рассказываем серверу Freeradius с какими устройствами ему можно работать
Все разрешенные для общения устройства прописываются в файле /etc/raddb/clients.conf
# nano /etc/raddb/clients.conf
Добавляем в конец файла:
client SMA-Mos4a-R3 {
      ipaddr = 10.200.255.9
      secret = 12345
      shortname = CiscoVoip
}
Этим блоком мы разрешили сервису freeradius работать с ip адресом 10.200.255.9 с использованием radius секрета – 12345
Для применения изменений рестартуем сервис radius
# systemctl restart radiusd.service

Подготавливаем базу данных MySQL, в которую будем записывать аккаутинговую информацию.
# mysql -u root -p
>create database radius;
>use radius;
Создаем основную таблицу аккаутинга с помощью трех последовательных команд:
>CREATE TABLE IF NOT EXISTS `radacct` (
  `radacctid` bigint(21) NOT NULL,
  `acctsessionid` varchar(64) NOT NULL DEFAULT '',
  `acctuniqueid` varchar(32) NOT NULL DEFAULT '',
  `username` varchar(64) NOT NULL DEFAULT '',
  `groupname` varchar(64) NOT NULL DEFAULT '',
  `realm` varchar(64) DEFAULT '',
  `nasipaddress` varchar(15) NOT NULL DEFAULT '',
  `nasportid` varchar(15) DEFAULT NULL,
  `nasporttype` varchar(32) DEFAULT NULL,
  `acctstarttime` datetime DEFAULT NULL,
  `acctupdatetime` datetime DEFAULT NULL,
  `acctstoptime` datetime DEFAULT NULL,
  `acctinterval` int(12) DEFAULT NULL,
  `acctsessiontime` int(12) unsigned DEFAULT NULL,
  `acctauthentic` varchar(32) DEFAULT NULL,
  `connectinfo_start` varchar(50) DEFAULT NULL,
  `connectinfo_stop` varchar(50) DEFAULT NULL,
  `acctinputoctets` bigint(20) DEFAULT NULL,
  `acctoutputoctets` bigint(20) DEFAULT NULL,
  `calledstationid` varchar(50) NOT NULL DEFAULT '',
  `callingstationid` varchar(50) NOT NULL DEFAULT '',
  `acctterminatecause` varchar(32) NOT NULL DEFAULT '',
  `servicetype` varchar(32) DEFAULT NULL,
  `framedprotocol` varchar(32) DEFAULT NULL,
  `framedipaddress` varchar(15) NOT NULL DEFAULT '',
  `h323incomingconfid` varchar(60) NOT NULL,
  `h323remoteaddress` varchar(16) NOT NULL,
  `remotemediaaddress` varchar(16) NOT NULL,
  `h323disconnectcause` varchar(5) NOT NULL,
  `releasesource` varchar(5) NOT NULL,
  `h323callorigin` varchar(64) NOT NULL,
  `gwrxdcdn` varchar(50) NOT NULL,
  `gwrxdcgn` varchar(50) NOT NULL,
  `packettype` varchar(10) NOT NULL,
  `packettypeupdate` varchar(10) NOT NULL,
  `h323setuptime` varchar(50) NOT NULL,
  `h323connecttime` varchar(50) NOT NULL,
  `h323disconnecttime` varchar(50) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
>ALTER TABLE `radacct`
  ADD PRIMARY KEY (`radacctid`),
  ADD KEY `acctsessionid` (`acctsessionid`),
  ADD KEY `acctsessiontime` (`acctsessiontime`),
  ADD KEY `acctstarttime` (`acctstarttime`),
  ADD KEY `calledstationid` (`calledstationid`),
  ADD KEY `callingstationid` (`callingstationid`),
  ADD KEY `h323disconnectcause` (`h323disconnectcause`),
  ADD KEY `h323incomingconfid` (`h323incomingconfid`);
>ALTER TABLE `radacct`
  MODIFY `radacctid` bigint(21) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=1;
Создаем пользователя radius с паролем radpass для работы с базой данных radius и наделяем его правами для работы с базой radius:
>create user 'radius'@'localhost' identified by 'radpass';
>grant all privileges on radius.* to 'radius'@'localhost';

Подключаем модуль SQL в Freeradius
Подключаем модуль SQL глобально
# ln -s /etc/raddb/mods-available/sql /etc/raddb/mods-enabled/sql
В настройках файла default разрешаем работу с SQL
# nano /etc/raddb/sites-enabled/default 
В секции accounting {…} раскомментируем модуль sql (или удаляем знак минус перед словом sql). Должно получиться:
    #  See "Accounting queries" in sql.conf
    sql
В модуле SQL задаем параметры для доступа к базе данных
# nano /etc/raddb/mods-enabled/sql
sql {

       driver = "rlm_sql_mysql" (по умолчанию здесь rlm_sql_null)
...
        dialect = "mysql" (по умолчанию здесь sqlite)

       server = "localhost" (раскомментируем строку)
       port = 3306 (раскомментируем строку)
       login = "radius"(раскомментируем строку)
       password = "radpass" (раскомментируем строку)

        radius_db = "radius" (проверяем)

        acct_table1 = "radacct" (проверяем)
        acct_table2 = "radacct" (проверяем)
...
}
Для применения изменений рестартуем сервис radius
# systemctl restart radiusd.service

Учим FREERADIUS работать с атрибутами Cisco AV-Pair:
Открываем файл /etc/raddb/mods-available/preprocess 
# nano /etc/raddb/mods-available/preprocess
устанавливаем директиву with_cisco_vsa_hack в yes (по умолчанию no)
with_cisco_vsa_hack = yes
Для применения изменений рестартуем сервис radius
# systemctl restart radiusd.service

Определяем запросы по вставке информации в БД MySQL
Резервируем оригинальный файл запросов:
# cp  /etc/raddb/mods-config/sql/main/mysql/queries.conf /etc/raddb/mods-config/sql/main/mysql/queries.conf.original
Редактируем файл запросов:
# nano /etc/raddb/mods-config/sql/main/mysql/queries.conf
Раздел Accounting and Post-Auth Queries приводим к виду:
#######################################################################
# Accounting and Post-Auth Queries
#######################################################################
# These queries insert/update accounting and authentication records.
# The query to use is determined by the value of 'reference'.
# This value is used as a configuration path and should resolve to one
# or more 'query's. If reference points to multiple queries, and a query
# fails, the next query is executed.
#
# Behaviour is identical to the old 1.x/2.x module, except we can now
# fail between N queries, and query selection can be based on any
# combination of attributes, or custom 'Acct-Status-Type' values.
#######################################################################
accounting {
        reference = "%{tolower:type.%{Acct-Status-Type}.query}"

        # Write SQL queries to a logfile. This is potentially useful for bulk inserts
        # when used with the rlm_sql_null driver.
#       logfile = ${logdir}/accounting.sql

        column_list = "\
                acctsessionid,          acctuniqueid,           username, \
                realm,                  nasipaddress,           nasportid, \
                nasporttype,            acctstarttime,          acctupdatetime, \
                acctstoptime,           acctsessiontime,        acctauthentic, \
                connectinfo_start,      connectinfo_stop,       acctinputoctets, \
                acctoutputoctets,       calledstationid,        callingstationid, \
                acctterminatecause,     servicetype,            framedprotocol, \
                framedipaddress, \
                h323incomingconfid, h323remoteaddress, remotemediaaddress, h323disconnectcause, \
                releasesource, h323callorigin, gwrxdcdn, gwrxdcgn, packettype, \
                h323setuptime, h323connecttime, h323disconnecttime"

        type {
                accounting-on {
                        #
                        #  Bulk terminate all sessions associated with a given NAS
                        #
                        query = "\
                                UPDATE ${....acct_table1} \
                                SET \
                                        acctstoptime = FROM_UNIXTIME(\
                                                %{integer:Event-Timestamp}), \
                                        acctsessiontime = '%{integer:Event-Timestamp}' \
                                                - UNIX_TIMESTAMP(acctstarttime), \
                                        acctterminatecause = '%{%{Acct-Terminate-Cause}:-NAS-Reboot}' \
                                WHERE acctstoptime IS NULL \
                                AND nasipaddress   = '%{NAS-IP-Address}' \
                                AND acctstarttime <= FROM_UNIXTIME(\
                                        %{integer:Event-Timestamp})"
                }

                accounting-off {
                        query = "${..accounting-on.query}"
                }

                start {
                        #
                        #  Insert a new record into the sessions table
                        #
                        query = "\
                                INSERT INTO ${....acct_table1} \
                                        (${...column_list}) \
                                VALUES \
                                        ('%{Acct-Session-Id}', \
                                        '%{Acct-Unique-Session-Id}', \
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
                                        '%{NAS-Port}', \
                                        '%{NAS-Port-Type}', \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        NULL, \
                                        '0', \
                                        '%{Acct-Authentic}', \
                                        '%{Connect-Info}', \
                                        '', \
                                        '0', \
                                        '0', \
                                        '%{Called-Station-Id}', \
                                        '%{Calling-Station-Id}', \
                                        '', \
                                        '%{Service-Type}', \
                                        '%{Framed-Protocol}', \
                                        '%{Framed-IP-Address}', \
                                        '%{h323-incoming-conf-id}', \
                                        '%{h323-remote-address}', \
                                        '%{remote-media-address}', \
                                        '%{h323-disconnect-cause}', \
                                        '%{release-source}', \
                                        '%{h323-call-origin}', \
                                        SUBSTRING_INDEX('%{gw-rxd-cdn}', ':', -1), \
                                        SUBSTRING_INDEX('%{gw-rxd-cgn}', ':', -1), \
                                        'start', \
                                        '%{h323-setup-time}', \
                                        '%{h323-connect-time}', \
                                        '%{h323-disconnect-time}' \
                                        )"

                        #
                        #  Key constraints prevented us from inserting a new session,
                        #  use the alternate query to update an existing session.
                        #
                        ##query = "\
                        ##      UPDATE ${....acct_table1} SET \
                        ##              acctstarttime   = FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                        ##              acctupdatetime  = FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                        ##              connectinfo_start = '%{Connect-Info}' \
                        ##      WHERE acctsessionid = '%{Acct-Session-Id}' \
                        ##      AND username            = '%{SQL-User-Name}' \
                        ##      AND nasipaddress        = '%{NAS-IP-Address}'"
                }

                interim-update {
                        query = "SELECT radacctid FROM radacct WHERE radacctid=0 LIMIT 1"
                        ##Просто так, что бы сервер что-то выполнял при приходе interim-update
                }

                stop {
                        #
                        #  Session has terminated, update the stop time and statistics.
                        #
                        query = "\
                                UPDATE ${....acct_table2} SET \
                                        acctstoptime    = FROM_UNIXTIME(\
                                                %{integer:Event-Timestamp}), \
                                        acctsessiontime = '%{Acct-Session-Time}', \
                                        acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
                                                << 32 | '%{%{Acct-Input-Octets}:-0}', \
                                        acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
                                                << 32 | '%{%{Acct-Output-Octets}:-0}', \
                                        acctterminatecause = '%{Acct-Terminate-Cause}', \
                                        connectinfo_stop = '%{Connect-Info}', \
                                        h323remoteaddress = '%{h323-remote-address}', \
                                        remotemediaaddress = '%{remote-media-address}', \
                                        h323disconnectcause = '%{h323-disconnect-cause}', \
                                        releasesource = '%{release-source}', \
                                        gwrxdcgn = SUBSTRING_INDEX('%{gw-rxd-cgn}', ':', -1), \
                                        gwrxdcdn = SUBSTRING_INDEX('%{gw-rxd-cdn}', ':', -1), \
                                        packettypeupdate = 'update', \
                                        h323setuptime = '%{h323-setup-time}', \
                                        h323connecttime = '%{h323-connect-time}', \
                                        h323disconnecttime = '%{h323-disconnect-time}' \
                                WHERE h323incomingconfid        = '%{h323-incoming-conf-id}' \
                                AND h323callorigin      = '%{h323-call-origin}' \
                                AND acctsessionid = '%{Acct-Session-Id}' "

                        #
                        #  The update condition matched no existing sessions. Use
                        #  the values provided in the update to create a new session.
                        #
                        query = "\
                                INSERT INTO ${....acct_table2} \
                                        (${...column_list}) \
                                VALUES \
                                        ('%{Acct-Session-Id}', \
                                        '%{Acct-Unique-Session-Id}', \
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
                                        '%{NAS-Port}', \
                                        '%{NAS-Port-Type}', \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp} - \
                                                %{%{Acct-Session-Time}:-0}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        '%{Acct-Session-Time}', \
                                        '%{Acct-Authentic}', '', \
                                        '%{Connect-Info}', \
                                        '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
                                                '%{%{Acct-Input-Octets}:-0}', \
                                        '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
                                                '%{%{Acct-Output-Octets}:-0}', \
                                        '%{Called-Station-Id}', \
                                        '%{Calling-Station-Id}', \
                                        '%{Acct-Terminate-Cause}', \
                                        '%{Service-Type}', \
                                        '%{Framed-Protocol}', \
                                        '%{Framed-IP-Address}', \
                                        '%{h323-incoming-conf-id}', \
                                        '%{h323-remote-address}', \
                                        '%{remote-media-address}', \
                                        '%{h323-disconnect-cause}', \
                                        '%{release-source}', \
                                        '%{h323-call-origin}', \
                                        SUBSTRING_INDEX('%{gw-rxd-cdn}', ':', -1), \
                                        SUBSTRING_INDEX('%{gw-rxd-cgn}', ':', -1), \
                                        'stop', \
                                        '%{h323-setup-time}', \
                                        '%{h323-connect-time}', \
                                        '%{h323-disconnect-time}' \
                                        )"
                }
        }
}
Для применения изменений рестартуем сервис radius
# systemctl restart radiusd.service

Настраиваем маршрутизатор Cisco для отправки аккаутинговой информации на наш сервер.
1) Включаем возможность аккаутинга глобально
(config)# aaa new-model
2) Добавляем Radius-сервер:
(config)# radius-server host 10.200.16.200 auth-port 1812 acct-port 1813 timeout 35 retransmit 5 key 12345
Здесь 10.200.16.200 – IP нашего настраиваемого сервера
12345 – radius секрет
При вводе команды может выйти предупреждение:
Warning: This CLI will be deprecated soon. Please move to radius server <name> CLI.
На предупреждение не обращаем внимание. Cisco просто нам предлагает использовать новый синтаксис. А нам пофигу, нам и так нраится.
3) Прописываем параметры radius-серверов:
(config)# radius-server vsa send cisco-nas-port
(config)# radius-server vsa send accounting
(config)# radius-server attribute 31 send nas-port-detail
(config)# radius-server deadtime 5
Последняя команда (radius-server deadtime 5) означает, что radius сервер может быть не доступен 5 минут. По умолчанию – 0. Если оставить значение по умолчанию, то в логах будут появляться записи типа
%RADIUS-4-RADIUS_DEAD: RADIUS server 10.200.16.200:1812,1813 is not responding.
%RADIUS-4-RADIUS_ALIVE: RADIUS server 10.200.16.200:1812,1813 is being marked alive.
4) Добавляем новую группу серверов с именем VOIPACCAUNT
Включаем в эту группу только один наш сервер 10.200.16.200
(config)# aaa group server radius VOIPACCAUNT
(config-sg-radius)# server 10.200.16.200 auth-port 1812 acct-port 1813
(config-sg-radius)# exit
Теоритически в группе может быть несколько записей, но если первый в списке сервер доступен, лог аккаутинга будет валиться только на него.
5) Прописываем параметры аккаутинга для созданной группы
(config)# aaa accounting delay-start
(config)# aaa accounting update periodic 1
(config)# aaa accounting network default start-stop group VOIPACCAUNT
(config)# aaa accounting connection default start-stop group VOIPACCAUNT
(config)# aaa accounting connection h323 start-stop group VOIPACCAUNT
(config)# aaa accounting connection sip start-stop group VOIPACCAUNT
6) Задаем глобально параметры NAS серве6а
(config)# aaa nas port voip
(config)# aaa session-id common
7) Для того что бы маршрутизатор логировал все попытки соединения в блоке gw-accounting aaa не должно быть команды suppress pots rotary
(config)# gw-accounting aaa
(config-gw-accounting-aaa)# no suppress pots rotary

1 комментарий: