All posts filed under “Python

Azure Ad Connect Samba4

Read in : English English

Bonjour a tous

Cela fait bien longtemps que je n’est pas écris sur ce blog.

Récemment je suis tomber sur plusieurs article concernant le fonctionnement de la synchronisation des password active directory avec azure ad connect:
Microsoft how-password-hash-synchronization-works
Dsinternals how-azure-active-directory-connect-syncs-passwords

Le fonctionnement est le suivant : azure ad connect prend le hashnt encodé en utf16-le, et le rentre dans la fonction PBKDF2-HMAC-SHA256″ avec 1000 itérations un sel aléatoire

Ces même recherches m’ont amenez tomber sur le travail de Dr Nestori Syynimaa (@DrAzureAD):
aadinternals long-passwords  qui permet a partir d’un simple script powershell d’envoyer un hashnt.
Je tente donc ma chance avec le powershell et je m’aperçois que cela fonctionne !
Le code de @DrAzureAD est opensource, je me dit donc… il y a sûrement possibilité de le convertir en python pour l’utiliser sous linux et donc avec samba.

Après analyse du code je comprend que microsoft communique avec en WCF binary xml  ,
Bonne nouvelle les spécification sont ouverte et un projet python existe sur github : python-wcfbin

Je créer donc un repo git baptiser AADInternals_python   qui reprend le code de AADInternals qui m’intéresse pour le convertir en python. Après avoir tâtonner un peu… victoire, le code fonctionne !

Je m’aperçois ensuite que le projet AADInternals de @DrAzureAD reprend tout ce qui est nécessaire pour créer/supprimer des utilisateurs et des groupes a la mode azuread il me reste donc a porter le reste du code.

Mais dans mes tests, les serveur Microsoft répondent:

 « The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/online/aws/change/2010/01:syncRequest. The InnerException message was ‘Element ‘http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value’ contains data from a type that maps to the name ‘:mustUnderstand’. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver if you are using DataContractSerializer or add the type corresponding to ‘mustUnderstand’ to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to the serializer.’. Please see InnerException for more details. »

Je comprend donc que c’est lib python-wcfbin qui serialize mal dans certain cas.

Je demande donc au boulot un petit coup de main a des dev plus expérimenter et notement a AndreasLrx  un stagiaire chez nous, (oui oui ils sont très bon les stagiaires chez nous ! )

Et il trouve le problème, les spécifications de microsoft ne sont pas à jour ! Nous faisons même une pull request sur le projet d’origine : https://github.com/ernw/python-wcfbin/pull/16

J’ai maintenant tout ce qu’il faut pour construire un outils de synchronisation complet.

Je créer donc sur mon projet github un projet: AzureADConnect_Samba4

Le projet est capable d’envoyer des groupe et utilisateurs ansi que d’envoyer des hashnt  (aadhash) a l’azure ad.

Le script conserve ensuite les dernières informations dans une base sqlite local,  il l’enverra a nouveau l’objet a azure ad uniquement si l’objet a été modifier depuis le dernier envoie.

ATTENTION Si vous avez déjà utiliser l’azure ad connect windows, vous devez bien identifier votre « sourceanchor » ou « immutableId » de votre configuration précédente.
Par défaut microsoft utilise l’ objectGUID avec msDSConsistencyGuid, j’ai donc repris ce fonctionnement, cependant, en fonction des versions votre paramétrage peu être différent.
A vous donc de bien identifier votre sourceanchor pour ne pas créer de doublon et/ou mauvaise suppression.

Attention également, le script ne prend pas en charge le password writeback !

En espérant que ce travail vous sera utile ! N’hésitez pas a me faire des retours !

Edit:

Depuis le 13/05/2023, le script gère également les Device (jonction hybrid)

Découverte Rpyc

Il y a peu de temps j’ai découvert RPYC https://rpyc.readthedocs.io/en/latest/

L’outil est pratique pour piloter des machines a distance a travers ssh.

Pourquoi ne pas utiliser Ansible  ?

Disons que c’est une méthode différente.
Ansible est très pratique (je l’utilise parfois), beaucoup de module et grosse communauté mais je préférerais écrire du python pour faire des boucle, des while etc, et je dois bien dire que je ne suis pas très fan du yaml …

RPYC permet de faire discuter deux connexion python ensemble, par exemple, je suis capable de créer une liste (python) sur un serveur1, de la récupérer sur mon poste (python local) pour la renvoyer via RPYC sur une connexion python du serveur2

Je découvre donc les possibilités, voici un bout de code d’exemple :


import rpyc 
from rpyc.utils.classic import upload_package 
from rpyc.utils.zerodeploy import MultiServerDeployment 
from rpyc.utils.zerodeploy import DeployedServer 
from plumbum import SshMachine 
import ldap3 
import pyasn1 
 
# create the deployment 
 
list_host=['mysrv.domain.lan'] 
list_ssh=[] 
 
for f in list_host: 
    mach = SshMachine(f, user="root", keyfile="/home/user/.ssh/id_ed25519",port="22") 
    list_ssh.append(mach) 
 
 
def get_etc_file(): 
    import glob 
    allfile = [] 
    for i in glob.glob('/etc/*'): 
        allfile.append(i) 
    return allfile 
 
def get_ldap_version(): 
    import ldap3 
    return ldap3.__version__ 
 
 
dep = MultiServerDeployment(list_ssh) 
conns = dep.classic_connect_all() 
 
for conn in conns: 
 
    run_get_etc_file = conn.teleport(get_etc_file) 
    print(run_get_etc_file()) 
 
    rpyc.classic.upload_package(conn, ldap3) 
    rpyc.classic.upload_package(conn, pyasn1) 
    run_get_ldap_version = conn.teleport(get_ldap_version) 
 
    print(run_get_ldap_version()) 

Dans l’exemple ici on a quelque chose d’intéressant, j’utilise rpyc.classic.upload_package, en gros les serveurs sur lequel je me connecte n’ont pas le paquet ldap3, mais il est installé sur ma machine, du coup avec rpyc je le pousse dans la connexion python instancier avec rpyc, (je le l’install pas sur le serveur !), et du coup il est utilisable. teleport lui me permet d’envoyer une fonction sur le python distant pour ensuite l’executer sur le distant. J’aime bien l’idée.

Je découvre l’outil et je vois un potentiel intéressant. Attention je n’ai pas fais de réel test en prod ni d’utilisation intensive, mais j’aime bien l’idée.