Как настроить FreeRADIUS с модулем linelog для захвата и логирования внутреннего имени пользователя из запросов аутентификации EAP-TTLS/PAP вместо логирования только внешнего анонимного идентификатора?
Я настраиваю FreeRADIUS для проксирования запросов EAP-TTLS/PAP на RADIUS-сервер, который поддерживает только обычную аутентификацию PAP. Хотя проксирование и логирование работают корректно, текущая конфигурация linelog логирует только User-Name из внешнего туннельного запроса (который по умолчанию является ‘anonymous’ для клиентов Android). Мне нужно получить и залогировать фактическое имя пользователя, предоставленное внутри внутреннего туннеля для целей аутентификации.
Текущие файлы конфигурации:
sites-enabled/default:
listen {
type = auth
ipaddr = *
port = 1812
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
type = acct
ipaddr = *
port = 1813
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
type = auth
ipv6addr = ::
port = 1812
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
type = acct
ipv6addr = ::
port = 1813
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
authorize {
eap {
ok = return
}
pap
update control {
&Proxy-To-Realm := "authentik"
}
}
authenticate {
Auth-Type PAP {
pap
}
eap
}
post-auth {
log_access
update {
&reply: += &session-state:
}
if (&reply:EAP-Session-Id) {
update reply {
EAP-Key-Name := &reply:EAP-Session-Id
}
}
}
post-proxy {
eap
}
accounting {
log_accounting
}
}
mods-enabled/eap:
default_eap_type = ttls
timer_expire = 60
ignore_unknown_eap_types = no
max_sessions = ${max_requests}
tls-config tls-common {
private_key_file = /certs/live/{DOMAIN}/privkey.pem
certificate_file = /certs/live/{DOMAIN}/fullchain.pem
cipher_list = "DEFAULT"
cipher_server_preference = no
tls_min_version = "1.2"
tls_max_version = "1.3"
ecdh_curve = ""
cache {
enable = no
lifetime = 24
store {
Tunnel-Private-Group-Id
}
}
ocsp {
enable = no
override_cert_url = yes
url = "http://127.0.0.1/ocsp/"
}
}
ttls {
tls = tls-common
default_eap_type = pap
copy_request_to_tunnel = no
use_tunneled_reply = no
virtual_server = "default"
}
}
mods-enabled/linelog:
filename = ${logdir}/custom/log_access.log
permissions = 0600
reference = "messages.%{%{reply:Packet-Type}:-default}"
messages {
default = "timestamp=\"%t\" event=\"ACCESS-OTHER\" packet_type=\"%{Packet-Type}\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Access-Accept = "timestamp=\"%t\" event=\"ACCESS-ACCEPT\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Access-Reject = "timestamp=\"%t\" event=\"ACCESS-REJECT\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" reason=\"%{Module-Failure-Message}\""
}
}
linelog log_accounting {
filename = ${logdir}/custom/log_accounting.log
permissions = 0600
reference = "messages.%{%{Acct-Status-Type}:-default}"
messages {
default = "timestamp=\"%t\" event=\"ACCT-OTHER\" acct_status=\"%{Acct-Status-Type}\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\""
Start = "timestamp=\"%t\" event=\"ACCT-START\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\""
Stop = "timestamp=\"%t\" event=\"ACCT-STOP\" user=\"%{User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\" duration_sec=\"%{Acct-Session-Time}\""
Accounting-On = "timestamp=\"%t\" event=\"ACCT-ON\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Accounting-Off = "timestamp=\"%t\" event=\"ACCT-OFF\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
}
}
Текущие журналы:
log_access.log:
timestamp="Wed Nov 5 17:30:31 2025" event="ACCESS-ACCEPT" user="someuser" client_mac="" nas_id="" nas_ip=""
timestamp="Wed Nov 5 17:30:29 2025" event="ACCESS-ACCEPT" user="anonymous" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x"
log_accounting.log:
timestamp="Wed Nov 5 17:29:15 2025" event="ACCT-STOP" user="anonymous" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x" session_id="4DCA6606E70B1D31" duration_sec="3333"
timestamp="Wed Nov 5 17:30:34 2025" event="ACCT-START" user="anonymous" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x" session_id="02A4FDCAB6135746"
Какие конкретные изменения мне нужно внести в мою конфигурацию FreeRADIUS, чтобы захватывать и логировать внутреннее имя пользователя в журналах доступа и учета?
Для захвата и регистрации внутреннего имени пользователя из запросов аутентификации EAP-TTLS/PAP необходимо изменить конфигурацию FreeRADIUS, чтобы сохранить внутреннее имя пользователя в процессе аутентификации и правильно сослаться на него в конфигурации linelog.
Основная проблема заключается в том, что текущая конфигурация ссылается только на внешний атрибут User-Name, который для клиентов Android отображается как “anonymous”. Вам необходимо получить доступ к фактическому имени пользователя, предоставленному во внутреннем туннеле.
Содержание
- Понимание потока внутреннего/внешнего имени пользователя
- Необходимые изменения в конфигурации
- Модификация виртуального сервера inner-tunnel
- Обновление конфигурации модуля linelog
- Тестирование и проверка
- Альтернативные подходы
Понимание потока внутреннего/внешнего имени пользователя
В аутентификации EAP-TTLS существуют два различных потока имени пользователя:
-
Внешнее имя пользователя: Это идентификатор, отправляемый в открытом виде во время начального рукопожатия. Клиенты Android часто отправляют “anonymous” для обеспечения конфиденциальности.
-
Внутреннее имя пользователя: Это фактическое имя пользователя, отправляемое внутри зашифрованного туннеля TTLS во время внутреннего процесса аутентификации (в вашем случае - PAP).
FreeRADIUS обрабатывает их через два отдельных виртуальных сервера:
- Сервер
defaultобрабатывает внешнюю аутентификацию - Сервер
inner-tunnelобрабатывает внутреннюю аутентификацию
Внутреннее имя пользователя доступно на виртуальном сервере inner-tunnel, но его необходимо сохранить и передать обратно во внешнюю сессию для регистрации.
Необходимые изменения в конфигурации
Модификация виртуального сервера inner-tunnel
Сначала необходимо убедиться, что ваш виртуальный сервер inner-tunnel правильно настроен для захвата и сохранения внутреннего имени пользователя. Создайте или отредактируйте /etc/raddb/sites-enabled/inner-tunnel:
server inner-tunnel {
authorize {
# Ваши существующие модули authorize здесь
# Убедитесь, что аутентификация PAP включена
pap
}
authenticate {
Auth-Type PAP {
pap
}
}
post-auth {
# Обновите session-state для сохранения внутреннего имени пользователя
# Это делает внутреннее имя пользователя доступным во внешней сессии
update outer.session-state {
&User-Name := &User-Name
}
# Опционально: Отдельно зарегистрируйте внутреннюю аутентификацию
linelog inner_auth {
filename = ${logdir}/custom/inner_auth.log
reference = "messages.%{%{reply:Packet-Type}:-default}"
messages {
default = "timestamp=\"%t\" event=\"INNER-OTHER\" inner_user=\"%{User-Name}\" outer_user=\"%{outer.request:User-Name}\""
Access-Accept = "timestamp=\"%t\" event=\"INNER-ACCEPT\" inner_user=\"%{User-Name}\" outer_user=\"%{outer.request:User-Name}\""
Access-Reject = "timestamp=\"%t\" event=\"INNER-REJECT\" inner_user=\"%{User-Name}\" outer_user=\"%{outer.request:User-Name}\" reason=\"%{Module-Failure-Message}\""
}
}
# Продолжите с существующими модулями post-auth
}
}
Обновление конфигурации модуля linelog
Теперь измените конфигурацию log_access linelog, чтобы сослаться на сохраненное внутреннее имя пользователя:
linelog log_access {
filename = ${logdir}/custom/log_access.log
permissions = 0600
reference = "messages.%{%{reply:Packet-Type}:-default}"
messages {
default = "timestamp=\"%t\" event=\"ACCESS-OTHER\" packet_type=\"%{Packet-Type}\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Access-Accept = "timestamp=\"%t\" event=\"ACCESS-ACCEPT\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Access-Reject = "timestamp=\"%t\" event=\"ACCESS-REJECT\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" reason=\"%{Module-Failure-Message}\""
}
}
Аналогично обновите вашу конфигурацию log_accounting:
linelog log_accounting {
filename = ${logdir}/custom/log_accounting.log
permissions = 0600
reference = "messages.%{%{Acct-Status-Type}:-default}"
messages {
default = "timestamp=\"%t\" event=\"ACCT-OTHER\" acct_status=\"%{Acct-Status-Type}\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\""
Start = "timestamp=\"%t\" event=\"ACCT-START\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\""
Stop = "timestamp=\"%t\" event=\"ACCT-STOP\" user=\"%{outer.session-state:User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" session_id=\"%{Acct-Session-Id}\" duration_sec=\"%{Acct-Session-Time}\""
Accounting-On = "timestamp=\"%t\" event=\"ACCT-ON\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Accounting-Off = "timestamp=\"%t\" event=\"ACCT-OFF\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
}
}
Модификация конфигурации внешнего сервера
Обновите ваш сервер default для правильной обработки обновлений состояния сессии:
server default {
# ... существующие конфигурации прослушивания ...
authorize {
eap {
ok = return
}
pap
update control {
&Proxy-To-Realm := "authentik"
}
}
authenticate {
Auth-Type PAP {
pap
}
eap
}
post-auth {
# Обновите session-state для включения внутреннего имени пользователя
update session-state {
&Tmp-String-1 := "accept"
}
# Это скопирует внутреннее имя пользователя во внешнюю сессию
if (&reply:EAP-Session-Id) {
update reply {
EAP-Key-Name := &reply:EAP-Session-Id
}
}
log_access
# Это гарантирует, что внутреннее имя пользователя доступно для конечного ответа
update {
&reply: += &session-state:
}
}
post-proxy {
eap
}
accounting {
# Обновите учет для использования внутреннего имени пользователя
update request {
User-Name := "%{outer.session-state:User-Name}"
}
log_accounting
}
}
Тестирование и проверка
После внесения этих изменений перезапустите FreeRADIUS и протестируйте аутентификацию. Ваши журналы теперь должны отображать внутреннее имя пользователя вместо “anonymous”:
Ожидаемый вывод в журнале:
# log_access.log
timestamp="Wed Nov 5 17:30:31 2025" event="ACCESS-ACCEPT" user="actual_user" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x"
timestamp="Wed Nov 5 17:30:29 2025" event="ACCESS-ACCEPT" user="actual_user" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x"
# log_accounting.log
timestamp="Wed Nov 5 17:29:15 2025" event="ACCT-STOP" user="actual_user" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x" session_id="4DCA6606E70B1D31" duration_sec="3333"
timestamp="Wed Nov 5 17:30:34 2025" event="ACCT-START" user="actual_user" client_mac="someid" nas_id="someid" nas_ip="10.0.5.x" session_id="02A4FDCAB6135746"
# Опциональный inner_auth.log
timestamp="Wed Nov 5 17:30:31 2025" event="INNER-ACCEPT" inner_user="actual_user" outer_user="anonymous"
Альтернативные подходы
Если приведенный выше подход не работает, попробуйте эти альтернативы:
Метод 1: Использование User-Name с nostrip
Убедитесь, что ваша конфигурация realm включает опцию nostrip для сохранения полного имени пользователя:
# В realms или sites-available/default
realm example.com {
authhost = localhost
accthost = localhost
nostrip = yes
}
Метод 2: Использование Inner-Tunnel-User-Name
Некоторые версии FreeRADIUS поддерживают атрибут Inner-Tunnel-User-Name:
linelog log_access {
# ... существующая конфигурация ...
messages {
Access-Accept = "timestamp=\"%t\" event=\"ACCESS-ACCEPT\" user=\"%{Inner-Tunnel-User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
Access-Reject = "timestamp=\"%t\" event=\"ACCESS-REJECT\" user=\"%{Inner-Tunnel-User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\" reason=\"%{Module-Failure-Message}\""
}
}
Метод 3: Использование разбора EAP-Message
В качестве запасного варианта можно разобрать EAP-Message для извлечения внутреннего имени пользователя:
# В разделе post-auth
update control {
&Inner-User-Name := "%{mschap:User-Name}"
}
linelog log_access {
messages {
Access-Accept = "timestamp=\"%t\" event=\"ACCESS-ACCEPT\" user=\"%{control:Inner-User-Name}\" client_mac=\"%{Calling-Station-Id}\" nas_id=\"%{NAS-Identifier}\" nas_ip=\"%{NAS-IP-Address}\""
}
}
Не забывайте тщательно тестировать после каждого изменения конфигурации и следить за журналами FreeRADIUS на предмет ошибок или сбоев аутентификации. Ключевым является обеспечение сохранения внутреннего имени пользователя через всю цепочку аутентификации и правильная ссылка на него в ваших конфигурациях журналирования.
Источники
- Документация FreeRADIUS - inner-tunnel
- Вики FreeRADIUS - ведение журнала eduroam
- Документация FreeRADIUS - модуль linelog
- Конфигурация EAP-TTLS FreeRADIUS
- Руководство по конфигурации EAP FreeRADIUS
- Обработка имени пользователя inner-tunnel FreeRADIUS
- Захват внутреннего имени пользователя EAP-TTLS FreeRADIUS
Заключение
Чтобы успешно регистрировать внутреннее имя пользователя из аутентификации EAP-TTLS/PAP в FreeRADIUS, вам необходимо:
-
Настроить виртуальный сервер inner-tunnel для захвата и сохранения внутреннего имени пользователя с помощью
update outer.session-state { &User-Name := &User-Name } -
Обновить ваши конфигурации linelog для ссылки на сохраненное внутреннее имя пользователя с помощью
%{outer.session-state:User-Name}вместо просто%{User-Name} -
Изменить разделы post-auth и accounting внешнего сервера для правильной обработки обновлений состояния сессии
-
Тщательно протестировать для правильной работы потока имени пользователя и его появления в журналах
Ключевое понимание заключается в том, что FreeRADIUS поддерживает отдельные пространства атрибутов для внутренней и внешней аутентификации, и вам явно нужно скопировать внутреннее имя пользователя во внешнее состояние сессии для целей ведения журналов. Этот подход обеспечивает безопасность при предоставлении подробных журналов аутентификации, необходимых для мониторинга и устранения неполадок.