Dysfonctionnements sur des partages DFS

Ces derniers jours, j'ai été confronté à un incident qui arrivait de manière complètement aléatoire lors de l'accès à des partages réseaux via DFS. Dans l'explorateur, un comportement erratique de celui-ci en naviguant dans les partages DFS, l'impossibilité de créer des répertoires ("une erreur réseau inattendue s'est produite"), la nécessité d'appuyer sur F5 pour voir le contenu d'un dossier ou encore l'impossibilité pour une application comme Excel d'écrire dans le chemin étaient des symptômes du dysfonctionnement.

Voici quelques caractéristiques :

  • Toutes les versions sur parc de Windows 10 sont concernées (1703, 1809, 1903)
  • De multiples serveurs de fichiers hébergeant les partages réseaux sont concernés
  • Les utilisateurs sont situés sur des emplacements géographiques différents et le dysfonctionnement est présent par connexion filaire, en WiFi ou via le VPN.

La recherche était d'autant plus compliquée pour isoler le dysfonctionnement que :

  • Aucun log n'était parlant ou utile sur les clients ou les serveurs de fichiers, pas plus que côté équipements réseau ou couche de virtualisation
  • Je n'avais aucun dysfonctionnement côté baie de stockage
  • Aucune modification n'a été faite côté infrastructure Windows / ActiveDirectory
  • Aucune campagne de patch n'a été réalisée avant l'apparition du bug.

Le travail n'a pas été facile car le problème étant complètement aléatoire et parfois résolu d'un simple F5 où d'un redémarrage, l'incident ne m'arrivait pas de suite et était généralement traité par les équipes support. Tous les serveurs étant sur le même hyperviseur, j'ai déplacé un ou deux serveurs sur un autre hôte : sans effet. Par chance, j'ai été très rapidement victime du dysfonctionnement, ce qui m'a permis de dégrossir l'incident et d'éliminer quelques pistes.

L'accès aux partages réseau DFS fonctionnait mal, que le lecteur soit monté sur un disque réseau ou un emplacement réseau ; à la main ou par GPO, aucune différence. Cependant, en accès direct en appelant le partage par le nom du serveur, aucun problème. Je n'ai rien relevé de particulier concernant la connectivité au serveur de fichiers en lui-même et les écoutes du réseau n'ont rien révélé de ce point de vue : j'étais donc quasiment certain qu'il s'agissait d'un problème au niveau de la couche DFS et non du partage de fichiers en lui-même.

J'axe donc mes recherches sur le DFS. Sur un poste qui rencontre le problème, une capture Wireshark est effectuée et je récupère les trames TCP qui vont enfin me permettre d'avancer. En effet, je vois régulièrement ces messages dans les trames TCP :

Ioctl Request FSCTL_DFS_GET_REFERRALS, File: \dundermifflin.inc\SHARES\MSCOTT
Ioctl Response, Error: STATUS_NOT_FOUND

En cherchant un peu sur le web, je suis tombé sur des cas où un référent DFS a été créé et supprimé rapidement, entraînant un souci lors de la redescente des informations sur le poste de travail. Le poste de travail tentait alors d'interroger un référent DFS qui n'existait plus. Ce n'était pas mon cas, en utilisant l'outil dfsutil comme ceci, je n'avais aucune trace du mauvais référent :

dfsutil /pktinfo

Cette commande permet de sonder les référents DFS qui répondent aux requêtes DFS soumises par les postes clients. Je n'avais aucun référent caduc ni aucun code d'erreur particulier. En continuant de creuser avec mon tractopelle, je suis tombé sur l'outil dfsdiag qui allait me mettre sur la bonne voie.

dfsdiag permet d'effectuer des tests sur le bon fonctionnement de l'infrastructure DFS. Voici l'instruction que j'ai utilisé :

dfsdiag /testdfsintegrity /DfsRoot:\\dundermifflin.inc\SHARES /Recurse

J'ai donc bien une erreur concernant ce namespace :

Je teste sur un autre namespace pour lequel je n'ai eu aucun cas de remonté et qui est porté par des serveurs différents, et je n'ai pas ce message d'erreur.

Je recherche donc sur le net à propos de cette erreur et je tombe sur ce lien salvateur. Les namespaces DFS portés par un serveur doivent être enregistrés dans le registre ; il s'agit des clefs présentes dans l'arborescence Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DFS\Roots\domainV2\. Dans le cas du serveur indiqué dans le message d'erreur, je n'ai rien, par contre sur un autre serveur du pool DFS, j'ai bien des clefs qui correspondent aux namespaces, avec pour chacune d'entre elles, deux valeurs de type REG_SZ (chaîne de caractères) LogicalShare et RootShare qui contiennent le nom du namespace en question (cachés sur la capture) :

J'ai donc procédé à la création des clefs et valeurs correspondantes sur le serveur incriminé, puis relancé le service DFS Namespace. En exécutant de nouveau dfsdiag, l'erreur a disparu et je n'ai plus eu de cas de dysfonctionnements d'accès aux partages DFS.

Fermeture de force d'une session RDP avec rwinsta

J'ai eu le cas d'une session utilisateur sévèrement plantée sur un serveur Windows : tellement plantée que le login n'était même plus visible dans le gestionnaire de tâches et qu'il était impossible de fermer via le menu contextuel. L'utilisateur était incapable de se reconnecter en RDP sur la machine.

Afin de clôturer la session de force, je vais avoir recours à deux outils en ligne de commande : qwinsta et rwinsta ; qwinsta va me permettre de faire la requête afin de récupérer l'identifiant de la session plantée et rwinsta va me permettre de tuer cette session.

Sur la capture d'écran, ma session vrillée correspond à l'ID 63. Les deux autres sont actives et la mienne est indiquée par le signe > devant le nom. Il suffit d'appeler la commande suivante pour forcer la fermeture de cette session.

rwinsta 63

Powershell : liste des entrées DNS sans reverse

Ce script permet d'afficher la liste des enregistrements DNS "A" d'une zone qui n'ont pas d'enregistrements reverse DNS (PTR). Après affichage des enregistrements, un export CSV est réalisé permettant une exploitation plus simple dans Excel ou simplement une injection dans un autre script.

param([Parameter(Mandatory=$true)][string] $Zone, [Parameter(Mandatory=$true)][string] $DNSHost)

$records = Get-DnsServerResourceRecord -Computer $dnshost -ZoneName $zone -RRtype "A"
$output = @()
foreach($record in $records){
	$ipStr = $record.RecordData.IPv4Address.ToString()
	$ipB1 = $ipStr.SubString(0,$ipStr.IndexOf("."))
	$ipstr = $ipstr.SubString(($ipB1.Length)+1,($ipstr.Length-$ipB1.Length)-1)
	$ipB2 = $ipStr.SubString(0,$ipStr.IndexOf("."))
	$ipstr = $ipstr.SubString(($ipB2.Length)+1,($ipstr.Length-$ipB2.Length)-1)
	$ipB3 = $ipStr.SubString(0,$ipStr.IndexOf("."))
	$ipstr = $ipstr.SubString(($ipB3.Length)+1,($ipstr.Length-$ipB3.Length)-1)
	$ipB4 = $ipStr.Substring(0,$ipStr.Length)
	$zoneptr = $record.RecordData.IPv4Address.ToString().SubString(0,$ipB1.Length)+".in-addr.arpa"
	if($ipB1 -ne $lastzone) { $recptr = Get-DnsServerResourceRecord -Computer $dnshost -ZoneName $zoneptr }
    $lastzone = $ipB1
	$ipptr = $ipB4+"."+$ipB3+"."+$ipB2
	if(($recptr | Where-Object { $_.HostName -like $ipptr }) -eq $null) { 
		Write-Host $record.HostName";"$record.RecordData.IPV4Address 
		$noptr = New-Object PSCustomObject
		$noptr | Add-Member -Name "Hostname" -Value $record.HostName -MemberType NoteProperty
		$noptr | Add-Member -Name "IPV4Address" -Value $record.RecordData.IPV4Address -MemberType NoteProperty
		$output+=$noptr
		}
}
$output | Export-CSV DNSnoPTR.csv -Encoding UTF8

Le script prend en paramètre ZoneName le nom de la zone DNS que l'on souhaite scanner et DnsHost le nom du serveur DNS qui renverra les enregistrements. Le script pourra donc être appelé de cette manière :

.\win_dnsnoptr.ps1 -Zone intra.dundermifflin.inc -DNSHost dc01

Comme toujours, le script avec quelques commentaires est disponible en téléchargement.

Powershell : affichage de la taille de répertoires

M'occupant de migrer des serveurs de fichiers vers des versions plus récentes de Windows et ayant conçu un nouveau "design" de ceux-ci, j'ai développé un script Powershell qui fait +/- le même travail que l'application TreeSize. L'avantage est que ce script peut appeler des répertoires réseau, ce que ne permet pas l'application dans sa version gratuite.

Ce script peut-être une bonne base pour être amélioré ensuite pour n'afficher que les répertoires plus gros qu'une taille spécifiée. Il est également aisé de faire un export CSV des valeurs retournées (il suffira de piper $results avec un Export-CSV).

param([string]$path)
$tierone = Get-ChildItem $path -Directory
$results = @()
foreach($toneitem in $tierone){
	$size = 0
	$tiertwo = Get-ChildItem $toneitem.FullName -Recurse -File | select Name, Length
	foreach($ttwoitem in $tiertwo){
		$size += $ttwoitem.Length   
	}
    $size = [math]::Round($size/1048576,0)
	$pso = New-Object PSCustomObject
	$pso | Add-Member -Name "Name" -Value $toneitem.Name -MemberType NoteProperty
	$pso | Add-Member -Name "Size (M)" -Value $size -MemberType NoteProperty
	$results+=$pso
}
$results

Ce script me permet donc de visualiser rapidement la taille des répertoires principaux d'une arborescence, et de pouvoir redistribuer plus efficacement ces répertoires sur des disques d'une taille plus petite.

Une version du script avec des commentaires est disponible.