FreeRadius Notes

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску

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

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

Тут чертовски важен порядок модулей - если переставить местами 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"