Restauration autoritaire de SYSVOL

J’aime bien commencer une belle et chaude semaine d’été par une restauration autoritaire de SYSVOL. Ou comment annoncer gentillement que bordel de merde, c’est pas le moment, j’avais vraiment autre chose à faire que de me prendre le choux avec ça. Ce billet détaille ma résolution pour remettre debout l’infra Active Directory.

État des lieux

Avant toute chose, il est de rigueur de décrire le parc et les versions utilisées. En effet Microsoft a introduit énormément de changements à partir de Windows Server 2008, et la procédure décrite ici ne pourra en conséquence pas fonctionner pour un contrôleur de domaine Windows Server 2003 ou plus ancien. Pour être plus précis, la procédure ne peut s’appliquer que lorsque le niveau fonctionnel du domaine est au minimum fixé à Windows Server 2008.

Le parc est plutôt modeste et est, pour la partie qui nous concerne, composé de :

  • 1 contrôleur de domaine sous Windows Server 2012 (AZSHARA)
  • 1 contrôleur de domaine sous Windows Server 2012R2 (ALEXSTRASZA)
  • le niveau fonctionnel du domaine est fixé à Windows Server 2012
  • le niveau fonctionnel de la forêt est fixé à Windows Server 2012
  • un script PowerShell qui s’exécute au démarrage de chaque ordinateur via une GPO. Le script est enregistré quelque part dans le partage SYSVOL

sysvol_script_ps

Symptômes

J’avoue qu’il s’agit d’un problème qui aurait pu être évité, mais ne connaissant aucun indicateur fiable « OK, ton domaine fonctionne normalement, tout est bien synchronisé » (en revanche si vous en connaissez un, un script ou n’importe quoi qui puisse me donner l’état de santé du domaine, je suis preneur), j’ai du faire avec les petits moyens dont je dispose.
A priori, les contrôleurs de domaine fonctionnaient normalement, les utilisateurs pouvaient se logguer, les contrôleurs étaient redémarrés régulièrement pour appliquer les mises à jour Windows. Cependant il existait un phénomène assez curieux mais sur lequel je ne me suis penché qu’assez tardivement : de temps en temps, les changements que je faisais sur le script PowerShell n’étaient pas pris en compte lorsque je le testais par la suite sur une VM de test. Ce script est mis à jour assez régulièrement car il sert à déployer tout ce qui ne repose pas sur du MSI, c’est à dire Firefox, Filezilla, les grosses mises à jour Visual Studio etc…
Après une énième occurrence du phénomène, je décide de prendre 5 minutes pour regarder de plus près ce qu’il se passe vraiment. J’accède alors au partage SYSVOL où se trouve le script sur chaque contrôleur de domaine et là, c’est le drame : chaque serveur possède une version du script différente ; le partage n’est absolument plus synchronisé, dans un sens comme dans l’autre.

Diagnostic

Comme toujours dans ces cas là, on part à la chasse aux informations. Je trouve assez rapidement la raison pour laquelle il n’existe plus de réplication dans le journal d’événement d’un des contrôleurs de domaine.

event_bad-stop

C’est à partir d’ici où il est important de savoir dans quel niveau fonctionnel le domaine se trouve. Les contrôleurs de domaines sont tous maîtres, il n’y a pas (par défaut) de relation 1 maître – x esclaves comme c’est souvent le cas pour les serveurs de bases de données transactionnelles. Les données synchronisés entre les contrôleurs de domaine sont :

  • l’annuaire. Il contient entre autre tous les utilisateurs, groupes, unités organisationnelles, les enregistrements DNS, la topologie de réplication.
  • le partage SYSVOL, il contient mon script et des fichiers en relation directe avec les stratégies de groupe (les GPOs) mises en place.

Etant donné que le problème ne se situe pas sur l’annuaire, la commande repadmin.exe ne sera d’aucun secours. A la place, il faudra utiliser la commande dfsrdiag.exe.
Vu que cette dernière est bien barbare et demande un certain nombre de paramètre non évident, j’ai préféré examiner la réplication via la console de gestion du système de fichiers distributés DFS (dfsmgmt.msc).
Cette console permet de créer un rapport d’intégrité (première option), ce que j’ai fait ou de tester la propagation (seconde option pour lancer le test, troisième option pour suivre la progression du test)
Je vous conseille fortement d’ouvrir ce rapport exclusivement avec Internet Explorer sous peine d’avoir du hachis parmentier à l’écran avec un autre navigateur.

rapport-1 rapport-2 rapport-3 rapport-4

 

Résolution

Je suis donc la procédure décrite dans l’événement pour relancer la synchronisation, cela nous mène au second drame.

Et impose un choix à faire parmi deux propositions :

  • on suit les recommandations de l’événement et on considère que la copie du partage SYSVOL est foutue car trop vieille. La bonne copie est donc celle se trouvant sur l’autre contrôleur de domaine. C’est souvent le choix à faire dans le cas où l’on a au moins 3 contrôleurs de domaines dont 2 au minimum ont une réplication fonctionnelle du partage SYSVOL.
  • on fait sa tête de mule et l’on décide que finalement la vieille copie est la bonne et devrait écraser l’autre copie.

Restauration autoritaire / non-autoritaire

  • Si vous avez un contrôleur de domaine avec un SYSVOL propre, alors vous pouvez (et même devriez) effectuer une restauration dite non-autoritaire, c’est à dire que l’on va invalider la copie du SYSVOL foutue, afin que le contrôleur de domaine qui héberge la copie la resynchronise à partir des autres contrôleurs de domaine. Dans mon cas, cela se traduit par AZSHARA qui va synchroniser son partage SYSVOL à partir de ALEXSTRASZA
  • Si vous avez opté pour le second choix ou bien si aucun contrôleur de domaine n’a un SYSVOL propre, alors il est nécessaire d’effectuer une restauration dite autoritaire, c’est-à-dire que l’on va explicitement déclarer qu’un des contrôleurs de domaine a la « bonne » copie du SYSVOL, et tous les autres contrôleurs de domaines vont effectuer une resynchronisation sur cette copie. La restauration autoritaire est à considérer comme la solution de la dernière chance, il est préférable tant que faire se peut d’effectuer en priorité une restauration non-autoritaire.

Sources

Authentification SSH par AD

J’essaie lorsque c’est possible d’éviter la sur-multiplication des mots de passe à gérer. Dans un environnement principalement Microsoft, Active Directory permet de résoudre assez bien ce problème, c’est cool. Un annuaire pour les unir tous (et dans les ténèbres les lier).

Qu’en est-il des distributions Linux ? Je gère un petit nombre de serveurs sous Linux, pour lesquels l’intérêt de les intégrer dans un domaine Active Directory est proche du néant (sans parler du foutoir à mettre en place côté Linux). En effet, il n’y a pas de GPO, l’authentification par mot de passe est désactivé sur ces serveurs, et seul quelques personnes doivent pouvoir y avoir accès et de manière limitée.

Je me suis donc orienté vers une solution où la base utilisateurs des serveurs Linux reste locale (via /etc/passwd et /etc/shadow donc) mais en cherchant à centraliser la clé SSH de ces utilisateurs avec Active Directory.

David Gwynne (voir source) s’est déjà penché sur ce problème et a exhibé un paramètre contenu dans OpenSSH permettant de contrôler l’étape d’authentification via clé publique de manière très flexible, ce paramètre acceptant un script en entrée.

Le principe est alors très simple :

  1. un utilisateur tente de se connecter au serveur linux en SSH
  2. le serveur SSH récupère le nom d’utilisateur et exécute le script indiqué par le paramètre AuthorizedKeysCommand
    1. par mesure de sécurité, OpenSSH propose d’exécuter ce script sous une autre identité, ce qui est fortement préférable vu qu’OpenSSH est root
    2. ce script va se connecter à un serveur LDAP à l’aide d’identifiants dédié
    3. il va récupérer l’objet utilisateur correspondant au nom d’utilisateur, et s’il existe retourner sa ou ses clés publique SSH

L’intérêt consiste à éviter de devoir manipuler le fichier /home/{user}/.ssh/authorized_keys2 sur chaque serveur pour chaque utilisateur, la création d’un accès se faisant alors en deux temps :

  1. création du compte sur le serveur linux avec la commande useradd
  2. si ce n’est pas déjà fait, ajout de sa clé dans Active Directory

Mise en place de la solution

  1. Dans le fichier /etc/ssh/sshd_config, on ajoute/modifie les lignes suivantes:
AuthorizedKeysCommand /etc/ssh/authorized_keys.pl
AuthorizedKeysCommandUser _sshkeys

# si ce n'est pas déjà fait, ci-dessous les paramètres à modifier pour désactiver l'identification par mot de passe
PasswordAuthentication no
ChallengeResponseAuthentication no

2. Contenu du fichier /etc/ssh/authorized_keys.pl. Il faudra renseigner le nom du domaine, le nom d’utilisateur et mot de passe d’un compte pour se connecter à l’annuaire.

#!/usr/bin/perl -w

use strict;
use Net::DNS;
use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_INVALID_CREDENTIALS);
use Net::LDAP::Util qw(escape_filter_value);

sub ad_bind($$$)
{
	my $domain = shift;
	my $user = shift;
	my $pass = shift;

	my $res = Net::DNS::Resolver->new;
	my $query;
	my $answer;
	my $ldap_error;

	$query = $res->query("_ldap._tcp." . $domain, 'SRV');
	if (!$query) {
		die "unable to query SRV records for $domain";
	}

	my @answers = $query->answer;
	foreach $answer (@answers) {
		# next if ($answer->port != 389)

		my $ldap = new Net::LDAP($answer->target, timeout => 2);

		$ldap_error = $@;
		next unless ($ldap);

		my $mesg = $ldap->bind(sprintf("%s@%s", $user, uc($domain)),
			password => $pass);
		return ($ldap) if ($mesg->code == LDAP_SUCCESS);

		if ($mesg->code == LDAP_INVALID_CREDENTIALS) {
			return (undef, $mesg->error);
		}

		$ldap_error = $mesg->error;
	}

	return (undef, $ldap_error);
}

if (scalar @ARGV != 1) {
	die "username not specified";
}

my $username = $ARGV[0];
my $ad_filter = sprintf('(&(sAMAccountName=%s)(objectClass=user))', escape_filter_value($username));

my %d = (
	'kveer.fr' => {
		'user' => 'sshkeys',
		'pass' => 'ton_mot_de_passe',
		'base' => 'DC=kveer,DC=fr',
		'filter' => $ad_filter
	}
);

foreach my $domain (keys %d) {
	my $param = $d{$domain};

	my ($ds, $err) = ad_bind($domain, $param->{'user'}, $param->{'pass'});
	next unless ($ds);

	my $mesg = $ds->search(
		base	=> $param->{'base'},
		filter	=> $param->{'filter'},
		attrs	=> ['altSecurityIdentities']
	);
	next unless ($mesg->code == LDAP_SUCCESS);

	for (my $i = 0; $i < $mesg->count; $i++) {
		my $entry = $mesg->entry($i);

		my @ids = $entry->get_value('altSecurityIdentities');
		foreach my $id (@ids) {
			my ($type, $value) = split(/:/, $id, 2);
			print "$value\n" if ($type eq 'SSHKey');
		}
	}
}

3. puis enfin quelques commandes pour terminer la configuration (à adapter en fonction de la distribution)

#!/bin/bash

USERNAME=_sshkeys
SCRIPT_FILE=/etc/ssh/authorized_keys.pl

useradd -s /sbin/nologin -d /var/empty/ $USERNAME
chmod 750 "$SCRIPT_FILE"
chown root:"$USERNAME" "$SCRIPT_FILE"

Ajouter la clé SSH

Pour chaque utilisateur devant avoir un accès nunux, il suffit de déclarer leur clé publique au sein d’Active Directory, dans l’attribut altSecurityIdentities. L’avantage de cet attribut est qu’il existe déjà, et fonctionne à l’aide d’un préfixe sur les valeurs, de sorte à ce qu’il puisse être utilisé par un acteur différent (ici OpenSSH) sans impacter l’usage initial qui en est fait de cet attribut. Ci-dessous à travers l’outil de gestion graphiques de l’annuaire dsa.msc.

ssh_ad_dsa

Remarques

  • Le script n’est pas parfait et peut être améliorer. Il ne vérifie pas par exemple si le compte en question est désactivé ou non.
  • Le script utilise l’attribut altSecurityIdentities. On aurait très bien pu créer un attribut dédié, mais cette solution a l’avantage d’éviter de modifier manuellement le schéma AD.

Source

Active Directory: le service de réplication de fichiers échoue

Dès lors qu’il y a plusieurs contrôleurs de domaines dans un domaine Active Directory, il peut y avoir des problèmes de réplication des fichiers, ce qui empêche la fonction de contrôleur de domaines sur les serveurs sur lesquels il n’y a pas eu de réplication, et là c’est la merde.

La réplication en question concerne les fichiers des dossiers partagés SYSVOL et NETLOGON. Ils contiennent entre autres les stratégies de sécurité, et les scripts de démarrage.
Afin d’être opérationnel, tous les contrôleurs de domaines doivent avoir exactement les mêmes fichiers dans les partages SYSVOL et NETLOGON.

Une des solutions est de forcer un des contrôleurs de domaines, souvent l’émulateur PDC, à être autoritaire sur la réplication de fichiers, de sorte que tous les autres contrôleurs de domaines le voient comme le master. Pour ce faire, arrêtez le service de réplication de fichiers (ceci est nécessaire pour ne pas réinitialiser la valeur du registre) puis naviguez jusqu’à la clef :

HKLM\SYSTEM\CurrentControlSet\Services\NtFrs\Paramaters\Backup/Restore\Process at Startup.

Editez ensuite la valeur DWORD BurFlags  et mettez lui la valeur D4.
Démarrez le serveur de réplication de fichiers, et le problème se résolvera tout seul. Les serveurs à problèmes se verront créer les partages SYSVOL et NETLOGON avec le contenu correct.

Voir aussi

Active Directory: le service de réplication de fichiers échoue (le retour)

Cette fois tout semble correcte, mais la réplication échoue encore avec l’événement 13552, la source Ntfrs et l’erreur FrsErrorJournalInitFailed.

L’erreur signifie tout simplement qu’il n’y a pas assez d’espace disque restant pour le service de réplication de fichiers. Il faut en effet au minimum 512Mo d’espace disque libre pour que le tout fonctionne. Après avoir fait le ménage, et si cela est toujours insuffisant, il est toujours possible de réduire la taille du journal pour l’adapter à l’espace disque restant. Pour cela, allez dans le registre à la clé HKLM\System\CurrentControlSet\Services\NtFrs\Parameters  et éditer la valeur DWORD Ntfs Journal size in MB . La valeur par défaut est de 512, la valeur du temps de Windows 2000 Server était de 128.

Redémarrez le service de réplication de fichiers une fois l’opération terminée.

Voir aussi

Gene6FTP: partage réseau inaccessible

Gene6FTP Server n’arrive plus à synchroniser un utilisateur Active Directory. Les partages réseaux sont en accès refusés et pourtant vous avec tous les droits (WTF ?).

Dans le gestionnaire d’événements, vous verrez alors :

Event ID: 2011 Source: Srv The server's configuration parameter "irpstacksize" is too small for the server to use a local device. Please increase the value of this parameter.

Dans ce cas, il faut faire un tour dans la base de registre et créer la valeur DWORD HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer \Parameters\IRPStackSize  avec une valeur par défaut de 15 si inexistante, puis augmentez-la de 3 jusqu’à ce que ça marche.

Redémarrez l’ordinateur à chaque fois pour tester le changement.

Voir aussi