FreeRADIUS

FreeRADIUS ist eine weit verbreitete Open-Source-Software, die die Authentifizierung der Nutzenden durchführt.

Diese Anleitung nutzt FreeRADIUS 3.0.21 auf Debian 11. Bitte überprüfen Sie also ggf. Inkompatibiltäten zwischen der von Ihnen eingesetzten FreeRADIUS-Version.

Die Installation von FreeRADIUS geschieht auf Debian/Ubuntu über apt install freeradius. Wenn Sie die LDAP-Anbindung von FreeRADIUS nutzen möchten, müssen Sie ggf. noch das Paket freeradius-ldap zusätzlich installieren.

Die Konfigurationsdateien von FreeRADIUS liegen standardmäßig in /etc/freeradius/3.0

Im Ordner sites-available liegen die Standard-Sites bzw. -Server von FreeRADIUS. Ein Server führt dabei immer Authentifizierung und Autorisierung der anfragenden Nutzenden durch und nutzt dafür verschiedene Module.

Die Konfiguration der Server gliedert sich dabei in verschiedene Bereiche:

  • listen In dieser Sektion wird konfiguriert, auf welchen IP-Adressen und Ports der Server auf Verbindungen hört.
  • authorize Diese Sektion konfiguriert die Autorisierung, also die Feststellung ob und wenn ja welche Authentifizierung durchgeführt wird.
  • authenticate In dieser Sektion sind die Authentifizierungsmethoden konfiguriert.
  • preacct/accounting/session Diese Sektionen sind für die Behandlung von Accounting-Anfragen zuständig. Auf Accounting werden wir in diesen Anleitungen nicht eingehen.
  • post-auth In dieser Sektion werden Schritte nach einer erfolgen Authentifizierung konfiguriert.
  • pre-proxy/post-proxy Diese Sektionen sind für die Behandlung von Requests zuständig, die an andere RADIUS-Server weitergeleitet werden.

Jede Site/Server hat ein Label, über das er referenziert wird. Dieser Name wird am Anfang konfiguriert.

server default {
    listen {
        [...]
    }
    authorize {
        [...]
    }
    authenticate {
        [...]
    }
    [...]
}

bzw. für inner-tunnel

server inner-tunnel {
    [...]
}

Dieser Server ist für alle Authentifizierungen zuständig (auch für Roaming-Requests). Für eigene Nutzende ist dieser Server dafür zuständig, den TLS-Tunnel aufzubauen, damit dann im Tunnel die Authentifizierung durchgeführt werden kann.

listen-Sektion

Die listen-Sektion legt fest, auf welchen IP-Adressen und Ports der Server auf Anfragen antworten soll. Hierbei muss für jedes Protokoll (Authentifizierung oder Accounting) und für jede Adressfamilie (IPv4/IPv6) bzw. ggf. sogar jede IP-Adresse ein eigener listen-Block angelegt werden. Diese Anleitung wird sich nur auf den Authentifizierungs-Teil beschränken und kein Accounting durchführen.

In jedem listen-Block müssen die folgenden Konfigurationen gesetzt werden:

  • type: Dies kann entweder auth für Authentifizierung oder acct für Accounting sein. Da wir uns nur mit Authentifizierung beschäftigen, wird es immer auf auth gesetzt sein.
  • ipaddr: Die IP-Adresse des Hosts, auf der Verbindungen angenommen werden sollen. Mit * werden auf allen IPv4-Adressen des Hosts Verbindungen angenommen. Für verschiedene IP-Adressen muss jeweils ein eigener listen-Block angelegt werden, wenn nicht auf allen IPv4-Adressen Verbindungen angenommen werden sollen.
    • Um die IP-Adress-Familie zu spezifizieren kann stattdessen ein anderer Konfigurationsparameter benutt werden:
    • ipv4addr: spezielle IPv4-Adresse des Servers, alternativ * für alle IPv4-Adressen des Servers
    • ipv6addr: spezielle IPv6-Adresse des Servers, alternativ * für alle IPv6-Adressen des Servers
    • Wichtig: Sollten mehrere IP-Adress-Konfigurationen im listen-Block existieren, wird nur die erste verwendet. Für verschiedene Konfigurationen je nach IP-Adresse muss also jeweils ein eigener listen-Block angelegt werden.
  • port: Port, auf dem Verbindungen engegengenommen werden. Standard für RADIUS ist Port 1812. Mit 0 wird der Standardport ausgewählt

Beispiel:

server default {
    listen {
        type = auth
        ipv4addr = 203.0.113.1
        port = 0
    }
    listen {
        type = auth
        ipv6addr = 2001:db8:0:203::1
        port = 0
    }
    [...]
}

authorize-Sektion

In der authorize-Sektion ist konfiguriert, welche Autorisierungsmechanismen verwendet werden. FreeRADIUS trennt hier Autorisierung (Wer darf was?) von Authentifizierung (Ist das auch wirklich der:die Anfragende?). In der authorize-Sektion wird also zunächst nur festgelegt, ob und wenn ja auf welche Arten sich das anfragende Gerät authentifizieren darf.

Hierfür stellt FreeRADIUS ein paar vordefinierte Filter und Methoden zur Verfügung, um ungültige Kennungen schon früh abzulehnen. Die folgenden Optionen sollten in der authorize-Sektion vorhanden sein:

  • filter_username: Eine Policy, die den Usernamen auf korrektes Format prüft. Die entsprechenden Konfigurationen finden Sie in der Datei policy.d/filter
  • suffix: Setzt die Realm des Usernamens für die folgende Routing-Entscheidung. Dies setzt voraus, dass der Username das Format <username>@<realm> hat.
  • eap: Dies konfiguriert, dass zur Authentifizierung das EAP-Protokoll genutzt werden soll.

Ebenfalls sollte in diesem Teil bereits eine Prüfung stattfinden, ob eine Realm gesetzt wurde, da Nutzerkennungen ohne Realm im eduroam unzulässig sind und zu inkonsistenten Konfigurationen führen, die auf dem lokalen Campus funktionieren, aber nicht an anderen Einrichtungen. Wichtig: Diese Konfiguration muss nach suffix auftauchen, da hier erst das Realm-Attribut gesetzt wird.pr

if (!&Realm) {
    reject
}

Da im Default-Server noch keine Authentifizierung stattfindet, muss hier keine weitere Konfiguration (LDAP/MySQL/…) eingebunden werden. Die finale Konfiguration sollte dann wie folgt aussehen:

server default {
    [...]
    authorize {
        filter_username
        suffix
        if (!&Realm) {
            reject
        }
        eap {
            ok = return
        }
    }
    [...]
}

authenticate-Sektion

In der authenticate-Sektion wird die Authentisierung der Geräte vorgenommen. Da im eduroam lediglich EAP genutzt wird, beinhaltet die Konfiguration auch nur den Verweis auf EAP.

server default {
    [...]
    authenticate {
        eap
    }
    [...]
}

Accounting-Sektionen (preacct/accounting/session)

Die Sektionen preacct, accounting und session sind für die Behandlung von Accounting-Anfragen zuständig. Da wir kein Accounting konfigurieren, können wir diese Sektionen auslassen bzw. komplett auskommentieren.

post-auth-Sektion

Die post-auth-Sektion ist für die Behandlung von Requests nach der Authentifizierung zuständig. Hierbei gibt es die Haupt-Sektion, die die laufenden und erfolgreichen Requests abhandelt, sowie eine Sub-Sektion Post-Auth-Type REJECT, die für die abgelehnten Requests zuständig ist.

Zunächst muss mit dem folgenden Block der Zwischenstatus der Anfragen gespeichert werden, die noch nicht abgehandelt sind:

if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) {
    update reply {
        &User-Name !* ANY
    }
}
update {
    &reply: += &session-state:
}

Zudem muss eine mögliche Reply-Nachricht entfernt werden, wenn eine EAP-Nachricht gesendet wird:

remove_reply_message_if_eap

In dieser Sektion kann auch zusätzliches Logging konfiguriert werden, wie z.B. das von uns empfohlene Linelog-Modul.

outer_linelog

Für Rejects muss die Subsektion Post-Auth-Type REJECT genutzt werden. Hierin werden zusätzlich noch Attribute gefiltert, da bei Rejects nur bestimmte RADIUS-Attribute mitgeschickt werden sollen.

Post-Auth-Type REJECT {
    # Logging des Rejects
    outer_linelog
    # Filtern von Attributen
    attr_filter.access_reject
    # Nutzung von EAP für die Reject-Nachricht
    eap
    # Reply-Nachricht entfernen, falls es eine EAP-Nachricht ist
    remove_reply_message_if_eap
}

Die gesamte post-auth Sektion sieht damit wie folgt aus:

server default {
    [...]
    post-auth {
        if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) {
            update reply {
                &User-Name !* ANY
            }
        }
        update {
            &reply: += &session-state:
        }
        outer_linelog
        remove_reply_message_if_eap

        Post-Auth-Type REJECT {
            outer_linelog
            attr_filter.access_reject
            eap
            remove_reply_message_if_eap
        }
    }
    [...]
}

Proxy-Sektionen (pre-proxy/post-proxy)

Die Sektionen pre-proxy und post-proxy sind für Konfiguration speziell für Requests gedacht, die an andere Server weitergeleitet werden sollen. Hierbei ist im Normalfall keine weitere Konfiguration nötig, lediglich im post-proxy muss eap gelistet sein:

server default {
    [...]
    pre-proxy {
    }
    post-proxy {
        eap
    }
}

Komplette Beispielkonfiguration für sites-available/default

server default {
    listen {
        type = auth
        ipv4addr = 203.0.113.1
        port = 0
    }
    authorize {
        filter_username
        suffix
        if (!&Realm) {
            reject
        }
        eap {
            ok = return
        }
    }
    authenticate {
        eap
    }
    post-auth {
        if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) {
            update reply {
                &User-Name !* ANY
            }
        }
        update {
            &reply: += &session-state:
        }
        outer_linelog
        remove_reply_message_if_eap

        Post-Auth-Type REJECT {
            outer_linelog
            attr_filter.access_reject
            eap
            remove_reply_message_if_eap
        }
    }
    pre-proxy {
    }
    post-proxy {
        eap
    }
}

Der Server inner-tunnel ist für die Authentifizierung der lokalen Nutzenden zuständig und wird innerhalb des TLS-Tunnels, der durch EAP-TTLS/EAP-PEAP aufgebaut wurde, aufgerufen. In diesem Tunnel wird von den Nutzenden die tatsächliche Kennung und, je nach verwendeter Methode, das Passwort oder der MS-CHAP-v2 Handshake übermittelt.

Die Konfiguration des inner Tunnel benötigt im Gegensatz zum default-Server lediglich die Sektionen authorize und authenticate.

authorize-Sektion

In der Authorize-Sektion muss, wie beim default-Server, zunächst die Form des Usernamens überprüft werden.

filter_username
suffix

Zusätzlich muss hier nun allerdings konfiguriert werden, dass, auch wenn die Realm nicht lokal ist, der Request nicht weitergeleitet werden soll. Andernfalls könnte ein Request mit dem äußeren Usernamen anonymous@my-realm.tld und innerem Usernamen something@other-realm.tld im inneren Tunnel nochmals weitergeleitet werden.

update control {
    &Proxy-To-Realm := LOCAL
}

Hier wird nun auch die Datenherkunft für die Nutzer-Accounts konfiguriert. In unserem Fall gehen wir von einem LDAP-Backend aus. Alternativ können die User-Accounts auch in einer SQL-Datenbank oder in einer Datei verwaltet werden

ldap # Für ein LDAP-Backend
sql # Für ein SQL-Backend
files # Für ein Datei-Backend

Final müssen die Authentifizierungsmethoden vorbereitet werden. Hier ist die Konfiguration nun abhängig davon, in welcher Form das Passswort vorliegt. Soll MS-CHAP-v2 genutzt werden, muss hier mschap eingebunden werden. Das Modul pap sollte immer eingebunden werden, da es die vorliegenden Passwörter in die benötigte Form bringt. Soll im Tunnel EAP genutzt werden (z.B. bei PEAP), muss hier auch EAP konfiguriert werden. Damit im Inneren Tunnel nicht nochmals TTLS/PEAP gesprochen werden kann, wird hier ein anderer EAP-Kontext genutzt, in dem dann Passwort-basierte Protokolle (PAP/GTC) genutzt werden können.

Damit sieht die authorize-Sektion wie folgt aus (Bsp für LDAP-Backend mit PAP):

server inner-tunnel {
    authorize {
        filter_username
        suffix
        update control {
            &Proxy-To-Realm := LOCAL
        }
        ldap
        inner_eap {
            ok = return
        }
        pap
    }
    [...]
}

authenticate-Sektion

Hier werden nun die in der authorize-Sektion konfigurierten Authentifizierungsmechanismen ausgeführt. Die Konfiguration hierfür ist recht simpel.

server inner-tunnel {
    [...]
    authenticate {
        Auth-Type PAP {
            pap
        }
        # Den folgenden Block nur nutzen, wenn das Passwort im Klartext oder als NT-Hash vorliegt.
        # Sonst wird MS-CHAP nicht funktionieren.
        Auth-Type MS-CHAP {
            mschap
        }
        Auth-Type inner_eap {
            inner_eap
        }
    }
    [...]
}

post-auth-Sektion

Wie beim default-Server wird auch hier in der post-auth-Sektion die finale Antwort nochmals abgearbeitet.

Zunächst fügen wir hier auch wieder ein Logging hinzu, damit wir auch den inneren Usernamen loggen. Hierfür benutzen wir ebenfalls das Linelog-Modul, allerdings mit einer anderen Konfiguration.

inner_tunnel_linelog

Die abgelehnten Requests werden ebenfalls behandelt. Zunächst werden auch diese Requests geloggt und, wie im default-Server, werden bestimmte Attribute gefiltert.

Post-Auth-Type REJECT {
    inner_tunnel_linelog
    attr_filter.access_reject
    [...]
}

Um bei der Fehlersuche zu helfen, kann zusätzlich noch die Fehlermeldung des Moduls in die äußere Authentifizierung übertragen werden. Diese Information wird im äußeren Tunnel für das Logging verwendet.

update outer.session-state {
    &Module-Failure-Message := &request:Module-Failure-Message
}

Komplette Beispielkonfiguration für sites-available/inner-tunnel

Dieses Beispiel geht von einer LDAP-Konfiguration mit PAP als innerer Authentifizierung aus. Da im inneren Tunnel keine Requests weitergeleitet werden, können die Sektionen pre-proxy/post-proxy entfallen.

server inner-tunnel {
    authorize {
        filter_username
        suffix
        update control {
            &Proxy-To-Realm := LOCAL
        }
        ldap
        eap {
            ok = return
        }
        pap
    }

    authenticate {
        Auth-Type PAP {
            pap
        }
        Auth-Type EAP {
            eap
        }
    }

    post-auth {
        inner_tunnel_linelog

        Post-Auth-Type REJECT {
            inner_tunnel_linelog
            attr_filter.access_reject
            update outer.session-state {
                &Module-Failure-Message := &request:Module-Failure-Message
            }
        }
    }
}

Die Seiten in sites-available/ sind nicht automatisch aktiv. Zur Aktivierung muss im Ordner sites-enabled/ ein Symlink auf die Datei in sites-available/ gesetzt werden. Für die Seiten default und inner-tunnel sollten diese Links in der voreingestellten Konfiguration schon existieren.

Im Ordner mods-available bzw. mods-config liegen die Konfigurationsdateien der einzelnen FreeRADIUS-Module. In dieser Anleitung werden die Module eap, ldap und linelog behandelt. Erweiterte Konfiguration über zusätzliche Module wird ggf. in anderen Anleitungsseiten erklärt.

Das EAP-Modul ist für die Behandlung des EAP-Traffics zuständig. Hier werden alle genutzten EAP-Methoden konfiguriert. Es gibt die Möglichkeit, mehrere EAP-Instanzen zu konfigurieren, z.B. um das innere EAP vom äußeren EAP zu unterscheiden.

Allgemeine EAP-Konfiguration

Zunächst muss das allgemeine EAP-Modul konfiguriert werden. Hierfür sollte der Default-EAP-Type gesetzt werden. Geschieht dies nicht, wird jede Authentifizierung um einen Roundtrip erweitert, weil Client und Server erst die korrekte EAP-Methode aushandeln müssen. Die Option sollte also auf die in Anleitungen präferierte EAP-Methode (ttls oder peap) gesetzt werden.

default_eap_type = ttls

Um möglichst viele gleichzeitige Authentifizierungen zuzulassen, sollte zudem max_sessions auf den Standardwert aus der globalen Konfiguration gesetzt werden. Bei dem Standardwert von 256 kann es bei Lastspitzen sonst zu Engpässen kommen.

max_sessions = ${max_requests}

TLS-Konfiguration

Die TLS-Konfiguration geschieht in einem tls-config-Block.

Zunächst müssen die Zertifikats-Dateien konfiguriert werden. Die Datei mit dem Zertifikat sollte dabei auch die Zertifikatskette (in umgekehrter Reihenfolge, d.h. zuerst das Server-Zertifikat, dann die Zertifikatskette vom letzten zum ersten Intermediate-Zertifikat) ohne das Root-Zertifikat beinhalten. Das Root-Zertifikat ist in den Clients konfiguriert und muss nicht übermittelt werden.

Die Zertifikate müssen dabei im Ordner certs/ liegen und müssen für den User freerad lesbar sein.

tls-config tls-common {
    certificate_file = ${certdir}/name-of-certificate-file.pem
    private_key_file = ${certdir}/name-of-private-key-file.pem
    [...]
}

Für den Schlüsselaustausch per Diffie-Hellman müssen Parameter generiert werden. Hierfür empfehlen wir 2048 bit-Schlüssel. Bei Installation generiert FreeRADIUS 1024bit Schlüssel, es müssen also neue Parameter generiert werden:

$> openssl dhparam -out certs/dh 2048

Die so erzeugte Datei wird im TLS-Block referenziert:

tls-config tls-common {
    [...]
    dh_file = ${certdir}/dh
    [...]
}

Final können noch Einstellungen zu den unterstützten Cipher Suites und Elliptischen Kurven für den Schlüsselaustausch gesetzt werden. Die empfohlene Cipher-List deaktiviert alle Cipher Suites, die kein Forward Secrecy unterstützen sowie Cipher Suites, die für EAP-TTLS/EAP-PEAP nicht nutzbar sind. Mit dem Setzen der ecdh_curve-Option werden alle aktuellen ECDHE-Kurven aktiviert, statt ausschließlich die Kurve prime256v1.

tls-config tls-common {
    [...]
    cipher_list = "DEFAULT !kRSA !PSK !RSP !SSLv3"
    ecdh_curve = ""
}

Konfiguration der EAP-Methoden

Als EAP-Methoden können EAP-TTLS oder EAP-PEAP zum Einsatz kommen.

Die Konfiguration beinhaltet einen Verweis auf die zu nutzende TLS-Konfiguration sowie auf den zu verwendenden Server für den inneren Tunnel. Ebenfalls sollte die Konfiguration eine Default-EAP-Methode für die Nutzung im inneren Tunnel beinhalten.

ttls {
    tls = tls-common
    default_eap_type = gtc
    virtual_server = "inner-tunnel"
}
peap {
    tls = tls-common
    default_eap_type = gtc
    virtual_server = "inner-tunnel"
}

Konfiguration der inneren EAP-Methode

Die innere EAP-Methode wird genutzt, wenn im TTLS/PEAP-Tunnel nochmals EAP gesprochen wird.

Die Konfiguration hierfür kann minimal sein, da hier lediglich GTC bzw. MS-CHAP-v2 konfiguriert werden müssen.

eap inner_eap {
    default_eap_type = gtc
    gtc { }
    # Den folgenden Block wieder nur bei Nutzung von MS-CHAP-v2
    mschapv2 { }
}

Komplette Beispielkonfiguration für mods-available/eap

eap {
    default_eap_type = ttls
    max_sessions = ${max_requests}
    tls-config tls-common {
        certificate_file = ${certdir}/name-of-certificate-file.pem
        private_key_file = ${certdir}/name-of-private-key-file.key
        dh_file = ${certdir}/dh
        cipher_list = "DEFAULT !kRSA !PSK !SRP !SSLv3"
        ecdh_curve = ""
    }

    ttls {
        tls = tls-common
        default_eap_type = gtc
        virtual_server = "inner-tunnel"
    }
    peap {
        tls = tls-common
        default_eap_type = gtc
        virtual_server = "inner-tunnel"
   }
}
eap inner_eap {
    default_eap_type = gtc
    gtc { }
}

Das ldap-Modul ist für die Nutzerauthentifizierung zuständig. In dieser Anleitung gehen wir davon aus, dass FreeRADIUS über einen Account auf dem LDAP-Server verfügt.

Konfiguration der Serververbindung

Zunächst muss eine Verbindung zum Server konfiguriert werden. Der Server kann dabei entweder per IP-Adresse oder per Hostname angegeben werden. Ebenfalls muss der Base-DN sowie Username und Passwort des FreeRADIUS-Accounts konfiguriert werden.

Standardmäßig wird von FreeRADIUS unverschlüsseltes LDAP auf Port 389 gesprochen. Soll ein anderer Port verwendet werden kann dies mit port = <portnummer> konfiguriert werden.

ldap {
    server = 'ldap.example.com'
    base_dn = 'dc=example,dc=com'
    identity = 'cn=freeradius,dc=example,dc=com'
    password = '<password>'
    [...]
}

Neben der grundsätzlichen Verbindung zum LDAP-Server können noch weitere Parameter der Verbindung eingestellt werden.

TLS-Konfiguration

Soll die LDAP-Verbindung verschlüsselt sein, muss ein TLS-Block hinzugefügt werden, in dem die TLS-Konfiguration hinterlegt ist. Dabei kann konfiguriert werden, wie das Zertifikat des LDAP-Servers überprüft werden soll. Damit die Überprüfung funktioniert, muss der konfigurierte Servername dem Namen im Zertifikat entsprechen. Andernfalls kann die Zertifikatsüberprüfung mit require_cert='allow' abgeschaltet werden.

ldap {
    tls {
        start_tls = yes # Nur notwendig, wenn StartTLS verwendet wird. Wenn LDAP über Port 636 (ldaps) gesprochen wird, ist TLS implizit.
        ca_file = /path/to/the/ca/file.pem
        require_cert = 'hard'
    }
}

Connection-Pool

Zur Verarbeitung von vielen gleichzeitigen Anfragen kann es sinnvoll sein, mehrere LDAP-Verbindungen zu öffnen, damit mehrere LDAP-Anfragen zeitgleich durchgeführt werden können.

Für den Aufgbau der Verbindungen gibt es 4 wichtige Konfigurationsoptionen:

  • start: Wie viele LDAP-Verbindungen werden beim Start des FreeRADIUS-Servers aufgebaut? (Default: 5)
    • Achtung: Ist eine Zahl > 0 konfiguriert, wird FreeRADIUS nicht starten, falls die Verbindung nicht aufgebaut werden kann.
  • min: Wie viele LDAP-Verbindungen sollen mindestens offen sein? (Default: 5)
  • max: Wie viele LDAP-Verbindungen dürfen maximal offen sein? (Default: 10)
  • spare: Wie viele LDAP-Verbindungen sollen in Reserve gehalten werden? (Default: 3)

Für den Abbau Verbindungen können ebenfalls verschiedene Parameter gesetzt werden:

  • uses: Anzahl an LDAP-Anfragen, nach denen die Verbindung geschlossen werden soll (0 für unbegrenzt, Default: 0)
  • lifetime: Anzahl an Sekunden, nach denen die Verbindung geschlossen werden soll (0 für unbegrenzt, Default: 0)
  • idle_timeout: Anzahl an Sekunden nach der nach letzter Nutzung die Verbindung geschlossen werden soll (Default: 60)

Sollen die Default-Werte genutzt werden, kann dieser Block auch weggelassen werden.

ldap {
    pool {
        start = 5
        min = 5
        max = 10
        spare = 3
        idle_timeout = 60
    }
}

Konfiguration der User-Suche

Als nächstes muss konfiguriert werden, in welchem Verzeichnisbaum FreeRADIUS nach den Useraccounts suchen soll. Hierfür muss der base_dn entsprechend der vorhandenen LDAP-Struktur konfiguriert werden. Liegen die User-Accounts z.B. unter ou=People,dc=example,dc=com, dann muss der base_dn auf ou=People,${..base_dn} gesetzt werden. ${..base_dn} übernimmt hierbei den konfigurierten base_dn aus dem übergeordneten Block.

Außerdem muss auch der LDAP-Filter konfiguriert werden. Sind nur Nutzende mit bestimmten Attributen zur eduroam-Nutzung berechtigt, kann dies in diesem LDAP-Filter entsprechend angepasst werden. Als Platzhalter für den Usernamen sollte allerdings immer die Ersetzung %{%{Stripped-User-Name}:-%{User-Name}} verwendet werden.

ldap {
    [...]
    user {
        base_dn = "ou=People,${..base_dn}"
        filter = (uid=%{%{Stripped-User-Name}:-%{User-Name}})"
    }
    [...]

Konfiguration der verwendeten LDAP-Felder

Um die Authentifizierung korrekt durchführen zu können muss FreeRADIUS nun noch die entsprechenden LDAP-Felder in eigene Variablen übersetzen. Hierfür ist es wichtig zu wissen, in welchem Format das Passwort vorliegt.

Ist im Passwort-Feld ein Header enthalten, der das Format mit angibt (z.B. {ssha}....), kann die Variable control:Password-With-Header genutzt werden. Liegt das Passwort ohne Header in einem gesonderten LDAP-Feld, muss es in das entsprechende Feld überführt werden. Die möglichen Felder sind in der Man-Page von rlm_pap aufgeführt.

In diesem Beispiel gehen wir davon aus, dass das Passwort mit Header im LDAP-Attribut userPassword liegt.

ldap {
    update {
        control:Password-With-Header    += 'userPassword'
    }
}

Komplette Beispielkonfiguration für mods-available/ldap

Diese Konfiguration geht von einer unverschlüsselten LDAP-Verbindung aus.

ldap {
    server = 'ldap.example.com'
    identity = 'cn=freeradius,dc=example,dc=com'
    password = '<password>'
    base_dn = 'dc=example,dc=com'
    user {
        base_dn = "ou=People,${..base_dn}"
        filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
    }
    update {
        control:Password-With-Header += 'userPassword'
    }
}

Im Linelog-Modul können eigene Log-Meldungen generiert werden. Dies ist insbesondere Hilfreich, da die Standard-Logmeldungen von FreeRADIUS im inneren Tunnel nicht die MAC-Adresse des anfragenden Geräts loggt, sodass bei hoher Auslastung die Requests im äußeren Tunnel nicht den Requests im inneren Tunnel zugeordnet werden können.

In den Servern default und inner-tunnel rufen wir unterschiedliche Linelog-Module auf, die nun in der Datei mods-available/linelog konfiguriert werden müssen:

Die unten stehende Konfiguration geht davon aus, dass das Log an Syslog mit der Facility LOCAL3 übergeben wird. Alternativ kann hier auch ein Dateiname angegeben werden.

linelog outer_linelog {
    filename = syslog
    syslog_facility = local3
    reference = "messages.%{%{reply:Packet-Type}:-default}"
    messages {
        default = "Unknown packet type %{Packet-Type}"
        Access-Accept = "Login OK: [%{User-Name}] (cli %{request:Calling-Station-Id})"
        Access-Reject = "Login incorrect: [%{User-Name}] (%{%{reply:Reply-Message}:-%{request:Module-Failure-Message}}) (cli %{request:Calling-Station-Id})"
    }
}
linelog inner_tunnel_linelog {
    filename = syslog
    syslog_facility = local3
    reference = "messages.%{%{reply:Packet-Type}:-default}"
    messages {
        default = "Unknown packet type %{Packet-Type}"
        Access-Accept = "Login OK: [%{User-Name}] (cli %{outer.request:Calling-Station-Id} via TLS tunnel)"
        Access-Reject = "Login incorrect: [%{User-Name}] (%{request:Module-Failure-Message}) (cli %{outer.request:Calling-Station-Id} via TLS tunnel)"
    }
}

Standardmäßig sind in FreeRADIUS eine Vielzahl von Modulen aktiv, die für die hier beschriebene Funktionsweise nicht benötigt werden. Die für diesen FreeRADIUS benötigten Module sind:

  • always
  • attr_filter
  • eap
  • ldap
  • linelog
  • pap
  • realm

Die Aktivierung der Module geschieht, wie bei den Servern, über einen Symlink in mods-enabled/ auf die entsprechenden Konfigurationsdateien in mods-available/.

Die Anbindung der Access Points geschieht über die Datei clients.conf. Ebenso muss hier die Verbindung vom Radsecproxy zum FreeRADIUS konfiguriert werden.

Jeder Client-Bock besitzt dabei ein Label, das für das Logging genutzt werden kann.

Innerhalb des Client-Blocks müssen dann die verschiedenen Parameter gesetzt werden:

  • IP-Adresse: Hier gibt es verschiedene Möglichkeiten (eine muss ausgewählt werden):
    • ipaddr: IPv4, IPv6 oder Hostname (Achtung: Schlägt das DNS-Lookup fehl, startet FreeRADIUS nicht)
    • ipv4addr: IPv4-Adresse (ggf. mit Subnetzmaske)
    • ipv6addr: IPv6-Adresse (ggf. mit Subnetzmaske)
  • secret: Das gemeinsame Geheimnis zwischen dem Client und FreeRADIUS
client ap1 {
    ipv4addr = 198.51.100.1
    secret = <shared secret>
}
client ap2 {
    ipv4addr = 198.51.100.2
    secret = <shared secret>
}
client andere-aps {
    ipv4addr = 198.51.100.128/25
    secret = <shared secret>
}
client radsecproxy {
    ipv4addr = 203.0.113.5
    secret = <shared secret>
}

Für Test-Zwecke ist es ebenfalls sinnvoll eine Konfiguration für localhost hinzuzufügen:

client localhost {
    ipaddr = 127.0.0.1
    secret = <shared secret>
}

Abschließend muss noch das Verhalten von FreeRADIUS in Bezug auf die verschiedenen Realms konfiguriert werden. Dies geschieht in der Datei proxy.conf

Die Konfiguration gliedert sich in 3 Teile:

  • Home-Server: Hier werden die Kommunikationspartner (i.d.R. nur der Radsecproxy) konfiguriert
  • Home-Server-Pool: Ein Pool gruppiert Home-Server zusammen und stellt das Fail-Over sicher.
  • Realm: Die Realms definieren das Verhalten je nach Realm des Usernamens.

Zunächst muss das Proxying der Requests generell aktiviert werden:

proxy server {
}

Dann ist für jeden Server ein eigener home_server-Block anzulegen. In der Regel ist dies nur der Radsecproxy (bzw. mehrere bei redundantem Setup)

home_server radsecproxy {
    ipaddr = 203.0.113.5
    secret = <shared secret>
    [...]
}

Um ungewolltes Fail-Over-Verhalten zu verhindern, sollte hierbei dann auch noch StatusServer konfiguriert werden. Diese Konfiguration sorgt dafür, dass die Verbindung an sich getestet wird. Andernfalls kann es sein, dass FreeRADIUS die Verbindung als Zombie markiert, wenn Requests von einer bestimmten Realm nicht beantwortet werden, obwohl die Verbindung für andere Realms funktioniert.

home_server radsecproxy {
    [...]
    status_check = status-server
    check_interval = 60 # Anzahl Sekunden zwischen StatusServer-Checks.
    num_answers_to_be_alive = 3 # Anzahl von StatusServer-Antworten, nach denen bei einem Fail-Over-Event dieser Server wieder aktiv geschaltet wird
}

Als nächstes muss ein home_server_pool konfiguriert werden. In dieser Sektion werden die Fail-Over-Regeln konfiguriert. FreeRADIUS unterstützt verschiedene Fail-Over-Mechanismen, die nicht alle für EAP-basierte Methoden geeignet sind. Geeignet sind:

  • client-balance: Entscheidung auf Basis der Source-IP-Adresse des eingegangenen Requests (nicht sinnvoll bei nur einem WLAN-Controller). Ist der so gewählte Server nicht aktiv, wird der nächste genommen.
  • client-port-balance: Wie client-balance, bezieht allerdings den Source-Port mit ein (dementsprechend auch bei nur einem WLAN-Controller nutzbar)
  • fail-over: Hot-Standby-Redundanz (der primäre Server wird standardmäßig genommen, es sei denn er ist als nicht aktiv markiert)
home_server_pool radsecproxy {
    type = fail-over
    home_server = radsecproxy
    # Hier können dann weitere Home-Server eingetragen werden.
    # z.B.
    home_server = radsecproxy2
}

Abschließend müssen die Realms konfiguriert werden. Hierfür müssen zunächst die eigenen Realms eingetragen werden. Ein leerer Realm-Block bedeutet hierbei, dass die Requests lokal bearbeitet werden sollen.

realm example.com { }
realm LOCAL { }
realm NULL { }

LOCAL und NULL sind hierbei spezielle Werte, die intern genutzt werden, wenn z.B. keine Realm vorhanden ist oder wenn für den Request manuell gesetzt werden soll, dass er lokal bearbeitet wird.

Für alle weiteren Realms können wir das Keyword DEFAULT nutzen. Hierbei muss der zuständige home_server_pool sowie die Option nostrip angegeben werden. Wird die Option nostrip weggelassen, bearbeitet FreeRADIUS den weitergeleiteten Request und tauscht den Usernamen durch Stripped-User-Name und Realm aus, was in der eduroam-Routing-Struktur nicht funktioniert.

realm DEFAULT {
    auth_pool = radsecproxy
    nostrip
}
proxy server { }

home_server radsecproxy {
    ipaddr = 203.0.113.5
    secret = <shared secret>
    status_check = status-server
    check_interval = 60
    num_answers_to_be_alive = 3
}
home_server_pool radsecproxy {
    type = fail-over
    home_server = radsecproxy
}
realm example.com { }
realm LOCAL { }
realm NULL { }

realm DEFAULT {
    auth_pool = radsecproxy
    nostrip
}
  • Zuletzt geändert: vor 2 Jahren