Установка 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