Виникло завдання зробити зовнішлю авторизацію через LDAP для сервіса, який її не підтримував, зате підтримував авторизацію через Radius. А у radius’a виявився модуль LDAP.
Тестовий стенд: Debian Linux 11.6 + Freeradius.
Нам знадобляться пакети freeradius-ldap, freeradius-utils (для локального тесту через утиліту radtest).
Файл clients.conf (описуються клієнти, які будуть підключатися, на кожного клієнта окрема секція)
client srv_auth { ipaddr = X.X.X.X secret = supersecretpass }
Якщо ви невірно напишите secret на клієнті, то в логах (якщо ввімкнути debug паролів) паролі будуть ієрогліфами. Інакше – у відкритому виглядя. Це зовсім не означає, що паролі передаються у відкритому вигляді, вони шифруються цим secret’ом на клієнті і розшифровуються на сервері. Якщо secret не співпадає, то розшифрований пароль виглядає як ієрогліф.
Файл dictionary (типи авторизації, які будемо використовувати)
VALUE Auth-Type LDAP 5
Робимо symlink: mods-available/ldap -> mod-enable/ldap і міняємо таке (бо там багато чого по замовчуванню):
ldap { server = 'ldaps://ldap.domain.com:636' port = 636 identity = 'cn=radius,ou=services,dc=domain,dc=com' password = 'megapass' base_dn = 'dc=domain,dc=com' sasl { } ... user_dn = "LDAP-UserDn" user { base_dn = "${..base_dn}" filter = "(&(objectClass=gosaMailAccount)(uid=%{%{Stripped-User-Name}:-%{User-Name}})(!(sn=blocked_*)))" sasl { } scope = 'sub' } group { base_dn = "${..base_dn}" filter = '(objectclass=organizationalRole)' scope = 'sub' name_attribute = cn membership_filter = "(roleOccupant=uid=%{%{Stripped-User-Name}:-%{User-Name}},ou=people,dc=domain,dc=com)" membership_attribute = 'memberOf' } ... tls { require_cert = 'never' } ...
Трохи поясню. Заблоковані користувачі у LDAP у мене додатково мають фільтр ‘sn=blocked_’, тому їх треба теж враховувати. Додатково, треба видавати доступи на основі груп, тобто, якщо користувач є у групі developer або vpn – дати доступ. Нагадаю, що належність користувача до груп у LDAP’і має 2 реалізації: користувачі описані в самій групі, або користувач має опис відвовідних груп. У мене перший варіант. Якщо у вас другий – то треба змінити membership_attribute. Умови належності до певних груп будуть далі. Тут лише те, що стосується користувача.
Файл users
DEFAULT Auth-Type := LDAP Fall-Through = 1 DEFAULT LDAP-Group == "developer" DEFAULT Auth-Type := Reject Reply-Message = "Sorry, you're not part of an authorized group."
Файл sites-enabled/default
authorize { ... ldap if ((ok || updated) && User-Password) { update { control:Auth-Type := ldap } } ... } ... authenticate { ... Auth-Type LDAP { ldap } ... } ... post-auth { if (LDAP-Group == "developer") { noop } elsif (LDAP-Group == "vpn") { noop } else { reject } ...
Ось тут влане і описуються всі 2 групи, які успішно авторизуються.
На цьому з конфігами все. Для тестування авторизації можна застосувати radtest з пакету freeradius-utils:
$ radtest -x testsuser MegaSuperveryPassword 127.0.0.1 2 testing123
Для debug режиму рекомендую вимкнути сервіс і запустити вручну:
# freeradius -X
Ось, як виглядає, коли користувач не в групі:
rlm_ldap (ldap): Released connection (1) (0) [ldap] = ok (0) } # Auth-Type LDAP = ok (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) if (LDAP-Group == "developer") { (0) Searching for user in group "developer" rlm_ldap (ldap): Reserved connection (0) (0) Using user DN from request "uid=testsuser,ou=people,dc=domain,dc=com" (0) Checking for user in group objects (0) EXPAND (&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=%{%{Stripped-User-Name}:-%{User-Name}},ou=people,dc=domain,dc=com)) (0) --> (&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com)) (0) Performing search in "dc=domain,dc=com" with filter "(&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com))", scope "sub" (0) Waiting for search result... (0) Search returned no results (0) Checking user object's memberOf attributes (0) Performing unfiltered search in "uid=testsuser,ou=people,dc=domain,dc=com", scope "base" (0) Waiting for search result... (0) No group membership attribute(s) found in user object rlm_ldap (ldap): Released connection (0) (0) User is not a member of "developer" (0) if (LDAP-Group == "developer") -> FALSE (0) else { (0) [reject] = reject (0) } # else = reject (0) } # post-auth = reject
А ось так, коли є:
rlm_ldap (ldap): Released connection (0) (0) [ldap] = ok (0) } # Auth-Type LDAP = ok (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) if (LDAP-Group == "developer") { (0) Searching for user in group "developer" rlm_ldap (ldap): Reserved connection (1) (0) Using user DN from request "uid=testsuser,ou=people,dc=domain,dc=com" (0) Checking for user in group objects (0) EXPAND (&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=%{%{Stripped-User-Name}:-%{User-Name}},ou=people,dc=domain,dc=com)) (0) --> (&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com)) (0) Performing search in "dc=domain,dc=com" with filter "(&(cn=developer)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com))", scope "sub" (0) Waiting for search result... (0) Search returned no results (0) Checking user object's memberOf attributes (0) Performing unfiltered search in "uid=testsuser,ou=people,dc=domain,dc=com", scope "base" (0) Waiting for search result... (0) No group membership attribute(s) found in user object rlm_ldap (ldap): Released connection (1) (0) User is not a member of "developer" (0) if (LDAP-Group == "developer") -> FALSE (0) elsif (LDAP-Group == "vpn") { (0) Searching for user in group "vpn" rlm_ldap (ldap): Reserved connection (0) (0) Using user DN from request "uid=testsuser,ou=people,dc=domain,dc=com" (0) Checking for user in group objects (0) EXPAND (&(cn=vpn)(objectclass=organizationalRole)(roleOccupant=uid=%{%{Stripped-User-Name}:-%{User-Name}},ou=people,dc=domain,dc=com)) (0) --> (&(cn=vpn)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com)) (0) Waiting for bind result... (0) Bind successful (0) Performing search in "dc=domain,dc=com" with filter "(&(cn=vpn)(objectclass=organizationalRole)(roleOccupant=uid=testsuser,ou=people,dc=domain,dc=com))", scope "sub" (0) Waiting for search result... (0) User found in group object "dc=domain,dc=com" rlm_ldap (ldap): Released connection (0) (0) elsif (LDAP-Group == "vpn") -> TRUE (0) elsif (LDAP-Group == "vpn") { (0) [noop] = noop (0) } # elsif (LDAP-Group == "vpn") = noop