Page tree
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

Плагин proxy

Замена фильтру conditional_plugin_selector из второй версии агента.

Структура конфигурации

plugins:
  proxy:
    dhcp:
      routes:
        base/dhcp-o82: $request.RAD_REQUEST.try("DHCP-Relay-Circuit-Id").present?()
        base/dhcp-ip-private: $request.RAD_REQUEST.DHCP-Client-IP-Address.private_ip4?()
 
      default: "base/dhcp-ip-public"

Запрос будет передан первому плагину, для которого выполнится условие. Если ни одно условие не выполнилось, запрос уйдёт плагину, указанному в ключе default

Плагин base

Данный плагин предназначен для непосредственной обработки запросов, поступающих от сервера FreeRADIUS.

Стэк обработки запроса

plugins → base → <name> → call_stack

Определяет порядок вызова рабочим процессом фильтров для обработки поступившего запроса:

  1. Вызываются фильтры в прямом (сверху вниз) порядке, при этом пропускаются after-фильтры.
  2. Запрос обрабатывается ядром плагина: выполняется действие, соответствующее типу запроса и формируется ответ.
  3. Вызываются фильтры в обратном (снизу вверх) порядке, при этом пропускаются before-фильтры.
Пример стэка обработки запроса — IPoE-доступ с идентификацией по IP-адресу и динамической авторизацией сервисов Cisco ISG
plugins:
  base:
    isg-ipoe-by-ip:
      call_stack:
        # Установка тега с типом запроса
        - set_tag_before/request_type
 
        # Отправка отказа в случае возникновения ошибки
        - reject_on_error/main

        # Игнорирование лишнего аккаунтинга
        - check_attr_before/skip_unnecessary_accounting_packet

        # Аккаунтинг по базовой сессии
        - set_tag_before/base-session-accounting

        # Авторизация ISG-сервисов вида IPOE-SC-INET-<Rate>-<Burst>
        - set_tag_before/auth-internet-access-isg-service
        - match_before/get_speed_limit_values_for_internet-access-isg-service
        - format_after/set_cisco-service-info_attr_for_internet-access-isg-service

        # Добавление префикса ко всем активируемым сервисам
        - map_after/add_prefix_to_isg-services

Действия ядра плагина

plugins → base → <name> → actions

В данной ветви конфигурации содержатся отдельные блоки, определяющие действия ядра плагина для каждого поддерживаемого типа запроса. На текущий момент поддерживаются 4 типа запросов сервера FreeRADIUS:

  1. Авторизация.
  2. Аутентификация.
  3. Пост-аутентификация.
  4. Аккаунтинг.

Общее описание того, что происходит на этих стадиях в самом сервере FreeRADIUS доступно в вики проекта: http://wiki.freeradius.org/guide/Concepts.

Авторизация — действие authorize

На данном этапе обработки запроса RADIUS-сервером от агента HARD требуется:

  1. Определить, известно ли ему абонентское оборудование, запросившее доступ.
  2. Предоставить RADIUS-серверу корректный пароль для выполнения аутентификации.

Для этого в действии выполняются:

  1. Подбор профилей оборудования.
  2. Формирование ответа.
Подбор профилей оборудования

Параметры подбора профилей оборудования определяются в блоках plugins → base → <name> → actions → authorize → customer_profile и plugins → base → <name> → actions → authorize → provider_profile для профилей абонентского и операторского оборудования соответственно. Подобранные профили записываются в системные переменные $customer_profile и $provider_profile, а идентификатор привязки абонентского профиля к операторскому — в переменную $bind_id.

Данные блоки имеют одинаковую структуру:

КлючДопустимые значенияОписание

 

set_by

search_cache

Поиск профиля в кэше (MongoDB). Поиск выполняется в соответствии с критериями, указанными в ключе query. Хотя бы один из профилей (абонентский либо операторский) должен подбираться с использованием данного метода. Для подбора обоих профилей данный метод, как правило, используется только при необходимости формировать различные ответы в зависимости от результатов подбора каждого из профилей и наличия между ними подходящей привязки. Пример использования — IPoE-портал, для которого при авторизации абонентскому оборудованию выдаются различные атрибуты в зависимости от актуальности данных о нём в Гидре.

Если оба профиля подбираются с использованием этого метода, системная переменная $bind_id (идентификатор привязки абонентского оборудования к операторскому) заполняется в соответствии с типом привязки, указанном в ключе plugins → base → <name> → actions → authorize → default_bind_type.

set_first_from_customer_profileПрофиль операторского оборудования извлекается из ранее подобранного профиля абонентского оборудования. В ключе filter определяются критерии отбора операторских профилей, а из удовлетворяющих фильтру профилей выбирается первый.
set_first_from_provider_profileПрофиль абонентского оборудования извлекается из ранее подобранного профиля операторского оборудования. В ключе filter определяются критерии отбора абонентских профилей, а из удовлетворяющих фильтру профилей выбирается первый. Данный метод, обычно используется для схемы доступа, в которой абонентское оборудование идентифицируется по данным операторского оборудования, к которому оно подключено: DHCP Option 82, Q-in-Q VLAN и т. д. При этом дополнительная фильтрация может использваться для проверки MAC-адреса абонентского оборудования, или ограничения профилей соответствующим нужной схеме доступа шаблоном, если одновременно используется несколько схем.
queryОдна или несколько пар key:value

Запрос для поиска профиля в кэше. Используется только с search_cache в качестве значения set_by. На основании пар key:value строится запрос в базу данных кэша.

Несмотря на то, что эти пары задаются не последовательностью, их порядок имеет значение. При запуске, агент формирует в БД кэша недостающие индексы для оптимизации работы с ней. Индексы для подбора профилей формируются с учётом порядка ключей в данном блоке. В связи с этим для оптимальной работы рекомендуется располагать ключи key в порядке убывания селективности: от более селективных (логин, MAC-адрес), к менее селективным (номер порта коммутатора, IP-адрес сервера доступа).

В качестве value такой пары может быть указан список значений. В таком случае соответствующий ключ профиля должен должен иметь значение, равное любому из элементов списка: в запросе к БД кэша к этому ключу применится оператор $in. Это позволяет, например, подбирать профиль по адресу привязанной к абонентскому оборудованию подсети, в которую входит IP-адрес из запроса, если маска подсети не известна. С помощью фильтра для IP-адреса вычисляются подсети всех возможных размеров и список этих подсетей указывается в качестве критерия подбора профиля абонентского оборудования.

filterОдна или несколько пар key:valueФильтр для отбора вложенных профилей. Используется только с set_first_from_customer_profile или set_first_from_provider_profile в качестве значения set_by. В качестве критерия фильтра можно указывать тип привязки абонентского оборудования к операторскому, значения атрибутов подбираемого профиля и т. д.
Пример раздельного подбора абонентского и операторского профилей — использование IPoE-портала и идентификация по VLAN'у и номеру порта коммутатора с дополнительной проверкой MAC-адреса
plugins:
  base:
    jmx5-qinq:
      actions:
        authorize:
          customer_profile: &customer_profile
            set_by: search_cache
            query:
              MAC-Address: $request.RAD_REQUEST.User-Name.normalize_mac()

          provider_profile: &provider_profile
            set_by: search_cache
            query:
              Switch-VLAN: $var.Remote-Circuit-Ids.Remote_Id
              Switch-Port-Code: $var.Remote-Circuit-Ids.Circuit_Id

          default_bind_type: network_connection
Пример подбора операторского профиля и выбор абонентского из него — идентификация по данным DHCP Option 82
plugins:
  base:
    se-ipoe-by-o82:
      actions:
        authorize:
          provider_profile:
            set_by: search_cache
            query:
              Switch-IP: $request.RAD_REQUEST.Agent-Remote-Id.substring(6).unhex()
              Switch-Port-Code: $request.RAD_REQUEST.Agent-Circuit-Id.substring(12).to_i(16)

          customer_profile:
            set_by: set_first_from_provider_profile
            filter:
              Auth-Scheme: '"DHCP+L2TP"'
Пример подбора абонентского профиля без подбора операторского — идентификация по логину
plugins:
  base:
    l2tp-by-login:
      actions:
        authorize:
          customer_profile: &customer_profile
            set_by: search_cache
            query:
              L2TP-Login: $request.RAD_REQUEST.User-Name.lower().regexp_replace("@.*$|\\\\t|\s")
              Auth-Scheme: '"DHCP+L2TP"'

          provider_profile: {}

 

Формирование ответа

После подбора профилей, можно формировать ответ. На этом этапе в распоряжении ядра плагина имеются следующие данные:

  • Обрабатываемый запрос в $request.
  • Профиль абонентского оборудования в $customer_profile.
  • Профиль операторского оборудования в $provider_profile.
  • Идентификатор привязки абонентского оборудования к операторскому в $bind_id.
  • Установленные before-фильтрами пользовательские переменные в словаре $var и теги в $context.

Все эти сведения об авторизуемом оборудовании могут использоваться в вычисляемых выражениях как для выбора шаблона ответа, так и для заполнения атрибутов выбранного шаблона конкретными значениями.

Шаблоны ответов определяются как последовательность и размещаются в ключе plugins → base → <name> → actions → authorize → reply конфигурации плагина. Элементы данной последовательности имеют следующую структуру:

КлючДопустимые значенияОписание
conditionЛогическое вычисляемое выражениеУсловие применения шаблона: шаблон выбирается, если вычисляемое выражение из данного ключа вернуло истину, либо не задано.
templateПары key:valueШаблон ответа, где key — наименование RADIUS-атрибута ответа, а value — вычисляемое выражение, результат которого станет значением этого атрибута. В качестве value могут использоваться как единичные выражения, так и последовательности таких выражений. Последовательности выражений применяются для атрибутов, допускающих множественные значения, таких как, например, Cisco-AVPair.

Ядро плагина по очереди (сверху вниз) проверяет каждый шаблон ответа на возможность его применения: если условие применения выполняется, для формирования ответа используется этот шаблон, если нет — ядро переходит к проверке условия для следующего.

Формирование ответа, включая выбор шаблона, может выполняться не самим ядром плагина, а специальным фильтром render_reply_after. При наличии такого фильтра в стэке обработки запроса, ядро плагина не формирует ответ, а только подбирает профили оборудования. Это позволяет провести дополнительные вычисления и проверки с помощью after-фильтров и уже на основании их результатов формировать ответ.

Пример последовательности шаблонов ответов — IPoE с использованием сервисной модели Cisco ISG
plugins:
  base:
    isg-ipoe-by-ip:
      actions:
        authorize:
          reply: &default_reply
            # Авторизация ISG-сервисов вида IPOE-SC-INET-<Rate>-<Burst>
            - condition: $context.has_tag?("auth-internet-access-isg-service") and $request.RAD_REQUEST.User-Name.like?("^IPOE-SC-INET(-\d+){2}$")
              template:
                RAD_CHECK: &default_rad_check
                  # Идентификация абонентского оборудования выполняется по IP-адресу, а для сервисов вовсе не нужна.
                  # Поэтому для PAP-модуля FreeRADIUS передаём в качестве «правильного» пароль из запроса.
                  Cleartext-Password: $request.RAD_REQUEST.User-Password

                RAD_REPLY:
                  Cisco-AVPair:
                    - '"subscriber:accounting-list=HYDRA-IPOE"'
                    - '"ip:traffic-class=in access-group name IPOE-ACL-ALL-TRAFF priority 1000"'
                    - '"ip:traffic-class=out access-group name IPOE-ACL-ALL-TRAFF priority 1000"'
                  Acct-Interim-Interval: '"300"'
 
                result: $rlm.OK

            # Оборудование не подобралось - отказ
            - condition: $customer_profile.null?()
              template:
                RAD_CHECK:
                  Auth-Type: '"Reject"'

                RAD_REPLY:
                  Reply-Message: '"Incorrect IP address"'

                result: $rlm.NOTFOUND

            # Всё в порядке
            - condition: $customer_profile.attributes.try("Internet-Access-Service-State") == "SERV_STATE_Provision"
              template:
                RAD_CHECK:
                  <<: *default_rad_check

                RAD_REPLY: &ok_rad_reply
                  Cisco-AVPair: '"subscriber:accounting-list=HYDRA-IPOE"'
                  Acct-Interim-Interval: '"300"'
                  Idle-Timeout: '"86400"'
                  Cisco-Account-Info: $customer_profile.attributes.ISG-IPoE-Services.split(",")

                result: $rlm.OK

            # Доступ заблокирован
            - template:
                RAD_CHECK:
                  <<: *default_rad_check

                RAD_REPLY:
                  <<: *ok_rad_reply
                  Cisco-Account-Info:
                    - '"IPOE-SC-REDIRECT"'
                    - '"IPOE-SC-OPENGARDEN"'

                result: $rlm.OK 

 

Аутентификация — действие authenticate

На данном этапе RADIUS-сервер от агента HARD требуется подготовить атрибуты ответа, которые сервер отправит в случае успешной аутентификации модулем pap, eap, mschap и т.п. Для этого в действии выполняются:

  1. Подбор профилей оборудования.
  2. Проверка ограничения количества одновременных сессий.
  3. Формирование ответа.
 

Подбор профилей и шаблоны ответов настраиваются аналогично действию authorize. Как правило, критерии подбора профилей и шаблоны ответов используются те же, что и в authorize — чтобы их не повторять в конфигурации, рекомендуется применять такие элементы YAML-формата как якори и ссылки.

Ограничение количества одновременных сессий

Проверка количества сессий выполняется в разрезе профилей оборудования следующим образом:

  1. Разрешённое количество сессий определяется как наибольшее значение атрибута Simultaneous-Use среди подобранных абонентского и операторского профилей.
  2. К базе данных кэша делается запрос для подсчёта текущего количества активных сессий. Учитываются только сессии, привязанные к подобранным ранее профилям оборудования, и находящиеся в состоянии Начата или Обновлена.
  3. Если текущее количество сессий больше или равно разрешённому, запрос маркируется тегом simultaneous-limit-exceeded.

Проверка наличия данного может быть использована как для выбора шаблона ответа, так и в after-фильтрах. Это позволяет, например, не отказывать в авторизации «лишним» сессиям, а отправлять для них особый набор атрибутов.

Пример настройки аутентификации по логину и паролю с ограничением на количество одновременных сессий
plugins:
  base:
    l2tp-by-login:
      actions:
        authenticate:
          customer_profile:
            # Критерии подбора абонентского профиля идентичны таковым в authorize
            <<: *customer_profile
 
          provider_profile: {}

          reply:
            # Достигнуто ограничение на количество одновременных сессий
            - condition: $context.has_tag?('simultaneous-limit-exceeded')
              template:
                RAD_REPLY:
                  Reply-Message: '"You are already logged in"'

                result: $rlm.REJECT

            # Фиксированный внешний IP-адрес
            - condition: '    $customer_profile.attributes.try("Internet-Access-Service-Status") == "SERV_STATE_Provision"
                          and $customer_profile.attributes.Real-IP-Address.ip4?()'
              template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"300"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=REAL"'
                    - '"ip:ip-unnumbered=loopback221"'
                  Class: $customer_profile.attributes.L2TP-Class.coalesce("7")
                  Framed-IP-Address: $customer_profile.attributes.Real-IP-Address
                  Idle-Timeout: '"86400"'

                result: $rlm.OK

            # Динамический внутренний IP-адрес
            - condition: $customer_profile.attributes.try("Internet-Access-Service-Status") == "SERV_STATE_Provision"
              template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"300"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=NAT"'
                    - '"ip:ip-unnumbered=loopback220"'
                  Class: $customer_profile.attributes.L2TP-Class.coalesce("5")
                  Framed-Pool: '"NAT-1"'
                  Idle-Timeout: '"86400"'

                result: $rlm.OK

            # Доступ заблокирован (недостаточно средств)
            - template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"600"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=NOPAY"'
                    - '"ip:ip-unnumbered=loopback222"'
                  Cisco-Service-Info: '"QU;256000;9600;9600;D;256000;9600;9600"'
                  Framed-Pool: '"FREE-DNAT"'
                  Idle-Timeout: '"86400"'
                  mpd-limit:
                    - '"out#100=all shape 256000 pass"'
                    - '"in#100=all shape 256000 pass"'
                  Session-Timeout: '"86400"'
 
                result: $rlm.OK 

 

Пост-аутентификация — действие post_auth

Данный тип запросов, как правило, используется сервером FreeRADIUS только при работе в режиме DHCP-cервера. Также может применяться для выполнения дополнительных обработок ответа перед отправкой его сервером FreeRADIUS.

Примером такой обработки является вызов хука для отправки в очередь сообщения, содержащего динамический IP-адрес, выдаваемый сервером FreeRADIUS. Если пул динамических IP-адресов реализован на стороне RADIUS-сервера, то выдача адреса из такого пула выполняется сервером в начале этапа пост-аутентификации. В этот момент серверу уже известно, нужно ли выдавать динамический IP-адрес, и если нужно, то из какого пула.

Операции выполняются те же, что и в действии authorize:

  1. Подбор профилей оборудования.
  2. Формирование ответа.

Критерии подбора и шаблоны профилей настраиваются аналогично действию authorize.

Пример настройки пост-аутентификации — FreeRADIUS в роли DHCP-сервера с индентификацией по данным опции 82
plugins:
  base:
    dhcp-by-o82:
      actions:
        post_auth:
          provider_profile:
            set_by: search_cache
            query:
              Switch-IP: $request.RAD_REQUEST.DHCP-Relay-Remote-Id.substring(6).unhex()
              Switch-Port-Code: $request.RAD_REQUEST.DHCP-Relay-Circuit-Id.substring(12).to_i(16)

          customer_profile:
            set_by: set_first_from_provider_profile
            filter:
              Auth-Scheme: '"DHCP+L2TP"'

          reply:
            # Всё в порядке
            - condition: '    $customer_profile.present?()
                          and $customer_profile.attributes.DHCP-IP-Address.ip4?()
                          and $customer_profile.attributes.DHCP-Gateway-IP.ip4?()
                          and $customer_profile.attributes.DHCP-Subnet-Mask.ip4_mask?()'
              template:
                RAD_REPLY:
                  DHCP-Domain-Name-Server:
                    - '"192.168.11.15"'
                    - '"192.168.11.16"'
                  DHCP-IP-Address-Lease-Time: '"86400"'
                  DHCP-Router-Address: $customer_profile.attributes.DHCP-Gateway-IP
                  DHCP-Subnet-Mask: $customer_profile.attributes.DHCP-Subnet-Mask
                  DHCP-Your-IP-Address: $customer_profile.attributes.DHCP-IP-Address

                result: $rlm.OK
 
            # Профиль абонентского оборудования не подобрался - отказ
            - condition: $customer_profile.null?()
              template:
                RAD_REPLY: {}
                reject_reason: '"Customer equipment profile not found // Option 82"'
                result: $rlm.REJECT

            # В профиле нет маски/адреса/шлюза - отказ
            - template:
                RAD_REPLY: {}
                reject_reason: '"Not valid customer equipment profile // Option 82"'
                result: $rlm.REJECT

 

Аккаунтинг — действие accounting

Выделение первого пакета аккаунтинга по сессии

  • No labels