Comment signer un script Powershell ?
Sommaire
I. Présentation
Aujourd'hui dans un environnement Microsoft, il y a mille et une raisons d'utiliser PowerShell ! Que ce soit pour l'administration et la configuration des serveurs, mais aussi pour exécuter des scripts via GPO sur les postes clients. Pour avoir cette possibilité qui est clairement indispensable, sans remettre en cause la sécurité des postes clients, il s'avère pertinent de signer les scripts PowerShell pour autoriser uniquement les scripts signés sur vos postes clients. Ceci vous permettra d'exécuter vos scripts sur un poste où la stratégie d'exécution PowerShell est définie sur "AllSigned".
Avant de continuer, vous devez avoir une autorité de certification opérationnelle, vous pouvez lire notre tutoriel à ce sujet : Créer une autorité de certification ADCS. Pour les tests, prévoyais également un poste client dans le domaine.
II. Générer un certificat pour signer du code
Connectez-vous sur votre autorité de certification (où le rôle ADCS est présent) et ouvrez une console PowerShell en tant qu'administrateur. Nous allons devoir créer un certificat dédié à la signature de code, mais avant cela il faut inclure le template grâce à la commande suivante :
Add-CATemplate -Name CodeSigning
Confirmez l'exécution de l'action, et listez les templates de votre autorité de certification : "CodeSigning" doit apparaître.
Toujours sur le serveur ADCS, ouvrez une console MMC vierge et ajoutez le composant "Certificats" pour l'utilisateur actuel.
Sur "Personnel", effectuez un clic droit et sélectionnez la tâche "Demander un nouveau certificat".
Sélectionnez "Stratégie d'inscription à Active Directory.
Nous allons demander un certificat "Signature du code", sélectionnez la case adéquate.
Si l'on regarde les détails, nous pouvons voir que le certificat sera valide 365 jours. Avant de cliquer sur "Inscription", cliquez sur "Propriétés" puis dans l'onglet "Clé privée" activez l'option "Permettre l'exportation de la clé privée".
Si vous obtenez le message "Opération réussie" alors c'est tout bon ! Vous pouvez afficher le certificat si vous le souhaitez.
Le certificat est bien valide 365 jours comme nous pouvons le voir (bon, j'avoue, ça fait un moment que j'ai pris les captures d'écrans...).
Maintenant que le certificat est créé, il ne reste plus qu'à l'utiliser pour signer un script PowerShell 🙂
III. Déployer le certificat sur les postes clients
Le certificat doit être approuvé par les postes clients, nous allons le déployer par GPO. Pour cela, il faut l'exporter donc toujours à partir de la console MMC précédemment ouverte, effectuez un clic droit sur le certificat puis lancez la tâche "Exporter".
Suivez l'assistant et sélectionnez les deux paramètres suivants : "Non, ne pas exporter la clé privée" et "X.509 binaire encodé DER (*.cer)". Enfin, donnez un nom au certificat et exportez-le.
Connectez-vous sur votre contrôleur de domaine et copiez le certificat dessus. Ensuite, créez une nouvelle GPO qui va déployer le certificat.
Voici à quel endroit vous devez importer le certificat (via un clic droit "Importer" une fois dans "Editeurs approuvés").
La GPO est prête, positionnez-la sur votre domaine pour que le certificat soit déployé ! Passons à la suite.
IV. Signer un script PowerShell
Basculez maintenant sur un poste client, idéalement Windows 10 puisque c'est la version qui tend à être utilisée à la place de Windows 7 petit à petit. Mettez à jour les GPO sur le poste et redémarrez-le :
gpupdate /force
Nous allons définir la politique d'exécution des scripts du poste sur "AllSigned" :
Set-ExecutionPolicy AllSigned
Maintenant qu'il est redémarré, le certificat doit être présent dans le magasin local. Nous pouvons le vérifier avec cette commande :
Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert
Maintenant, nous allons exécuter le script "IT-CONNECT.ps1" qui n'est pas signé et vous pourrez constater que ça ne fonctionne pas. Ceci est logique car la stratégie est définie sur "AllSigned". Ce qui donne l'erreur suivante :
Tous les éléments étant maintenant réuni pour que l'on signe notre script PowerShell, on ne va pas s'en priver ! Pour signer le script, on va utiliser le code suivant :
$CertCodeSigning = Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert $ScriptToSign = "C:\Scripting\Scripts\IT-CONNECT.ps1" $TimestampServer = "http://timestamp.comodoca.com/authenticode" Set-AuthenticodeSignature $ScriptToSign -Certificate $CertCodeSigning -TimestampServer $TimestampServer
Voici quelques infos sur les variables utilisées :
- CertCodeSigning : Chemin vers lequel récupérer le certificat de signature du code
- ScriptToSign : Chemin vers le fichier PS1 de notre script PowerShell
- TimestampServer : On s'appuie sur un service externe qui va permettre de dater notre signature, ainsi, lorsque le certificat sera expiré la signature restera valable car elle fût réalisée pendant que le certificat était valide
Enfin, on va utiliser ces 3 variables dans le cmdlet Set-AuthenticodeSignature pour signer le script PowerShell.
Si l'on ouvre le script, on voit qu'il contient maintenant une signature :
Retournez sur le poste client et exécutez le script à nouveau : il doit maintenant s'exécuter directement 😉
Voilà, vous pouvez maintenant mettre en place cette configuration et dégainer les signatures sur tout vos scripts PowerShell en production. Bien entendu, à chaque fois que vous modifiez un script déjà signé, il faudra le signer à nouveau.
En bonus, je vous livre un bout de code qui va signer automatiquement tous les fichiers PS1 contenu dans un dossier. Il vous suffit de modifier le chemin du dossier contenu dans la variable $ScriptFolder :
$CertCodeSigning = Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert $TimestampServer = "http://timestamp.comodoca.com/authenticode" $ScriptFolder = "C:\Dossier\Scripts" $ScriptList = (Get-ChildItem $ScriptFolder -Filter "*.ps1").FullName Foreach($ScriptToSign in $ScriptList){ Write-Output "Traitement du script : $ScriptToSign" Set-AuthenticodeSignature $ScriptToSign -Certificate $CertCodeSigning -TimestampServer $TimestampServer }
Bonjour,
Merci pour cet article. Je me demandai si il est possible de généré un certificat sur l’ADCS d’un domaine et appliquer ce certificat sur un poste qui n’est pas dans le domaine.
Je cherche a signé un script powershell qui sera installé sur un poste connecté a aucun réseau ni domaine.
Merci par avance.
Gael
Bonjour,
Pour moi oui c’est possible, mais il faudra que le poste dispose du certificat racine de votre autorité de certification pour qu’elle soit approuvée en tant que CA de confiance 🙂
Opération à réaliser manuellement comme le poste est hors domaine.
Florian
Bonjour,
Merci pour cet article.
Mais le cmdlet « Add-CATemplate » n’existe pas chez moi, comment faire pour l’avoir ? c’est un module a ajouter ?
Windows 10 pro 1903 avec RSAT.
Merci par avance.
0relien.
Le module n’existe pas sur mon poste client, mais existe sur les serveurs avec le rôle CA.
Bonjour 0relien,
Oui c’est normal c’est un cmdlet intégré au module ADCSAdministration donc il s’est ajouté automatiquement sur ton serveur autorité de certification, ce qui n’est pas le cas par défaut sur ton PC.
Cordialement,
Florian
Bonjour,
Lorsque je déploie le certificat via GPO, Il ne se retrouve pas dans le magasin local Cert:\CurrentUser\My
Il y a bien un certificat qui se retrouve dans les éditeurs approuvés mais lorsque j’utilise le paramètre -CodeSigningCert je n’ai aucun retour de la commande…
J’ai suivi la manipulation pas à pas et je comprends pas pourquoi cela ne s’applique pas comme sur la manipulation.Pourtant ma GPO s’applique bien…
Auriez vous une solution ou un indice pour mes recherches ?
Merci d’avance
Bien à vous,
Bonjour,
J’ai eu le même problème, essaie de taper les commandes depuis le serveur ADCS.
@Hendryckx Logan : Même problématique que toi, j’ai pu récupérer la réponse a la commande, en faisant une demande de certificat via le magasin Current User, personnel puis Request New Certificat sur un poste ou srv.
Uniquement par cette methode, la commande Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert me renvoi bien un résultat.
Bonjour,
Merci beaucoup cet article. Cependant, je suis comme Logan, en suivant pas à pas la procédure, mon certificat se déploie parfaitement mais à l’application de la commande Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert je n’ai aucun résultat.
Comment résoudre ce problème ?
Comment faire une demande de certificat via le current user, personnel car lorsque je le fais depuis la console MMC, je n’ai pas l’autorisation de demander des certificats de signature de code.
Merci pour vos retours et solutions.
Cordialement
Pour ceux qui ont posé la question avant moi, j’ai passer 2 jours à chercher pourquoi ça marchait et j’ai trouver pourquoi et je vais vous expliquer.
1 : Quand on fait « Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert », il prend le certicat de signature dans Utilisateur actuel\Personnel, donc si on lance depuis le ADCS ça marche car le certificat d’origine est la mais il est pas dans votre ADDS (par exemple).
2 : Dans votre magasin personnel ADCS, vous voyez bien que votre certificat il y a une colonne « Rôles prévus » et marquer dedans « Signature du code ». Le probleme c’est que SI vous l’exporter sans la clé privéé, bah cette colonne disparait et votre certificat ne peux PAS signer du code.
En faite, il faut l’exporter AVEC la clé privée et si vous faites le déploiement du fichier .pfx votre certificat a la colonne « Rôles prévus » avec « Signature du code » dedans.
Si vous tester la commande « Get-ChildItem Cert:\LocalMachine\TrustedPublisher\ -CodeSigningCert » normalement le certificat remonte MAIS j’ai pu le faire marcher que sur ADCS car le -CodeSigningCert est propre à ADCS.
Hello,
Je n’ai pas encore appliqué le tuto pour voir ce que ça donne, mais en l’état ça ne marche que dans le cas où il n’y a qu’une personne pour signer les scripts.
Dès lors qu’il y a plusieurs personnes qui peuvent signer leurs scripts, il faudrait soit exporter le certificat avec la clé sur chaque poste signataire, soit publier le certif sans clé de chacun des certificats générés depuis ces postes signataires.
ça me parait bien lourd.
Dès lors que le certificat est issue d’une CA d’entreprise (ADDS), le code ne peut-il pas être reconnu comme légit puisque le certif de la CA est déjà connu ? à l’image de ce qu’il se passe par exemple pour un serveur web en interne, si son certif est issue de la CA d’entreprise, on n’a pas besoin d’exporter le certificat de chaque serveur web sur le poste client (et heureusement).