schema MAB

MAC Authentication Bypass avec freeradius et openldap, avec assignation automatique de VLAN (mac to vlan)

Il est courant d’utiliser un serveur radius en coordination avec le protocole 802.1x pour sécuriser l’accès à un réseau. Ici, le but n’est pas du tout de sécuriser l’accès au réseau, mais de s’épargner de configurer le port du switch sur lequel on branche un bidule pour le mettre dans le bon VLAN, en utilisant le mac authentication bypass.

Ainsi, avec des ports banalisés, la vie est plus gaie.

Le principe est d’utiliser la fonctionnalité d’identification par adresse MAC disponible avec 802.1x, qui est prévue dans le protocole pour supporter les périphériques réseau incompatibles avec l’authentification 802.1x (comme nombre de vieilles imprimantes par exemple).

Et comme je dispose d’un gentil annuaire openldap, ça pourrait être sympa de stocker dedans la liste des MAC adresses et des VLANs associés, ainsi que les informations relatives à l’élément qui va servir de supplicant pour réaliser le dialogue protocolaire (mon switch quoi…).

schema MAB

Navigation

Openldap

Tout d’abord, c’est une bonne idée de modifier le schéma de l’annuaire openldap avec les extensions freeradius; en effet, ça va permettre d’y créer des objets de la classe adaptée pour stocker les attributs qui sont intéressants. Le paquet debian freeradius contient les fichiers suffisants pour réaliser l’opération : freeradius.ldif et freeradius-clients.ldif .

Il faut en premier lieu préparer les deux fichiers ldif pour les intégrer au schéma, en les éditant pour modifier le dn et le cn de l’objet ldif lui-même, de la manière suivante:

vi freeradius.ldif

dn: cn={5}radius,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: cn={5}radius
...
vi freeradius-clients.ldif

dn: cn={6}radiusClient,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: cn={6}radiusClient
...

Les chiffres entre accolades suivent en séquence les schémas déjà présents, et sont donc à adapter en fonction des cas. Il reste ensuite à ajouter ces deux nouveaux schémas, avec la commande ldapadd fournie par le paquet ldapscripts:

ldapadd -Q -Y EXTERNAL -H ldapi:/// -W -f freeradius.ldif
ldapadd -Q -Y EXTERNAL -H ldapi:/// -W -f freeradius-clients.ldif

Si la commande ldapadd se termine avec une erreur du type:

ldap_add: Server is unwilling to perform (53)
  	additional info: operation requires sibling renumbering

C’est que le numéro de séquence entre accolades choisi entre en conflit avec un schéma existant dans l’annuaire, la commande suivant aidera à déterminer quels numéros choisir:

ldapsearch -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config cn

L’annuaire openldap est à présent prêt à stocker des objets de classe radiusClient et de classe radiusprofile. Ces objets stockent respectivement les attributs concernant:

  • les clients au sens radius du terme, c’est à dire les éléments qui vont relayer le dialoque 802.1x, voir ici le faire à la place du périphérique qui se connecte; dans cet exemple, le client est mon switch. Les attributs intéressants sont:
    • radiusClientIdentifier : le nom dns ou l’adresse IP du switch
    • radiusClientSecret : le secret partagé avec le serveur radius, un mot de passe quoi
    • radiusClientType : on mettra « other »
  • les users au sens radius, c’est à dire les périphériques se connectant, pour lesquels il faut attribuer un vlan au port utilisé correspondant. Les attributs intéressants sont:
    • radiusServiceType : on mettra « Framed-user »
    • radiusTunnelMediumType : on mettra « IEEE-802 »
    • radiusTunnelType : on mettra « VLAN »
    • radiusTunnelPrivateGroupId : on mettra le numéro de VLAN dans lequel on veut faire tomber le périphérique
    • macAddress : on mettra la mac address du périphérique, qui lui servira d’identifiant et de mot de passe. Cet attribut est fourni par la classe d’objet ieee802Device, on la rajoutera donc aux objets représentant les périphériques, en plus de la classe radiusprofile.

Freeradius

Bon, là il s’agit de ne pas trop faire le malin. L’installation du paquet freeradius dans debian stretch dépend entre autres du paquet freeradius-config qui apporte à l’installation une configuration par défaut qui marche. On va donc essayer de ne pas trop altérer celle-ci, et seulement ajouter le strict nécessaire.

En plus des dépendances, pour pouvoir utiliser le bel annuaire openldap configuré précédemment, il faut installer le paquet freeradius-ldap. Après installation, il faut configurer le module ldap:

vi freeradius/3.0/mods-available/ldap

ldap {
    ...
    # notre serveur ldap
    server = 'ip_ou_nom_dns_de_notre_serveur_ldap'
    # un compte qui peut lire, voir écrire dans notre annuaire
    identity = 'dn_de_notre_compte_utilisateur'
    password = 'son_mot_de_passe'
    ...
    # notre base DN
    base_dn = 'racine_de_notre_annuaire_ldap'
    ...
    # le mappage de nos attributs ldap avec nos propriétés radius pour les périphériques
    update {
        control:Password-With-Header    = 'macAddress'
        reply:Tunnel-Type               := 'radiusTunnelType'
        reply:Tunnel-Medium-Type        := 'radiusTunnelMediumType'
        reply:Tunnel-Private-Group-ID   := 'radiusTunnelPrivategroupId'
        ...
    }
    ...
    # la partie pour les périphériques à connecter
    user {
        ...
        # le filtre formatant la mac address servant de login
        filter = "(macAddress=%{%{Stripped-User-Name}:-%{User-Name}})"
        ...
    }
    ...
    # la partie pour les switchs
    client {
        # la branche de l'annuaire dans laquelle sont stockés les objets représentant nos clients radius : les switchs
        base_dn = 'l_ou_dans_laquelle_il_y_a_les_comptes_des_switchs'
        # le filtre pour nos clients
        filter = '(objectClass=radiusClient)'
        ...
        # le mappage des attributs ldap avec les propriétés radius pour les clients
        attribute {
            ipaddr                          = 'radiusClientIdentifier'
            secret                          = 'radiusClientSecret'
            shortname                       = 'radiusClientShortname'
            nas_type                        = 'radiusClientType'
        }
    }
    ...
    # charger les clients au démarrage
    read_clients = yes
   ...
}

Puis indiquer à freeradius de le charger:

cd /etc/freeradius/3.0/mods-enabled
ln -s ../mods-available/ldap ldap
systemctl restart freeradius

Le virtual host freeradius par défaut apporte l’ensemble des paramètres suffisants pour fonctionner dans le plus grand nombre de cas possibles, et il semble qu’il n’y ait pas de modification à faire dessus pour que ça fonctionne. On s’assurera juste que la section authorize contient:

authorize {
     ldap
}

Et que la section authenticate contient:

authenticate {
    eap
}

Ainsi, le module ldap de freeradius va faire une requête pour trouver l’objet portant l’adresse mac concernée dans l’openldap. Si elle trouve un objet qui porte cette adresse mac, la sortie du module est mise à jour, et dans ce cas le Auth-Type est passé à eap. Le supplicant exécuté par le switch se charge alors du dialogue eap. Si rien ne remonte de la requête ldap, la sortie du module n’est pas mise à jour, il n’y a pas de Auth-Type défini, et l’accès est rejeté en Post-Auth. On pourra tester que tout va bien jusqu’ici avec la commande radtest:

radtest aabbccddeeff aabbccddeeff localhost:1812 10 testing123

Où aabbccddeeff est la mac address d’un équipement pour lequel l’annuaire openldap comporte une entrée. Si tout va bien, on devrait obtenir un retour de ce genre:

Sent Access-Request Id 30 from 0.0.0.0:58281 to 127.0.0.1:1812 length 82
	User-Name = "aabbccddeeff"
	User-Password = "aabbccddeeff"
	NAS-IP-Address = xxx.xxx.xxx.xxx
	NAS-Port = 10
	Message-Authenticator = 0x00
	Cleartext-Password = "aabbccddeeff"
Received Access-Accept Id 30 from 127.0.0.1:1812 to 0.0.0.0:0 length 36
	Tunnel-Type:0 = VLAN
	Tunnel-Medium-Type:0 = IEEE-802
	Tunnel-Private-Group-Id:0 = "99"

On constate effectivement que la requête d’accès reçoit en retour une réponse d’accès autorisé. Celle-ci est accompagnée du numéro de VLAN associé à la mac address testée, ici 99.

En cas de souci, il est intéressant de faire tourner le serveur radius en avant-plan, car il fourni bon nombre d’informations de dépannage. Exemple :

freeradius -X
...

Ready to process requests
(0) Received Access-Request Id 178 from 127.0.0.1:41131 to 127.0.0.1:1812 length 82
(0)   User-Name = "aabb00ddeeff"
(0)   User-Password = "aabb00ddeeff"
(0)   NAS-IP-Address = xxx.xxx.xxx.xxx
(0)   NAS-Port = 10
(0)   Message-Authenticator = 0xc1f71c8e4dd21da486f3561ebd0ae740
(0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default
(0)   authorize {
(0)     policy filter_username {
(0)       if (&User-Name) {
(0)       if (&User-Name)  -> TRUE
(0)       if (&User-Name)  {
(0)         if (&User-Name =~ / /) {
(0)         if (&User-Name =~ / /)  -> FALSE
(0)         if (&User-Name =~ /@[^@]*@/ ) {
(0)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(0)         if (&User-Name =~ /\.\./ ) {
(0)         if (&User-Name =~ /\.\./ )  -> FALSE
(0)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(0)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(0)         if (&User-Name =~ /\.$/)  {
(0)         if (&User-Name =~ /\.$/)   -> FALSE
(0)         if (&User-Name =~ /@\./)  {
(0)         if (&User-Name =~ /@\./)   -> FALSE
(0)       } # if (&User-Name)  = notfound
(0)     } # policy filter_username = notfound
(0)     [preprocess] = ok
(0)     [chap] = noop
(0)     [mschap] = noop
(0)     [digest] = noop
(0) suffix: Checking for suffix after "@"
(0) suffix: No '@' in User-Name = "aabb00ddeeff", looking up realm NULL
(0) suffix: No such realm "NULL"
(0)     [suffix] = noop
(0) eap: No EAP-Message, not doing EAP
(0)     [eap] = noop
(0)     [files] = noop
rlm_ldap (ldap): Reserved connection (1)
(0) ldap: EXPAND (macAddress=%{%{Stripped-User-Name}:-%{User-Name}})
(0) ldap:    --> (macAddress=aabb00ddeeff)
(0) ldap: Performing search in "dc=annuaire,dc=local" with filter "(macAddress=aabb00ddeeff)", scope "sub"
(0) ldap: Waiting for search result...
(0) ldap: Search returned no results
rlm_ldap (ldap): Released connection (1)
rlm_ldap (ldap): Need 4 more connections to reach 10 spares
rlm_ldap (ldap): Opening additional connection (6), 1 of 26 pending slots used
rlm_ldap (ldap): Connecting to ldap://serveur-ldap:389
rlm_ldap (ldap): Waiting for bind result...
rlm_ldap (ldap): Bind successful
(0)     [ldap] = notfound
(0)     [expiration] = noop
(0)     [logintime] = noop
(0) pap: WARNING: No "known good" password found for the user.  Not setting Auth-Type
(0) pap: WARNING: Authentication will fail unless a "known good" password is available
(0)     [pap] = noop
(0)   } # authorize = ok
(0) ERROR: No Auth-Type found: rejecting the user via Post-Auth-Type = Reject
(0) Failed to authenticate the user
(0) Using Post-Auth-Type Reject
(0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default
(0)   Post-Auth-Type REJECT {
(0) attr_filter.access_reject: EXPAND %{User-Name}
(0) attr_filter.access_reject:    --> aabb00ddeeff
(0) attr_filter.access_reject: Matched entry DEFAULT at line 11
(0)     [attr_filter.access_reject] = updated
(0)     [eap] = noop
(0)     policy remove_reply_message_if_eap {
(0)       if (&reply:EAP-Message && &reply:Reply-Message) {
(0)       if (&reply:EAP-Message && &reply:Reply-Message)  -> FALSE
(0)       else {
(0)         [noop] = noop
(0)       } # else = noop
(0)     } # policy remove_reply_message_if_eap = noop
(0)   } # Post-Auth-Type REJECT = updated
(0) Delaying response for 1.000000 seconds
Waking up in 0.3 seconds.
Waking up in 0.6 seconds.
(0) Sending delayed response
(0) Sent Access-Reject Id 178 from 127.0.0.1:1812 to 127.0.0.1:41131 length 20

On voit qu’ici, le problème est que la requête ldap n’a retourné aucun résultat : il n’y a pas de périphérique avec la mac address concernée dans l’annuaire openldap.

Switch

Freeradius sait à présent requêter des users et des clients radius dans l’annuaire openldap. La dernière étape du projet est de configurer le switch pour qu’il travaille avec freeradius à identifier les périphériques que l’on va brancher dessus, et configurer automatiquement les port utilisés pour les faire tomber dans le bon VLAN, et à défaut de réussite de l’identification, les faire tomber dans un VLAN de replis. Chaque switch devra être déclaré dans l’openldap avec les attributs décrits plus haut.

Sur mon switch linksys LGS528-EU, motorisé par un genre d’IOS like, ou tout du moins à la syntaxe similaire, la configuration ressemble à la suivante:

dot1x system-auth-control
dot1x traps authentication failure 802.1x mac
dot1x traps authentication success 802.1x
radius-server host 192.168.10.130 key le_secret_partage_avec_freeradius 
radius-server timeout 15
!
interface vlan 4090
 name guest
 dot1x guest-vlan
!
interface gigabitethernet2
 dot1x guest-vlan enable
 dot1x authentication mac
 dot1x radius-attributes vlan
 dot1x port-control auto
 switchport mode access
!

Avec ces quelques lignes de configuration on:

  • active l’accès au réseau basé sur les ports
  • active l’identification basée sur la mac address, car l’authentification 802.1x échoue
  • déclare le serveur radius
  • défini à 15 secondes le timeout de l’identification par mac address auprès du serveur radius; en effet la valeur par défaut de 3 secondes était un peu juste dans mon cas, et je ne suis pas pressé
  • configure un VLAN invité, sur lequel se rabattre en cas d’échec; ici le VLAN 4090. Ça ne peut pas être le VLAN par défaut.
  • configure un port pour qu’il réalise la magie attendue:
    • utiliser le vlan invité au cas où
    • utiliser l’identification par mac address
    • récupérer auprès du freeradius, en cas de succès, le numéro du VLAN dans lequel tomber
    • actualiser son état (autorisé ou non autorisé) automatiquement
    • on ne taggue pas les trames : mode access

On peut vérifier que nos machines sont ien identifiées avec la commande suivante :

show dot1x users

dot1x users

Pfiou, tant d’efforts pour pouvoir ne plus rien faire après ! (à part déclarer les switchs et les périphériques dans l’annuaire)…

o/

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *