FreeRadius Notes: различия между версиями

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску
 
(не показано 70 промежуточных версий этого же участника)
Строка 6: Строка 6:
 
=Минимальный рабочий конфиг=
 
=Минимальный рабочий конфиг=
 
Тут чертовски важен порядок модулей - если переставить местами pap/files то получится что pap не сможет получить пароль
 
Тут чертовски важен порядок модулей - если переставить местами pap/files то получится что pap не сможет получить пароль
  +
==<code>sites-enabled/default</code>==
<BR>
 
По сути тут логика такая
 
* Пришел пользователь <code>User-Name = "bob"</code> <code> <code>User-Password = "hello"</code>
 
*
 
 
<PRE>
 
<PRE>
 
server default {
 
server default {
Строка 48: Строка 45:
 
} # end of SERVER
 
} # end of SERVER
 
</PRE>
 
</PRE>
  +
  +
==Клиенты <code>clients.conf</code>==
  +
  +
<PRE>
  +
client localhost {
  +
ipaddr = 127.0.0.1
  +
proto = *
  +
secret = secret
  +
require_message_authenticator = no
  +
limit {
  +
max_connections = 16
  +
lifetime = 0
  +
idle_timeout = 30
  +
}
  +
}
  +
</PRE>
  +
==Модуль PAP <code>mods-enabled/pap</code>==
  +
<PRE>
  +
pap {
  +
# normalise = yes
  +
auto_header = no
  +
}
  +
</PRE>
  +
  +
==Модуль files==
  +
===<code>mods-enabled/files</code>===
  +
<PRE>
  +
files {
  +
moddir = ${modconfdir}/${.:instance}
  +
#key = "%{%{Stripped-User-Name}:-%{User-Name}}"
  +
filename = ${moddir}/authorize
  +
usersfile = ${moddir}/authorize
  +
# acctusersfile = ${moddir}/accounting
  +
}
  +
</PRE>
  +
===<code>mods-config/files/authorize</code>===
  +
<PRE>
  +
bob Cleartext-Password := "hello"
  +
Reply-Message := "Hello, %{User-Name}"
  +
</PRE>
  +
  +
=Проверка работы минимальной конфигурации=
  +
<BR>
  +
  +
* Для проверки использую команду <code>radtest bob hello localhost 0 secret</code>
  +
** <code>bob</code> - имя пользователя, атрибут <code>User-Name</code>
  +
** <code>hello</code> - пароль, который будет передан в атрибуте <code>User-Password</code>
  +
** <code>localhost</code> - адрес куда отправить запрос
  +
** <code>0</code> - номер (виртуального) порта, <code>NAS-Port</code>
  +
** <code>secret</code> - radius shared secret, который указан в настройках сервера и клиента
  +
  +
По сути тут логика такая
  +
* "Пришел" пользователь <code>User-Name = "bob"</code> <code>User-Password = "hello"</code>
  +
* Его проверили по файлу (модуль <code>files</code> ищет пользователя в файле, имя файла определено конфигурацией модуля) и находит, о чем говорит запись <code>files: users: Matched entry bob at line 4</code>
  +
* Далее для модулей вызываных ниже по конфигу будут доступны атрибуты которые добавил модуль <code>files</code> а именно:
  +
<PRE>
  +
bob Cleartext-Password := "hello"
  +
Reply-Message := "Hello, %{User-Name}"
  +
</PRE>
  +
* При этом модуль <code>files</code> по сути не делает проверок - он только сопоставляет запрос, и по ключу который определен в конфигурации (<code>key = "%{%{Stripped-User-Name}:-%{User-Name}}"</code>) находит (или не находит) атрибуты.
  +
<BR>
  +
Запись в логах вида:
  +
<PRE>
  +
[files] = ok
  +
</PRE>
  +
Означает что такой пользователь существует, НО не означает что он успешно авторизован.
  +
* Далее модуль PAP осуществляет проверку пароля. Важно то, что пароль в описании пользователя должен содержаться в атрибутах "known good", в нашем случае - <code>Cleartext-Password</code>
  +
<BR>
  +
{{#spoiler:show=Выдержка из man rlm_pap |
  +
  +
<PRE>
  +
The module looks for the Password-With-Header control attribute to find the "known good" password. The attribute value comprises the header followed immediately by the password data. The
  +
header is given by the following table.
  +
  +
Header Attribute Description
  +
------ --------- -----------
  +
{clear} Cleartext-Password Clear-text passwords
  +
{cleartext} Cleartext-Password Clear-text passwords
  +
{crypt} Crypt-Password Unix-style "crypt"ed passwords
  +
{md5} MD5-Password MD5 hashed passwords
  +
{base64_md5} MD5-Password MD5 hashed passwords
  +
{smd5} SMD5-Password MD5 hashed passwords, with a salt
  +
{sha} SHA-Password SHA1 hashed passwords
  +
SHA1-Password SHA1 hashed passwords
  +
{ssha} SSHA-Password SHA1 hashed passwords, with a salt
  +
{sha2} SHA2-Password SHA2 hashed passwords
  +
{sha224} SHA2-Password SHA2 hashed passwords
  +
{sha256} SHA2-Password SHA2 hashed passwords
  +
{sha384} SHA2-Password SHA2 hashed passwords
  +
{sha512} SHA2-Password SHA2 hashed passwords
  +
{ssha224} SSHA2-224-Password SHA2 hashed passwords, with a salt
  +
{ssha256} SSHA2-256-Password SHA2 hashed passwords, with a salt
  +
{ssha384} SSHA2-384-Password SHA2 hashed passwords, with a salt
  +
{ssha512} SSHA2-512-Password SHA2 hashed passwords, with a salt
  +
{nt} NT-Password Windows NT hashed passwords
  +
{nthash} NT-Password Windows NT hashed passwords
  +
{md4} NT-Password Windows NT hashed passwords
  +
{x-nthash} NT-Password Windows NT hashed passwords
  +
{ns-mta-md5} NS-MTA-MD5-Password Netscape MTA MD5 hashed passwords
  +
{x- orcllmv} LM-Password Windows LANMAN hashed passwords
  +
{X- orclntv} NT-Password Windows NT hashed passwords
  +
</PRE>
  +
}}
  +
  +
  +
* Собственно на этом все.
   
 
<PRE>
 
<PRE>
Строка 95: Строка 198:
 
Fri Jul 14 18:07:17 2023 : Debug: Waking up in 4.9 seconds.
 
Fri Jul 14 18:07:17 2023 : Debug: Waking up in 4.9 seconds.
 
</PRe>
 
</PRe>
  +
  +
  +
=Ошибочная конфигурация=
  +
Если переставить модули местами и вместо
  +
<PRE>
  +
authorize {
  +
files
  +
pap
  +
}
  +
</PRE>
  +
Написать модули в обратном порядке
  +
<PRE>
  +
authorize {
  +
pap
  +
files
  +
}
  +
</PRE>
  +
  +
То на момент проверки пароля модулем PAP, атрибут <code>Cleartext-Password</code> еще не будет доступен о чем и будет сказано в логах:
  +
<PRE>
  +
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
  +
</PRE>
  +
Не смотря на то что модуль <code>files</code> говорит что "все хорошо" авторизация не работает.
  +
<PRE>
  +
Sat Jul 15 12:49:59 2023 : Debug: (0) [files] = ok
  +
</PRE>
  +
  +
  +
=How the <code>users</code> file works=
  +
  +
Существуют три списка атрибутов (точнее пар атрибут - значение )
  +
<BR>
  +
Они привязаны к конкретному запросу/ответу к серверу, и не доступны из других запросов/ответов
  +
  +
* <code>request</code> - содержит список атрибут-значение которые получены в запросе от NAS
  +
* <code>control</code> - изначально это пустой список, который определяет как и какие модули будут обрабатывать запрос. Этот список заполняется содулями (например files или python) или с помощью <code>ulang</code>
  +
* <code>reply</code> - содержит список атрибутов которые будут отправлены назад на NAS
  +
  +
Модуль <code>users file</code> определяет какие пары проверять а какие пары добавлять в зависимости их расположения в конфигурационном файле а так же в зависимости от операторов.
  +
<BR>
  +
Пример:
  +
<PRE>
  +
bob Cleartext-Password := "hello"
  +
Reply-Message := "Hello, %{User-Name}"
  +
  +
</PRE>
  +
Первая строка содержит пары атрибут-значение для проверки и пары которые будут добавлены в список <code>control</code>, <BR>
  +
добавление в список <code>control</code> происходит при полном совпадении всех проверок.
  +
<BR>
  +
Пары (в первой строке!) которые проверяются которые добавляются в список <code> control</code> можно отличить по используемым операторам
  +
  +
* Если используются операторы присвоения <code>:=</code> or <code>=</code> то пара атрибут-значение обрабатывается как <code>control</code> (добавляется в список)
  +
* Если используются операторы сравнения <code>></code>, <code><</code>, <code>==</code>, <code>>=</code>, <code><=</code>, <code>=~</code> то пара атрибут-значение используются для проверки соответвия
  +
  +
В примере выше используется только одна пара <code>Cleartext-Password := "hello"</code> где используется присвоение, те для этого пользователя будет только добавлена пара в список control (а проверка будет выполнена на другой стадии и другим модулем)
  +
<BR>
  +
Все пары кроме первой строки содержат только пары которые будут добавлены в ответ.
  +
==Описание операторов==
  +
{| class="wikitable"
  +
|+ Файлы
  +
|-
  +
! Operator !! Пример !!Использование в <code>check</code> <BR> (users и тому подобное), или в условиях unlang !! Использование в <code>reply</code><BR>(users и тому подобное), или в блоках unlang update
  +
|-
  +
| <code>=</code>
  +
||
  +
<code>Attribute = Value</code>
  +
||
  +
* Используется для установки атрибутов конфигурации сервера
  +
* Не допускается для операторов проверки атрибутов (например проверки пароля)
  +
||
  +
* Используется для добавления пары атрибут-значение, только если такой атрибут не был добавлен раньше (если был то значение изменено не будет)
  +
|-
  +
| <code> :=</code>
  +
||
  +
<PRE>
  +
lameuser Auth-Type := Reject
  +
Reply-Message = "Your account has been disabled."
  +
</PRE>
  +
<PRE>
  +
steve Cleartext-Password := "testing"
  +
Service-Type = Framed-User
  +
</PRE>
  +
||
  +
Всегда совпадает при проверках, и добавляет в <code>control</code> атрибут-значение, если такой атрибут уже был, то замещает значение. (по сути оператор присвоения)
  +
В первом примере пользователь всегда получит <BR> <code>Auth-Type := Reject </code>, если ниже нет другого условия (например для группы), которое переопределит <code>Auth-Type</code>
  +
<BR>
  +
Второй пример показывает как правильно установить пользователю пароль. Эта конструкция может показаться странной, так как присвоение пароля <code>:=</code> а не сравнение.
  +
<BR>Тут важно понимать следующее
  +
* Эта запись будет работать если в запросе есть атрибут <code>User-Name</code> со значением "steve"
  +
* В <code>control</code> словарь будет помещен атрибут <code>Cleartext-Password</code> со значением "testing"
  +
* В <code>reply</code> словарь будет помещен атрибут <code>Service-Type</code> со значением "Framed-User"
  +
* При этом проверка пароля будет происходить в зависимости от того что опеределено в настройке сервера
  +
  +
<PRE>
  +
server {
  +
  +
authorize {
  +
files
  +
pap
  +
}
  +
authenticate {
  +
Auth-Type PAP {
  +
pap
  +
}
  +
}
  +
</PRE>
  +
* В примере в секции <code> authorize</code> выше модуль <code>files</code> добавит атрибут <code>Cleartext-Password</code>, модуль <code>pap</code> добавит атрибут <code>Auth-Type</code> со значением "PAP"
  +
* И только в секции <code> authenticate </code> будет происходить проверка пароля
  +
||
  +
Для атрибутов в ответе (<code>reply</code>) работает так же, как оператор присвоения (добавляет атрибут-значение если его не было, переопределяет значение если атрибут уже был)
  +
<PRE>
  +
automate Cleartext-Password := "pppassss"
  +
User-Service-Type := 6
  +
</PRE>
  +
|-
  +
| <code> ==</code>
  +
|| <PRE>
  +
swilson Service-Type == Framed-User, Huntgroup-Name == "alphen"
  +
Framed-IP-Address = 192.0.2.65,
  +
Fall-Through = Yes
  +
</PRE>
  +
||
  +
Операция сравнения для <code>check</code> атрибутов, успешна если совпадает атрибут присутствует и его значение совпадает с указанным.<BR>
  +
В примере при совпадении <code>User-Name</code> со значением "swilson" и если в запросе
  +
присутвуют атрибут <code>Service-Type</code> со значением "Framed-User" и <code>Huntgroup-Name</code> со значением "alphen"
  +
<BR>
  +
Сам атрибут <code>Huntgroup-Name</code> должен быть установлен дополнительно в модуле <code>preprocess</code>
  +
<PRE>
  +
authorize {
  +
preprocess
  +
...
  +
}
  +
</PRE>
  +
||
  +
Не применимо
  +
|-
  +
| <code> +=</code>
  +
|| <code>Attribute += Value</code>
  +
<PRE>
  +
Cisco-Account-Info += "APOLICY_MAP_SERVICE_ON_SESSION_START_",
  +
Cisco-Account-Info += "NPOLICY_MAP_SERVICE_ON_SESSION_START_"
  +
</PRE>
  +
|| При проверке атрибутов - всегда дает совпадение и добавляет атрибут-значение к словарю конфигурационных атрибутов
  +
|| При формировании ответа добавляет атрибуты к ответу (не переписывая существующие)
  +
|-
  +
| <code> !=</code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение не равно указанному (соответственно если атрибута нет то совпадение невозможно)
  +
|| Не применимо
  +
|-
  +
| <code> > </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение больше указанного
  +
|| Не применимо
  +
|-
  +
| <code> >= </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение больше или равно указанному
  +
|| Не применимо
  +
|-
  +
|
  +
<code> < </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение меньше указанного
  +
|| Не применимо
  +
|-
  +
| <code> <= </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение меньше или равно указанному
  +
|| Не применимо
  +
|-
  +
| <code> =~ </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение совпадает с регулярным выражением, применим только для строк
  +
|| Не применимо
  +
|-
  +
| <code> !~ </code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его значение НЕ совпадает с регулярным выражением, применим только для строк
  +
|| Не применимо
  +
|-
  +
| <code> =*</code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут присутствует в запросе и его (значение не проверяется)
  +
|| Не применимо
  +
|-
  +
| <code> !*</code>
  +
|| Текст ячейки
  +
|| Дает совпадение, если атрибут НЕ присутствует в запросе и его (значение не проверяется по причине отсутвия)
  +
|| Не применимо
  +
|}
  +
  +
  +
  +
  +
==<code>Cleartext-Password</code>==
  +
  +
<code>Cleartext-Password</code> это исключительно <code>control</code> пара атрибут-гачение и эта пара не должна присутвовать ни в каком другом списке
  +
<BR>
  +
<code>Cleartext-Password</code> это один из списка атрибутов известных как <B>'known good' password</B> <BR> который содержит пользовательский пароль в открытом виде.
  +
<BR>
  +
Другой пример из этого набора "хорошо известных" <code>SSHA-Password</code> - который содержит salted SHA hash от пользовательского пароля.
  +
<BR>
  +
Проверка пользовательского пароля происходит cопоставлением атрибута из списка the <B>'known good' password</B> атрибутов <BR>
  +
которые содержаться в списке <code>control</code> модулями которые могут <code>authenticate</code> пользователя - это может быть модуль <code>rlm_pap</code>
  +
<BR>
  +
или другой - например самописный на python
  +
<PRE>
  +
authenticate {
  +
Auth-Type ISG {
  +
python3_isg
  +
}
  +
}
  +
</PRE>
  +
  +
По сути, модуль <code>rlm_pap</code> сравнивает значение <code>User-Password</code> которое вставил другой модуль - <code>files</code> как в этом примере или <code>sql</code> или еще какой-то
  +
  +
==<code>User-Password</code>==
  +
  +
<code>User-Password</code> это атрибут который должен содержаться только в запросе и более нигде
  +
<BR>
  +
<code>User-Password</code> этот атрибут который пользователь передал на NAS в том видже в котором он был передан <BR>
  +
  +
Что бы <code>authenticate</code> пользователя модуль сравнивает значение атрибута <code>User-Password</code> со значением (например) <code>Cleartext-Password</code> из списка атрибутов <code>control</code>.
  +
<BR>
  +
В файле <code>users</code> соответвенно нужно указать:
  +
<PRE>
  +
my_username Cleartext-Password := "known_good_password"
  +
</PRE>
  +
  +
Эта запись означает что при совпадении атрибута <code>User-Name</code> со значением слева (my_username), нужно в словарь <code>contro</code> добавить атрибут <code>Cleartext-Password</code> со значением "known_good_password".
  +
<BR>
  +
Соответвенно ответ на вопрос "почему писать так не правильно":
  +
<PRE>
  +
shad Cleartext-Password == "test"
  +
</PRE>
  +
<BR>
  +
такой:
  +
Эта конструкция работает не так как ожидается (не работает авторизация) по-тому что она говорит что при совпадении <code>User-Name</code> в списке атрибутов запроса (которые прислал NAS)следует найти атрибут
  +
<code>Cleartext-Password</code> и сравнить его значение со значением "test".
  +
Так как никакой NAS не присылает такой запрос (атрибут <code>Cleartext-Password</code> отсутствует) то поведение отличается от ожидаемого.
  +
<BR>
  +
Можно было бы предположить, что если указать <code>User-Password == "test"</code> то конструкция заработает<BR>
  +
К сожалению, это не так.
  +
<BR>
  +
Действительно, пользователей будет соответствовать и пароль на этой стадии пройдет проверку, но далее все равно пользователь (за исключением специальных конфигураций) получит <code>Reject</code>
  +
<BR>
  +
<BR>
  +
Ниже подробности как и почему это происходит.
  +
  +
==<code>Auth-Type</code>==
  +
  +
<code>Auth-Type</code> это атрибут который может помещаться только в список атрибутов <code>control</code>, <BR>
  +
и никогда не помещаться ни в какие другие списки.
  +
<BR>
  +
  +
На сервере есть три основных раздела для обработки запросов
  +
* authorize
  +
* authenticate
  +
* post-auth
  +
и этот список атрибутов <code>control</code> используется для передачи атрибутов между разными разделами.
  +
  +
==<code>authorize</code>==
  +
Секция <code>authorize</code> предназначена для сборки информации о пользователе.
  +
Например в этой секции:
  +
* Может происходить запрос к базе данных для проверки существования пользователя и получения его пароля.
  +
* В этой секции определяется значение атрибута <code>Auth-Type</code> на основании которого будет производиться дальнейшая обработка запроса в секции <code>authentication</code>
  +
  +
==<code>authenticate</code>==
  +
<code>Authenticate</code> это секция где происходит вызов модуля <code>authentication</code>.
  +
<BR>
  +
Какой именно модуль будет использоваться определяется значением атрибута <code>Auth-Type</code>
  +
<BR>
  +
Например
  +
<PRE>
  +
authenticate {
  +
Auth-Type ISG {
  +
python3_isg
  +
}
  +
}
  +
</PRE>
  +
При этом значение <code>Auth-Type</code>=<code>ISG</code> выставляет модуль в секции <code> authorize</code>
  +
<PRE>
  +
authorize {
  +
preprocess
  +
python3_isg
  +
}
  +
</PRE>
  +
Пример кода из <code>rlm_python</code>
  +
<PRE>
  +
...
  +
config = ( ('Auth-Type', 'ISG') )
  +
...
  +
</PRE>
  +
  +
==<code>Post-Auth</code>==
  +
<code>Post-Auth</code> используется в основном для логгирования и примерения других политик (а может и вообще не использоваться), список модулей запускаемых в этой секции определяются результатами работы модулей в секции <code>authenticate</code>
  +
  +
==По шагам==
  +
* Модули в секции <code>authorize</code> проверяют запрос
  +
* Если запрос удовлетворяет условиям (модуль "думает" что знает как работать с запросом на стадии <code> authenticate</code>) и атрибут <code>Auth-Type</code> еще не установлен то модуль выставляет атрибут <code>Auth-Type </code> (значение атрибута зависит от модуля)
  +
* В случае модуля <code>rlm_pap</code> модуль выставляет атрибут <code>Auth-Type</code> в значение <code>PAP</code> если находит в запросе атрибут <code>User-Password</code>
  +
* Если ни один из модулей не выставил атрибут <code>Auth-Type</code> то запрос будет отброшен (Rejected)
  +
  +
<BR>
  +
Теперь можно вернуться к вопросу что не так с конструкцией <code> User-Password == "test" </code>
  +
* Модуль <code>rlm_pap</code> в секции <code> authorize</code> выставит code>Auth-Type</code> в значение <code>PAP</code> так как в запросе присутствует <code> User-Password</code>
  +
* Модуль <code>rlm_pap</code> в секции <code> authenticate</code> попробует сравнить найти в списке атрибутов
  +
<code>config</code> один из "известных хороших" атрибутов с паролем, например <code>Cleartext-Password</code>
  +
или любой другой, и не найдя их вернет статус <code>Reject</code>
  +
  +
<BR>
  +
Если такое поведение не желательно, то его можно обойти с помощью "волшебного" значения <code>Auth-Type</code>, <code>Accept</code>,
  +
которое позволяет пропустить секцию <code>authenticate</code>
  +
  +
Если нужно реализовать такое поведение и не вызывать модуль <code>rlm_pap</code>:
  +
<PRE>
  +
shad Auth-Type := Accept, User-Password == "test"
  +
</PRE>

Текущая версия на 20:16, 20 марта 2024

Это просто сборник ссылок и заметок

Минимальный рабочий конфиг

Тут чертовски важен порядок модулей - если переставить местами 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 - содержит список атрибут-значение которые получены в запросе от NAS
  • control - изначально это пустой список, который определяет как и какие модули будут обрабатывать запрос. Этот список заполняется содулями (например 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
=

Attribute = Value

  • Используется для установки атрибутов конфигурации сервера
  • Не допускается для операторов проверки атрибутов (например проверки пароля)
  • Используется для добавления пары атрибут-значение, только если такой атрибут не был добавлен раньше (если был то значение изменено не будет)
 :=
 
lameuser       Auth-Type := Reject
               Reply-Message = "Your account has been disabled."
steve  Cleartext-Password := "testing"
       Service-Type = Framed-User

Всегда совпадает при проверках, и добавляет в control атрибут-значение, если такой атрибут уже был, то замещает значение. (по сути оператор присвоения) В первом примере пользователь всегда получит
Auth-Type := Reject , если ниже нет другого условия (например для группы), которое переопределит Auth-Type
Второй пример показывает как правильно установить пользователю пароль. Эта конструкция может показаться странной, так как присвоение пароля := а не сравнение.
Тут важно понимать следующее

  • Эта запись будет работать если в запросе есть атрибут User-Name со значением "steve"
  • В control словарь будет помещен атрибут Cleartext-Password со значением "testing"
  • В reply словарь будет помещен атрибут Service-Type со значением "Framed-User"
  • При этом проверка пароля будет происходить в зависимости от того что опеределено в настройке сервера
server {

    authorize {
            files
            pap
    }
    authenticate {
        Auth-Type PAP {
                pap
        }
    }
  • В примере в секции authorize выше модуль files добавит атрибут Cleartext-Password, модуль pap добавит атрибут Auth-Type со значением "PAP"
  • И только в секции authenticate будет происходить проверка пароля

Для атрибутов в ответе (reply) работает так же, как оператор присвоения (добавляет атрибут-значение если его не было, переопределяет значение если атрибут уже был)

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

Операция сравнения для check атрибутов, успешна если совпадает атрибут присутствует и его значение совпадает с указанным.
В примере при совпадении User-Name со значением "swilson" и если в запросе присутвуют атрибут Service-Type со значением "Framed-User" и Huntgroup-Name со значением "alphen"
Сам атрибут Huntgroup-Name должен быть установлен дополнительно в модуле preprocess

    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"