FHEM Forum

FHEM => Codeschnipsel => Thema gestartet von: betateilchen am 23 Dezember 2025, 13:06:53

Titel: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 23 Dezember 2025, 13:06:53
defmod web2 FHEMWEB 8085 global

defmod allowed4web2 allowed
attr allowed4web2 basicAuth {radius($user,$password)}
attr allowed4web2 validFor web2

sub radius {
  use Authen::Radius; #apt install libauthen-radius-perl
  my ($user,$password) = @_;
  my $clientPassword = getKeyValue("clientPassword");
  my $r = new Authen::Radius(Host => '192.168.123.152', Secret => $clientPassword);
  return $r->check_pwd($user,$password);
}


Warum? Weil in meinem Netzwerk eh ein Radius Server läuft.
Mich hatte einfach interessiert, ob das funktioniert und wie aufwändig es wird.
Dank der perl Library Authen::Radius ist das Ganze aber sehr einfach umzusetzen.

Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: JudgeDredd am 23 Dezember 2025, 13:14:33
Hallo betateilchen,

ja ist denn heute schon Weihnachten ? 😃
Die Idee ist echt prima, das werde ich über die Feiertage definitiv mal ausprobieren.

Ich hatte vor vielen Weihnachten mal das LDAP-Modul (keine Ahnung ob das im SVN ist) ausprobiert, aber dazu hätte ich Änderungen am LDAP-Schema vornehmen müssen, an die ich nicht ran wollte.

So kann ich nun die Auth gegen mein AD machen.

hab Dank für Deinen Vorschlag und ein frohes Fest,
JudgeDredd
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: CoolTux am 23 Dezember 2025, 16:37:46
Schöne Lösung. Auch nice wäre ein OAuth2 oder besser gleich OIDC. Mein Keycloak würde sich da freuen   ;D
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 23 Dezember 2025, 20:23:33
Beschreibe mir doch mal das Szenario, das Dir da vorschwebt.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: CoolTux am 23 Dezember 2025, 20:33:44
So ganz allgemein. So wie bisher BasicAuth funktioniert nur eben per SSO mittels OIDC. In meinem Fall an Keycloak.
Wenn der Token abgelaufen ist wird an Keycloak weiter geleitet um sich zu Authentifizieren.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 23 Dezember 2025, 20:51:59
Also im Prinzip sowas ähnliches, wie wir es bei der Anmeldung mit einem OTP gegen den Google-Authenticator bereits haben? Wenn die Anmeldung erfolgreich war, wird das für maximal 86400 Sekunden gecached und nicht mehr neu gefragt. Danach muss man sich neu anmelden.

Das sollte doch eigentlich nicht allzu schwierig umzusetzen sein. Das allowed-device muss ja nur einmal ein "true" zurückbekommen und sich das merken.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: passibe am 24 Dezember 2025, 13:34:52
Zitat von: CoolTux am 23 Dezember 2025, 16:37:46Schöne Lösung. Auch nice wäre ein OAuth2 oder besser gleich OIDC. Mein Keycloak würde sich da freuen   ;D
Ich nutze oauth2-proxy (https://oauth2-proxy.github.io/oauth2-proxy/) und nginx auth_request (https://nginx.org/en/docs/http/ngx_http_auth_request_module.html). Nginx sendet dann, bei erfolgreicher Anmeldung über den oauth2-proxy, einfach den Basic Auth header mit (also user:pass in base64; ähnlich wie in diesem Beispiel (https://oauth2-proxy.github.io/oauth2-proxy/configuration/integration#configuring-for-use-with-the-nginx-auth_request-directive) X-Access-Token nur eben hardcoded). In FHEM muss man also gar nichts umstellen.

oauth2-proxy funktioniert auch mit Keycloak als IDP (https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/keycloak_oidc/). (Ich nutze aktuell Nextcloud als IDP, weil das hier eh läuft.)
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: CoolTux am 24 Dezember 2025, 15:00:50
Mein FHEM läuft in einem Kubernetes Cluster und ist über Traefik Ingress aufrufbar. Muss mal schauen ob man Traefik diesbezüglich konfigurieren kann.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: passibe am 24 Dezember 2025, 16:32:08
Laut Doku geht da irgendwas mit Traefik: https://oauth2-proxy.github.io/oauth2-proxy/configuration/integration#configuring-for-use-with-the-traefik-v2-forwardauth-middleware
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 26 Dezember 2025, 11:23:11
Zitat von: CoolTux am 23 Dezember 2025, 16:37:46Schöne Lösung. Auch nice wäre ein OAuth2 oder besser gleich OIDC. Mein Keycloak würde sich da freuen   ;D

Es gibt doch für keycloak ein radius-plugin.
Würde das Deine Aufgabenstellung nicht abdecken?

(von keycloak habe ich keine Ahnung, ich habe nur ein bisschen im Internet rumgelesen, nachdem Du mit Deinem Wunsch kamst)
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: CoolTux am 26 Dezember 2025, 12:25:56
Zitat von: betateilchen am 26 Dezember 2025, 11:23:11
Zitat von: CoolTux am 23 Dezember 2025, 16:37:46Schöne Lösung. Auch nice wäre ein OAuth2 oder besser gleich OIDC. Mein Keycloak würde sich da freuen   ;D

Es gibt doch für keycloak ein radius-plugin.
Würde das Deine Aufgabenstellung nicht abdecken?

(von keycloak habe ich keine Ahnung, ich habe nur ein bisschen im Internet rumgelesen, nachdem Du mit Deinem Wunsch kamst)

Das muss ich mir mal in Ruhe anschauen. Wird sicher nicht Standard sein sondern muss man als Zusatz einbinden. Danke Dir auf jeden Fall.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 26 Dezember 2025, 13:23:35
https://github.com/vzakharchenko/keycloak-radius-plugin/blob/master/README.md
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: JudgeDredd am 27 Dezember 2025, 12:57:26
So, die Feiertage sind vorbei und ich habe mich mit der RADIUS-Auth mal beschäftigt.
Die erste Euphorie war allerdings schnell verflogen, da mein RADIUS nicht für PlainText-Passwort (PAP) konfiguriert ist.
Die Begeisterung für die Idee von betateilchen war aber ungebrochen.
Daher habe ich mir eine Sub für die RADIUS-Auth mittels MSCHAPv2 gebaut.
Die Response-Challenge ist hier manuell nachgebaut, da es dafür kein debian Paket gibt und ich von cpan absehen wollte.

Vielleicht gibt es ja weitere User, die ebenfalls MSCHAPv2 nutzen und Interesse daran haben.
use Authen::Radius;       #Debian Paket libauthen-radius-perl
use Digest::MD4 qw(md4);  #Debian Paket libdigest-md4-perl
use Digest::SHA qw(sha1); #      "            "
use Crypt::DES;           #Debian Paket libcrypt-des-perl

sub radius_mschapv2($$) {

    # -----------------------------
    # Konfiguration
    # -----------------------------
    my ($username, $password ) = @_;
    Log3 undef, 4, "Starting Auth for $username";

    my $radius_server            = 'radius.mydomain.intranet';                #IP/FQDN of RADIUS
    my $radius_secret            = getKeyValue('RADIUSclientPassword') || ''; #Client Password (clients.conf) keepair set via setKeyValue('RADIUSclientPassword','password')
    my $radius_port              = 1812;                                      #Listen Port
    my $radius_nas_Identifier    = 'fhem-server';                             #optional Attribut

    # -----------------------------
    # MS-CHAPv2 Parameter
    # -----------------------------
    my $auth_challenge = random_bytes(16);  # AuthenticatorChallenge
    my $peer_challenge = random_bytes(16);  # PeerChallenge

    # NT-Password-Hash
    my $nt_hash = md4(encode_unicode($password));

    # MS-CHAPv2 Response berechnen
    my $nt_response = mschapv2_response(
        $peer_challenge,
        $auth_challenge,
        $username,
        $nt_hash
    );

    Authen::Radius->load_dictionary( '/usr/share/freeradius/dictionary' );

    # -----------------------------
    # RADIUS Initialisierung
    # -----------------------------
    my $radius = Authen::Radius->new(
        Host  => $radius_server,
        Secret => $radius_secret,
        Port  => $radius_port,
        TimeOut => 5,
    );
    $radius->clear_attributes;

    # -----------------------------
    # RADIUS Attribute setzen
    # -----------------------------
    $radius->add_attributes(
        { Name => 'Service-Type',      Value => 'Login-User' },
        { Name => 'User-Name',         Value => $username },
        { Name => 'MS-CHAP-Challenge', Value => $auth_challenge },
        { Name => 'MS-CHAP2-Response', Value => pack("C C a24 a24", 1, 0, $peer_challenge, $nt_response ), Type => 'octets' },
        { Name => 'NAS-Identifier',    Value => $radius_nas_Identifier },
    );

    # -----------------------------
    # Access-Request senden
    # -----------------------------
    $radius->send_packet(ACCESS_REQUEST) and my $res = $radius->recv_packet();

    # -----------------------------
    # Ergebnis
    # -----------------------------

    if ( defined($res) ) {
        if ($res == ACCESS_ACCEPT) {
            Log3 undef, 4, "Auth for $username successfull";
            return $res;
        } elsif ($res == ACCESS_REJECT) {
            Log3 undef, 4, "Auth for $username failed";
        } else {
            Log3 undef, 4, "Auth for $username unknown ($res)";
        }
    } else {
        Log3 undef, 4, "No reply from $radius_server"
    }

    # =========================================================
    # Hilfsfunktionen
    # =========================================================

    sub random_bytes {
        my ($len) = @_;
        return join '', map { chr(int(rand(256))) } 1 .. $len;
    }

    sub encode_unicode {
        my ($str) = @_;
        return join('', map { $_ . "\x00" } split //, $str);
    }

    sub mschapv2_response {
        my ($peer_challenge, $auth_challenge, $user, $nt_hash) = @_;

        # ChallengeHash = SHA1(PeerChallenge | AuthChallenge | Username)[0..7]
        my $challenge = substr(
            sha1($peer_challenge . $auth_challenge . $user),
            0, 8
        );

        return des_encrypt_challenge($challenge, $nt_hash);
    }

    sub des_encrypt_challenge {
        my ($challenge, $hash) = @_;

        my $response = '';

        for my $i (0..2) {
            my $key = substr($hash . ("\0" x 5), $i * 7, 7);
            $key = expand_des_key($key);

            my $des = Crypt::DES->new($key);
            $response .= $des->encrypt($challenge);
        }

        return $response;
    }

    sub expand_des_key {
        my ($key7) = @_;
        my @k = unpack("C7", $key7);
        my @key8;

        $key8[0] = $k[0] & 0xFE;
        $key8[1] = (($k[0] << 7) | ($k[1] >> 1)) & 0xFE;
        $key8[2] = (($k[1] << 6) | ($k[2] >> 2)) & 0xFE;
        $key8[3] = (($k[2] << 5) | ($k[3] >> 3)) & 0xFE;
        $key8[4] = (($k[3] << 4) | ($k[4] >> 4)) & 0xFE;
        $key8[5] = (($k[4] << 3) | ($k[5] >> 5)) & 0xFE;
        $key8[6] = (($k[5] << 2) | ($k[6] >> 6)) & 0xFE;
        $key8[7] = ($k[6] << 1) & 0xFE;

        return pack("C8", @key8);
    }
}
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: betateilchen am 28 Dezember 2025, 19:17:45
Es freut mich, dass mein "einfacher" Ansatz (natürlich zum Testen nur mit PAP) dazu angeregt hat, das Thema "Radius" ein ganzes Stück weiter zu denken.
Titel: Aw: FHEMWEB basicAuth gegen einen Radius-Server
Beitrag von: JudgeDredd am 28 Dezember 2025, 20:23:30
Dann nimm bitte weiterhin "gute Ideen liefern" in Deine guten Vorsätze für 2026 auf 😉
So oder so ist der Ansatz es Wert, Dir einen guten Rutsch zu wünschen.