FreeRadius Notes
Это просто сборник ссылок и заметок
- https://shop.nag.ru/article/ericsson-smartedge-freeradius-billing
- https://code.google.com/archive/p/cakebilling/wikis/ConfiguringFreeRadius.wiki
Минимальный рабочий конфиг
Тут чертовски важен порядок модулей - если переставить местами pap/files то получится что pap не сможет получить пароль
sites-enabled/default
server default { listen { type = auth ipv4addr = * port = 1812 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipv4addr = * port = 1813 type = acct } instantiate { exec expr expiration } authorize { files pap } authenticate { Auth-Type PAP { pap } } } # end of SERVER
Клиенты clients.conf
client localhost { ipaddr = 127.0.0.1 proto = * secret = secret require_message_authenticator = no limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } }
Модуль PAP mods-enabled/pap
pap { # normalise = yes auto_header = no }
Модуль files
mods-enabled/files
files { moddir = ${modconfdir}/${.:instance} #key = "%{%{Stripped-User-Name}:-%{User-Name}}" filename = ${moddir}/authorize usersfile = ${moddir}/authorize # acctusersfile = ${moddir}/accounting }
mods-config/files/authorize
bob Cleartext-Password := "hello" Reply-Message := "Hello, %{User-Name}"
Проверка работы минимальной конфигурации
- Для проверки использую команду
radtest bob hello localhost 0 secret
bob
- имя пользователя, атрибутUser-Name
hello
- пароль, который будет передан в атрибутеUser-Password
localhost
- адрес куда отправить запрос0
- номер (виртуального) порта,NAS-Port
secret
- radius shared secret, который указан в настройках сервера и клиента
По сути тут логика такая
- "Пришел" пользователь
User-Name = "bob"
User-Password = "hello"
- Его проверили по файлу (модуль
files
ищет пользователя в файле, имя файла определено конфигурацией модуля) и находит, о чем говорит записьfiles: users: Matched entry bob at line 4
- Далее для модулей вызываных ниже по конфигу будут доступны атрибуты которые добавил модуль
files
а именно:
bob Cleartext-Password := "hello" Reply-Message := "Hello, %{User-Name}"
- При этом модуль
files
по сути не делает проверок - он только сопоставляет запрос, и по ключу который определен в конфигурации (key = "%{%{Stripped-User-Name}:-%{User-Name}}"
) находит (или не находит) атрибуты.
Запись в логах вида:
[files] = ok
Означает что такой пользователь существует, НО не означает что он успешно авторизован.
- Далее модуль PAP осуществляет проверку пароля. Важно то, что пароль в описании пользователя должен содержаться в атрибутах "known good", в нашем случае -
Cleartext-Password
- Собственно на этом все.
Fri Jul 14 18:07:17 2023 : Debug: (0) Received Access-Request Id 51 from 127.0.0.1:54599 to 127.0.0.1:1812 length 73 Fri Jul 14 18:07:17 2023 : Debug: (0) User-Name = "bob" Fri Jul 14 18:07:17 2023 : Debug: (0) User-Password = "hello" Fri Jul 14 18:07:17 2023 : Debug: (0) NAS-IP-Address = 10.90.1.213 Fri Jul 14 18:07:17 2023 : Debug: (0) NAS-Port = 0 Fri Jul 14 18:07:17 2023 : Debug: (0) Message-Authenticator = 0xa051e8612e62faaa98baa723ceb98219 Fri Jul 14 18:07:17 2023 : Debug: (0) session-state: No State attribute Fri Jul 14 18:07:17 2023 : Debug: (0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default Fri Jul 14 18:07:17 2023 : Debug: (0) authorize { Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authorize]: calling files (rlm_files) Fri Jul 14 18:07:17 2023 : Debug: (0) files: users: Matched entry bob at line 4 Fri Jul 14 18:07:17 2023 : Debug: (0) files: ::: FROM 1 TO 0 MAX 1 Fri Jul 14 18:07:17 2023 : Debug: (0) files: ::: Examining Reply-Message Fri Jul 14 18:07:17 2023 : Debug: Hello, %{User-Name} Fri Jul 14 18:07:17 2023 : Debug: Parsed xlat tree: Fri Jul 14 18:07:17 2023 : Debug: literal --> Hello, Fri Jul 14 18:07:17 2023 : Debug: attribute --> User-Name Fri Jul 14 18:07:17 2023 : Debug: (0) files: EXPAND Hello, %{User-Name} Fri Jul 14 18:07:17 2023 : Debug: (0) files: --> Hello, bob Fri Jul 14 18:07:17 2023 : Debug: (0) files: ::: APPENDING Reply-Message FROM 0 TO 0 Fri Jul 14 18:07:17 2023 : Debug: (0) files: ::: TO in 0 out 0 Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authorize]: returned from files (rlm_files) Fri Jul 14 18:07:17 2023 : Debug: (0) [files] = ok Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authorize]: calling pap (rlm_pap) Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authorize]: returned from pap (rlm_pap) Fri Jul 14 18:07:17 2023 : Debug: (0) [pap] = updated Fri Jul 14 18:07:17 2023 : Debug: (0) } # authorize = updated Fri Jul 14 18:07:17 2023 : Debug: (0) Found Auth-Type = PAP Fri Jul 14 18:07:17 2023 : Debug: (0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default Fri Jul 14 18:07:17 2023 : Debug: (0) Auth-Type PAP { Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authenticate]: calling pap (rlm_pap) Fri Jul 14 18:07:17 2023 : Debug: (0) pap: Login attempt with password "hello" (5) Fri Jul 14 18:07:17 2023 : Debug: (0) pap: Comparing with "known good" Cleartext-Password "hello" (5) Fri Jul 14 18:07:17 2023 : Debug: (0) pap: User authenticated successfully Fri Jul 14 18:07:17 2023 : Debug: (0) modsingle[authenticate]: returned from pap (rlm_pap) Fri Jul 14 18:07:17 2023 : Debug: (0) [pap] = ok Fri Jul 14 18:07:17 2023 : Debug: (0) } # Auth-Type PAP = ok Fri Jul 14 18:07:17 2023 : ERROR: (0) Cannot proxy packets unless 'proxy_requests = yes' Fri Jul 14 18:07:17 2023 : Debug: (0) Empty post-auth section in virtual server "default". Using default return values. Fri Jul 14 18:07:17 2023 : Auth: (0) Login OK: [bob/hello] (from client localhost port 0) Fri Jul 14 18:07:17 2023 : Debug: (0) Sent Access-Accept Id 51 from 127.0.0.1:1812 to 127.0.0.1:54599 length 32 Fri Jul 14 18:07:17 2023 : Debug: (0) Reply-Message = "Hello, bob" Fri Jul 14 18:07:17 2023 : Debug: (0) Finished request Fri Jul 14 18:07:17 2023 : Debug: Waking up in 4.9 seconds.
Ошибочная конфигурация
Если переставить модули местами и вместо
authorize { files pap }
Написать модули в обратном порядке
authorize { pap files }
То на момент проверки пароля модулем PAP, атрибут Cleartext-Password
еще не будет доступен о чем и будет сказано в логах:
Sat Jul 15 12:49:59 2023 : WARNING: (0) pap: No "known good" password found for the user. Not setting Auth-Type Sat Jul 15 12:49:59 2023 : WARNING: (0) pap: Authentication will fail unless a "known good" password is available ... Sat Jul 15 12:49:59 2023 : WARNING: (0) No module configured to handle comparisons with &control:Cleartext-Password Sat Jul 15 12:49:59 2023 : WARNING: (0) Add pap or chap to the authorize { ... } and authenticate { ... } sections of this virtual server to handle this "known good" password type Sat Jul 15 12:49:59 2023 : ERROR: (0) No Auth-Type found: rejecting the user via Post-Auth-Type = Reject
Не смотря на то что модуль files
говорит что "все хорошо" авторизация не работает.
Sat Jul 15 12:49:59 2023 : Debug: (0) [files] = ok
How the users
file works
Существуют три списка атрибутов (точнее пар атрибут - значение )
Они привязаны к конкретному запросу/ответу к серверу, и не доступны из других запросов/ответов
request
- содержит список атрибут-значение которые получены в запросе от NAScontrol
- изначально это пустой список, который определяет как и какие модули будут обрабатывать запрос. Этот список заполняется содулями (например files или python) или с помощьюulang
reply
- содержит список атрибутов которые будут отправлены назад на NAS
Модуль users file
определяет какие пары проверять а какие пары добавлять в зависимости их расположения в конфигурационном файле а так же в зависимости от операторов.
Пример:
bob Cleartext-Password := "hello" Reply-Message := "Hello, %{User-Name}"
Первая строка содержит пары атрибут-значение для проверки и пары которые будут добавлены в список control
,
добавление в список control
происходит при полном совпадении всех проверок.
Пары (в первой строке!) которые проверяются которые добавляются в список control
можно отличить по используемым операторам
- Если используются операторы присвоения
:=
or=
то пара атрибут-значение обрабатывается какcontrol
(добавляется в список) - Если используются операторы сравнения
>
,<
,==
,>=
,<=
,=~
то пара атрибут-значение используются для проверки соответвия
В примере выше используется только одна пара Cleartext-Password := "hello"
где используется присвоение, те для этого пользователя будет только добавлена пара в список control (а проверка будет выполнена на другой стадии и другим модулем)
Все пары кроме первой строки содержат только пары которые будут добавлены в ответ.
Описание операторов
Operator | Пример | Использование в check (users и тому подобное), или в условиях unlang |
Использование в reply (users и тому подобное), или в блоках unlang update |
---|---|---|---|
=
|
|
|
|
:=
|
lameuser Auth-Type := Reject Reply-Message = "Your account has been disabled." steve Cleartext-Password := "testing" Service-Type = Framed-User |
Всегда совпадает при проверках, и добавляет в
server { authorize { files pap } authenticate { Auth-Type PAP { pap } }
|
Для атрибутов в ответе ( automate Cleartext-Password := "pppassss" User-Service-Type := 6 |
==
|
swilson Service-Type == Framed-User, Huntgroup-Name == "alphen" Framed-IP-Address = 192.0.2.65, Fall-Through = Yes |
Операция сравнения для authorize { preprocess ... } |
Не применимо |
+=
|
Attribute += Value
Cisco-Account-Info += "APOLICY_MAP_SERVICE_ON_SESSION_START_", Cisco-Account-Info += "NPOLICY_MAP_SERVICE_ON_SESSION_START_" |
При проверке атрибутов - всегда дает совпадение и добавляет атрибут-значение к словарю конфигурационных атрибутов | При формировании ответа добавляет атрибуты к ответу (не переписывая существующие) |
!=
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение не равно указанному (соответственно если атрибута нет то совпадение невозможно) | Не применимо |
>
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение больше указанного | Не применимо |
>=
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение больше или равно указанному | Не применимо |
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение меньше указанного | Не применимо |
<=
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение меньше или равно указанному | Не применимо |
=~
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение совпадает с регулярным выражением, применим только для строк | Не применимо |
!~
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его значение НЕ совпадает с регулярным выражением, применим только для строк | Не применимо |
=*
|
Текст ячейки | Дает совпадение, если атрибут присутствует в запросе и его (значение не проверяется) | Не применимо |
!*
|
Текст ячейки | Дает совпадение, если атрибут НЕ присутствует в запросе и его (значение не проверяется по причине отсутвия) | Не применимо |
Cleartext-Password
Cleartext-Password
это исключительно control
пара атрибут-гачение и эта пара не должна присутвовать ни в каком другом списке
Cleartext-Password
это один из списка атрибутов известных как 'known good' password
который содержит пользовательский пароль в открытом виде.
Другой пример из этого набора "хорошо известных" SSHA-Password
- который содержит salted SHA hash от пользовательского пароля.
Проверка пользовательского пароля происходит cопоставлением атрибута из списка the 'known good' password атрибутов
которые содержаться в списке control
модулями которые могут authenticate
пользователя - это может быть модуль rlm_pap
или другой - например самописный на python
authenticate { Auth-Type ISG { python3_isg } }
По сути, модуль rlm_pap
сравнивает значение User-Password
которое вставил другой модуль - files
как в этом примере или sql
или еще какой-то
User-Password
User-Password
это атрибут который должен содержаться только в запросе и более нигде
User-Password
этот атрибут который пользователь передал на NAS в том видже в котором он был передан
Что бы authenticate
пользователя модуль сравнивает значение атрибута User-Password
со значением (например) Cleartext-Password
из списка атрибутов control
.
В файле users
соответвенно нужно указать:
my_username Cleartext-Password := "known_good_password"
Эта запись означает что при совпадении атрибута User-Name
со значением слева (my_username), нужно в словарь contro
добавить атрибут Cleartext-Password
со значением "known_good_password".
Соответвенно ответ на вопрос "почему писать так не правильно":
shad Cleartext-Password == "test"
такой:
Эта конструкция работает не так как ожидается (не работает авторизация) по-тому что она говорит что при совпадении User-Name
в списке атрибутов запроса (которые прислал NAS)следует найти атрибут
Cleartext-Password
и сравнить его значение со значением "test".
Так как никакой NAS не присылает такой запрос (атрибут Cleartext-Password
отсутствует) то поведение отличается от ожидаемого.
Можно было бы предположить, что если указать User-Password == "test"
то конструкция заработает
К сожалению, это не так.
Действительно, пользователей будет соответствовать и пароль на этой стадии пройдет проверку, но далее все равно пользователь (за исключением специальных конфигураций) получит Reject
Ниже подробности как и почему это происходит.
Auth-Type
Auth-Type
это атрибут который может помещаться только в список атрибутов control
,
и никогда не помещаться ни в какие другие списки.
На сервере есть три основных раздела для обработки запросов
- authorize
- authenticate
- post-auth
и этот список атрибутов control
используется для передачи атрибутов между разными разделами.
authorize
Секция authorize
предназначена для сборки информации о пользователе.
Например в этой секции:
- Может происходить запрос к базе данных для проверки существования пользователя и получения его пароля.
- В этой секции определяется значение атрибута
Auth-Type
на основании которого будет производиться дальнейшая обработка запроса в секцииauthentication
authenticate
Authenticate
это секция где происходит вызов модуля authentication
.
Какой именно модуль будет использоваться определяется значением атрибута Auth-Type
Например
authenticate { Auth-Type ISG { python3_isg } }
При этом значение Auth-Type
=ISG
выставляет модуль в секции authorize
authorize { preprocess python3_isg }
Пример кода из rlm_python
... config = ( ('Auth-Type', 'ISG') ) ...
Post-Auth
Post-Auth
используется в основном для логгирования и примерения других политик (а может и вообще не использоваться), список модулей запускаемых в этой секции определяются результатами работы модулей в секции authenticate
По шагам
- Модули в секции
authorize
проверяют запрос - Если запрос удовлетворяет условиям (модуль "думает" что знает как работать с запросом на стадии
authenticate
) и атрибутAuth-Type
еще не установлен то модуль выставляет атрибутAuth-Type
(значение атрибута зависит от модуля) - В случае модуля
rlm_pap
модуль выставляет атрибутAuth-Type
в значениеPAP
если находит в запросе атрибутUser-Password
- Если ни один из модулей не выставил атрибут
Auth-Type
то запрос будет отброшен (Rejected)
Теперь можно вернуться к вопросу что не так с конструкцией User-Password == "test"
- Модуль
rlm_pap
в секцииauthorize
выставит code>Auth-Type в значениеPAP
так как в запросе присутствуетUser-Password
- Модуль
rlm_pap
в секцииauthenticate
попробует сравнить найти в списке атрибутов
config
один из "известных хороших" атрибутов с паролем, например Cleartext-Password
или любой другой, и не найдя их вернет статус Reject
Если такое поведение не желательно, то его можно обойти с помощью "волшебного" значения Auth-Type
, Accept
,
которое позволяет пропустить секцию authenticate
Если нужно реализовать такое поведение и не вызывать модуль rlm_pap
:
shad Auth-Type := Accept, User-Password == "test"