Undernet Radius: відмінності між версіями

Матеріал з NoDeny
Перейти до навігації Перейти до пошуку
Рядок 1: Рядок 1:
==sql==
<pre>
alter table ip_pool add guest varchar(128) not bull default '';
alter table ip_pool add key (guest);
</pre>
==Настройка NoDeny==
==Настройка NoDeny==



Версія за 12:32, 30 червня 2023

sql

alter table ip_pool add guest varchar(128) not bull default '';
alter table ip_pool add key (guest);

Настройка NoDeny

Установить модуль radius атрибутов

Создать тариф 1Мбит, параметр radius установить в:

ERX-Service-Activate:1+= "svc-global-ipoe(128000, 128000)"
ERX-Virtual-Router-Name = "brs"

Создать группу

  • имя: switch
  • галки только на:
    • К учетным записям подключаются ip, имеют трафик
    • Учетные записи имеют сущность «Порты»

Дополнительные поля

  • Имя поля: BRAS
  • Имя поля в бд: _bras
  • Тип поля: выпадающий список
  • Галки по усмотрению
  • Тип объекта: bras
  • Группы: switch

  • Имя поля: SVLAN
  • Имя поля в бд: _svlan
  • Тип поля: целое положительное
  • Группы: switch

  • Имя поля: MAC адрес
  • Имя поля в бд: _mac
  • Тип поля: мак
  • Группы: switch

Тестовые данные

Создаем switch:

  • MAC адрес: 001122334455
  • BRAS: lesnoy
  • SVLAN: 3571

Создаем клиента. Данные неважны. Добавляем услугу: 1 Мбит Добавляем ip: 10.0.0.5

Добавляем подключение:

  • мак: e89a8f2db9f3
  • Мак или идентификатор устройства - выбираем созданный switch
  • Порт устройства или vlan: 3019
  • ip: 10.0.0.5

Установка Radius

apt install freeradius freeradius-mysql
rm /etc/freeradius/3.0/sites-enabled/default
cp /usr/local/nodeny/etc/raddb/clients.conf /etc/freeradius/3.0/

Конфигурирование

nano /etc/freeradius/3.0/sites-enabled/nodeny

Вставляем следующий текст:

server default {
    listen {
        type = auth
        ipaddr = *
        port = 1812
    }
    listen {
        type = acct
        ipaddr = *
        port = 0
    }
    authorize {
        sql
        update control {
          Auth-Type := Accept
        }
    }
    authenticate {
    }
    preacct {
        acct_unique
        preprocess
    }
    accounting {
        sql
    }
    session {
        radutmp
        sql
    }
    post-auth {
        sql
    }
    Post-Auth-Type ACCEPT {
        sql
    }
}

Sql конфиг

В консоли:

nano /etc/freeradius/3.0/mods-enabled/sql

Вставляем следующий текст:

sql {
    driver = "rlm_sql_mysql"
    mysql {
        warnings = auto
    }
    server = "localhost"
    port = 3306
    login = "nodeny"
    password = "hardpass"
    radius_db = "nodeny"

    read_groups = no
    #authorize_check_query = "call radcheck('%{User-Name}')"
    authorize_reply_query = "call radreply('%{User-Name}')"
    accounting {
        reference = "%{tolower:type.%{Acct-Status-Type}.query}"
        type {
            start {
                query = "call radupdate('%{reply:Callback-Number}','%{Framed-IP-Address}',\
                        'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}')"
            }
            interim-update {
                query = "${..start.query}"
            }
            stop {
                query = "call radstop('%{reply:Callback-Number}', '%{Framed-IP-Address}')"
            }
        }
    }
    post-auth {
        query = "call radupdate('%{reply:Callback-Number}','%{reply:Framed-IP-Address}',\
                'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}')"
    }
}

Логика radreply

UserName приходит в закодированном виде: lesnoy|e89a.8f2d.b9f3|ps50:3571-3019

Здесь:

  • lesnoy - имя BRAS
  • e89a.8f2d.b9f3 - мак клиента
  • ps50:3571-3019 - параметры подключения, могут иметь формат:
    • aaa:bbb-ccc : bbb - верхний влан, ccc - нижний влан
    • aaa:bbb : bbb - верхний влан, ccc - нижний влан

Алгоритм:

  • В зависимости от формата подключения:
    • Если aaa:bbb-ccc, то из базы выбираем подключение где: BRAS.name=lesnoy, BRAS.svlan=bbb, USER.svlan=ccc
    • Если aaa:bbb, то из базы выбираем подключение где: BRAS.name=lesnoy, BRAS.svlan=bbb, USER.mac=e89a.8f2d.b9f3
  • Если подключение не найдено, то return svc-guest-ipoe(svc-filter-notregistered)
  • Если не подвязан ip, то return svc-guest-ipoe(svc-filter-disable)
  • В ответ добавляем строку: Framed-IP-Address = хх.хх.хх.хх
  • Если не подвязана услуга типа "интернет", то return svc-guest-ipoe(svc-filter-disable)
  • Если баланс меньше 0, то return svc-guest-ipoe(svc-filter-nomoney)
  • В ответ добавляем атрибуты из услуги

Mysql процедуры

ALTER DATABASE nodeny CHARACTER SET utf8 COLLATE utf8_general_ci;
DELIMITER $$
CREATE FUNCTION `strSplit`(x MEDIUMTEXT, delim MEDIUMTEXT, pos int) RETURNS mediumtext CHARSET utf8
    DETERMINISTIC
RETURN 
    TRIM(BOTH '\r' FROM TRIM(
        REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos), LENGTH(SUBSTRING_INDEX(x, delim, pos - 1)) + 1), delim, '')
    ))$$
DELIMITER ;

DROP PROCEDURE IF EXISTS `radreply`;
DELIMITER $$
CREATE PROCEDURE `radreply`(IN encoded_user_name VARCHAR(128))
BEGIN
    DECLARE nas_name VARCHAR(32);
    DECLARE usr_mac VARCHAR(12);
    DECLARE vlans VARCHAR(64);
    DECLARE svlan VARCHAR(6);
    DECLARE cvlan VARCHAR(6);
    DECLARE usr_id INT;
    DECLARE usr_ip VARCHAR(15) DEFAULT NULL;
    DECLARE add_attr MEDIUMTEXT;
    DECLARE line MEDIUMTEXT;
    DECLARE i INT DEFAULT 1;

    SELECT strSplit(encoded_user_name, '=7C', 1) INTO nas_name;
    SELECT REPLACE(strSplit(encoded_user_name, '=7C', 2), '.', '') INTO usr_mac;
    SELECT strSplit(encoded_user_name, '=7C', 3) INTO vlans;
    SELECT strSplit(vlans, ':', 2) INTO vlans;
    SELECT strSplit(vlans, '-', 1) INTO svlan;
    SELECT strSplit(vlans, '-', 2) INTO cvlan;

    IF( LENGTH(cvlan) > 0 ) THEN
        SELECT m.uid INTO usr_id
          FROM mac_uid m
          JOIN data0 d ON m.device_mac=d._mac
          WHERE d._bras=nas_name AND d._svlan=svlan AND m.device_port=cvlan;
    ELSE
        SELECT m.uid INTO usr_id
          FROM mac_uid m
          JOIN data0 d ON m.device_mac=d._mac
          WHERE d._bras=nas_name AND m.device_port=svlan AND m.mac=usr_mac;
    END IF;

    IF( usr_id IS NULL ) THEN
        SELECT CONCAT(',guest,guest_user=', encoded_user_name,',') INTO encoded_user_name;
        UPDATE ip_pool
           SET `release`=UNIX_TIMESTAMP()+3600, tags=encoded_user_name
           WHERE tags=encoded_user_name
              OR (`release`<UNIX_TIMESTAMP() AND tags LIKE '%,guest,%')
           ORDER BY tags=encoded_user_name DESC LIMIT 1;
        SELECT INET_NTOA(ip) INTO usr_ip
           FROM ip_pool
           WHERE tags=encoded_user_name;
        SELECT NULL,encoded_user_name,'Framed-IP-Address',usr_ip,'=';
        SELECT NULL,encoded_user_name,'ERX-Service-Activate:1','svc-guest-ipoe(svc-filter-notregistered)','+=';
    ELSE
      SELECT NULL,encoded_user_name,'Callback-Number',usr_id,'=';
      SELECT INET_NTOA(ip) INTO usr_ip FROM ip_pool
          WHERE uid = usr_id AND type='static' LIMIT 1;

      IF( usr_ip IS NOT NULL ) THEN
          SELECT NULL,encoded_user_name,'Framed-IP-Address',usr_ip,'=';
      END IF;

      SELECT radius_attr INTO add_attr FROM users_services
        WHERE uid=usr_id AND tags LIKE '%,inet,%' LIMIT 1;

      IF( add_attr IS NULL OR usr_ip IS NULL ) THEN
          SELECT NULL,encoded_user_name,'ERX-Service-Activate:1','svc-guest-ipoe(svc-filter-disable)','+=';
      ELSEIF( SELECT 1 FROM users WHERE id=usr_id AND balance < 0 ) THEN
          SELECT NULL,encoded_user_name,'ERX-Service-Activate:1','svc-guest-ipoe(svc-filter-nomoney)','+=';
      ELSE
          attr_loop: WHILE TRUE DO
            SELECT strSplit(add_attr, '\n', i) INTO line;
            IF LENGTH(line) = 0 OR i > 20 THEN LEAVE attr_loop; END IF;
            IF line LIKE '%+=%' THEN
                SELECT NULL,encoded_user_name,strSplit(line, '+=', 1),strSplit(line, '+=', 2),'+=';
            ELSEIF line LIKE '%=%' THEN
                SELECT NULL,encoded_user_name,strSplit(line, '=', 1),strSplit(line, '=', 2),'=';
            END IF;
            SET i = i + 1;
          END WHILE;
      END IF;

    END IF;
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `radupdate`;
DELIMITER $$
CREATE PROCEDURE `radupdate`(
    IN usr_id VARCHAR(20), IN ipa VARCHAR(16), IN properties VARCHAR(255))
BEGIN
    IF( SELECT 1 FROM ip_pool WHERE ip=INET_ATON(ipa) AND uid=usr_id ) THEN
        CALL set_auth(ipa, CONCAT('mod=dhcp;', REPLACE(properties,';','')));
        UPDATE mac_uid SET time=UNIX_TIMESTAMP() WHERE ip=INET_ATON(ipa) LIMIT 1;
    ELSE
        UPDATE users SET id=usr_id WHERE id=usr_id LIMIT 1;
    END IF;
END$$
DELIMITER ;


DROP PROCEDURE IF EXISTS `radstop`;
DELIMITER $$
CREATE PROCEDURE `radstop`(IN usr_id VARCHAR(20), IN ipa VARCHAR(16))
BEGIN
    DECLARE auth_prop VARCHAR(255);
    DECLARE start_auth INT UNSIGNED;
    SELECT i.properties, i.start INTO auth_prop, start_auth
        FROM v_ips i JOIN users u ON i.uid=u.id WHERE u.id=usr_id AND i.ip=ipa AND i.auth=1;
    IF( auth_prop IS NOT NULL ) THEN
        INSERT INTO auth_log (uid, ip, start, end, properties) VALUES
            (usr_id, INET_ATON(ipa), start_auth, UNIX_TIMESTAMP(), auth_prop);
        DELETE FROM auth_now WHERE ip=ipa LIMIT 1;
    END IF;
END$$
DELIMITER ;

Тестирование

Запускаем радиус в режиме вывода в консоль:

freeradius -X

В соседней консоли тестовый запрос:

radtest "lesnoy|e89a.8f2d.b9f3|ps50:3571-3019" '' 127.0.0.1 0 hardpass5

В ответ должны получить следующее:

    Callback-Number = "4"
    Framed-IP-Address = 10.0.0.5
    ERX-Service-Activate:1 = "svc-global-ipoe(128000, 128000)"

В админ интерфейсе в данных абонента напротив его ip должен появиться зеленый ключик.