Migration vers Ansible Vault

Je l’avais évoqué plusieurs fois dans quelques articles, j’utilise ansible pour gérer l’installation automatique des services que j’auto-héberge. J’étais jusqu’à présent resté dans une gestion simple des mots de passe liés aux services (comprendre « exposé en clair dans les fichiers »). Vraiment pas terrible du point de vue sécurité, mais permettant de me concentrer sur la partie (ré-)installation automatique, là où réside le vrai gain de temps. Disposant de quelques jours de congés, je me suis donc attelé à ce sujet longtemps repoussé qu’est la migration des secrets vers le vault ansible.

Un vault ansible, en quelques mots, est un fichier chiffré contenant des pairs clé/valeur permettant de référencer un secret dans un script ansible.

Configuration

Côté implémentation, j’ai choisi de stocker mon vault dans le dossier group_vars/all/ et j’ai commencé en créant deux fichiers all.yml et vault_all.yml. Dans vault_all.yml, je stocke l’ensemble de mes secrets associés à un identifiant, par exemple, le mot de passe secretpassword associé à la clé vault_test_user_password donnera le fichier suivant :

---
vault_test_user_password: secretpassword

Ensuite, dans all.yml, je référence la variable du vault et l’expose dans une nouvelle variable. Cette étape n’est pas forcément nécessaire, il est tout à fait possible d’utiliser directement l’identifiant présent dans le vault. L’intérêt de cette méthode quelque peu répétitive au moment de la mise en place, réside dans le fait que l’on dispose d’un inventaire des variables stockées dans notre fichier vault, sans avoir besoin de déchiffrer le fichier vault pour parcourir son contenu. Voici donc, en reprenant l’exemple ci-dessus, le contenu de notre fichier de référence :

---
test_user_password: "{{ vault_test_user_password }}"

Maintenant que je dispose de mon dictionnaire de secrets, je peux passer à l’opération de chiffrement de ce dernier. Par défaut, ansible demandera le mot de passe à utiliser à chaque opération de chiffrement/déchiffrement du fichier, ce qui se révèle vite rébarbatif. Heureusement, il est possible de créer un fichier .vault_pass pour y stocker le mot de passe du vault. Évidemment, on prendra soin de l’ajouter à notre .gitignore, afin de ne pas commit cette information dans notre dépôt git. Dernière étape, référencer l’emplacement du mot de passe dans la configuration ansible, soit dans ansible.cfg, par ajout de la ligne :

[defaults]
vault_password_file = .vault_pass

Maintenant que tout est prêt, il convient de chiffrer le fichier vault. Pour effectuer cette opération, on utilisera la commande :

ansible-vault encrypt group_vars/all/vault_all.yml

Le fichier vault étant désormais chiffré, l’édition s’effectuera désormais avec :

ansible-vault edit group_vars/all/vault_all.yml

Parmi les autres opérations de la commande ansible-vault, on notera en particulier decrypt, view et rekey.

Utilisation

Maintenant que tout est configuré, je peux donc remplacer les références directes au mot de passe présent dans mes playbooks, par les nouvelles variables créées. Toujours avec l’exemple précédent, on passera de l’extrait de playbook suivant :

password: secretpassword

au nouveau contenu « sécurisé »:

password: "{{ test_user_password }}"

Il ne reste plus qu’à déclencher l’exécution de l’un des playbooks afin de vérifier que tout a été configuré correctement. Je pourrais m’arrêter là; les secrets de mes playbooks sont maintenant chiffrés dans un fichier dédié, tout va pour le mieux, mais un dernier point de faiblesse subsiste, le mot de passe du vault stocké en clair dans notre fichier .vault_pass. Évidemment, si un attaquant devait mettre la main sur ce fichier, il y a fort à parier que ce serait le cadet de mes soucis, mais voyons tout de même si je peux améliorer les choses.

Sécurisation du mot de passe Vault

En parcourant la documentation ansible liée au concept de vault, j’ai découvert que le paramètre de configuration vault_password_file est susceptible de prendre un fichier de script exécutable en entrée. Seule condition, le script doit renvoyer le mot de passe à utiliser. Étant donné que j’utilise déjà password-store en ligne de commande pour la gestion de mes mots de passe, j’y ai vu une occasion parfaite d’augmenter encore la sécurité en stockant le mot de passe du vault dans une entrée de mon password-store. Quelques recherches plus tard, j’obtiens donc le script suivant; script chargé de retourner le contenu de l’entrée ansible-vault.

#!/bin/bash

###################################
## Get password from password store
# Inspired by https://github.com/paulRbr/ansible-makefile/blob/master/pass.sh
###################################

if (command -v pass >/dev/null 2>&1)
then
    existingVault=$(pass "ansible-vault" || true)

    if [ -n "${existingVault}" ]
    then
        >&2 echo "Using passphrase found at 'ansible-vault' in your password store."
        echo "${existingVault}"
    else
        >&2 echo "No passphrase found at 'ansible-vault' in your password store."
        exit 0
    fi
fi

Ma configuration ansible devient donc (Ne pas oublier de rendre le fichier de script exécutable):

[defaults]
vault_password_file = ./get-vault-pass.sh

Autres améliorations

Il est possible d’avoir plusieurs vaults dans un projet ansible. Dans l’exemple précédent, toutes les entrées du vault sont partagées avec l’ensemble des playbooks, du fait d’appartenir au groupe all. Pour simplifier l’organisation, j’ai dans mon cas déplacé tous les secrets liés à unicoda et à son playbook de déploiement dans le dossier group_vars/unicoda/ et son vault dédié. De la même façon, on peut alors envisager d’améliorer le script pour utiliser un mot de passe différent pour chaque vault, chacun des mots de passe étant alors stocké de façon chiffrée dans password-store.

Par ailleurs, j’ai découvert pendant la mise en place de vault, qu’il existe un plugin ansible maintenu par la communauté et qui permet d’aller chercher directement le contenu d’un mot de passe dans password-store. Je n’ai pas poussé plus avant l’expérimentation, mais l’utilisation de ce plugin pourrait être une alternative valable à l’utilisation de vault.

Conclusion

Grâce à cette dernière amélioration, mes secrets ansible sont désormais stockés en lieu sûr et ne sont plus accessible au premier venu. C’est un élément de sécurité que j’encourage à mettre en place immédiatement, dès que le premier secret apparaît dans le dépôt de code.

Sources

Des Headers. En veux-tu ? En voilà !

J’ai commencé à me pencher sur HAProxy et à faire quelques tests, ce qui m’a conduit à m’intéresser aux entêtes de sécurité qu’un serveur Web peut renvoyer. Dans la même veine que SSL Labs, j’ai découvert securityheaders.io qui permet de faire rapidement le point sur les entêtes des réponses http d’un serveur pour une url donnée.

De mon côté, je me suis donc assuré de la présence des entêtes suivantes:

  • Referrer-Policy
  • Strict-Transport-Security
  • X-Content-Type-Options
  • X-Frame-Options
  • X-XSS-Protection

Configurées de la manière suivante :

http-response set-header Strict-Transport-Security "max-age=63072000;"
http-response set-header X-Content-Type-Options nosniff
http-response set-header Referrer-Policy no-referrer-when-downgrade
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header X-XSS-Protection 1;mode=block

Par ailleurs, j’en profite pour m’assurer que les informations concernant le serveur répondant à la requête ne soient pas renvoyées :

http-response del-header Server

Du coup, j’obtiens un petit A, étant donné que je n’ai pour l’instant pas intégré l’entête « Content-Security-Policy ».

Source: Securing haproxy and nginx via HTTP Headers.

[SFR] Faille sur le WPS de la box

Comme vous le savez certainement, une faille de sécurité a été découverte sur le WPS du WiFi des box de SFR et d’Orange. A priori, Orange aurait déployé un correctif, SFR étudierait la question.

J’ai eu l’occasion de tester l’attaque sur la box SFR de mes parents (avec leur autorisation). Son efficacité est impressionnante: 4 secondes montre en main pour la seule opération de récupération de la clé WPA. J’en ai donc profité pour désactiver le WPS dans l’interface d’admin et vérifier la neutralisation de l’attaque.

Tous les détails sont sur le forum crack-wifi.

All your password are belong to us

Ou pourquoi un mot de passe se doit d’avoir une taille minimum et d’éviter de suivre un motif particulier.

Comme vous le savez certainement, il n’est pas toujours aisé, pour un hacker ayant découvert une faille de sécurité dans un système informatique, de contacter les responsables du système pour le leur signaler. Certaines entreprises, la première me venant à l’esprit étant Google, prennent facilement en compte les alertes (il me semble). Dans d’autres cas, le simple fait de signaler la faille vous assimile à un pirate…

Sur cette petite introduction, je vais maintenant revenir sur une situation à laquelle j’ai été confronté. Pas de faille de sécurité à proprement parler, mais un constat sur la politique de création et d’utilisation des mots de passe d’une plate-forme. Penchons-nous donc sur le problème.

La plate-forme en question n’héberge pas de données personnelles sensibles à proprement parler, pas de numéro de carte de crédit, pas d’adresse. Elle propose néanmoins un compte pour l’utilisateur qui contient donc un minimum d’informations le concernant: ses noms et prénoms notamment. Venons en au sujet qui fâche: le mot de passe d’accès à l’espace utilisateur. Premièrement, il n’est pas possible de le modifier via l’interface. Deuxièmement, et c’est là que certains des lecteurs vont commencer à s’étrangler. Le mot de passe n’est composé que de 5 caractères.

Regardons le nombre de mot de passe différents qu’il est possible d’obtenir si on considère que le mot de passe est généré avec au choix:
* les lettres de l’alphabet latin en minuscule ou en majuscule (52 possibilités).
* les chiffres (10 possibilités).
* les caractères spéciaux comme @ / [ {  (34 possibilités environ).
En résumé, pour chaque caractère: 52 + 10 + 34 = 96 possibilités.
Ce qui nous donne donc 96 *96 *96 *96 *96 = 96^5 = 8 153 726 976, un peu plus de 8 milliard de mot de passe différents. C’est peu mais ça reste beaucoup plus élevé que le nombre qui va suivre.

En effet, le massacre n’est pas fini… les mots de passe suivent un motif particulier. Ils sont constitués de l’année de naissance de l’utilisateur avec la première lettre de son nom. En résumé, une lettre et un nombre à 4 chiffres, 10 * 10 * 10 * 10 * 26 soit 260 000 possibilités. Nous sommes loin des 8 milliard initiaux et ça commence à faire peur.

Ce nombre peut encore être réduit, puisqu’on parle d’année de naissance. En considérant un intervalle simple d’une centaine d’année, on est presque sûr d’englober tous les cas possible, à l’exception des personnes plus que centenaire. En définitive, on se retrouve avec grand maximum 100 * 26 = 2 600 mots de passe différents ce qui est tout simplement ridicule.

Alors évidemment, on pourrait me faire remarquer qu’il faudrait connaître l’identifiant du compte pour que cela deviennent dangereux. Effectivement, et dans notre cas, oh malheur! L’identifiant est un nombre à 9 chiffres. A mon sens, la sécurité est insuffisante sur cette plate-forme, à cause d’une politique de génération des mots de passe défectueuse.

Imaginons un instant que l’on puisse tester un mot de passe par seconde… Vous voyez où je veux en venir n’est-ce pas? En moins d’une heure, toutes les possibilités pourraient être essayer. Cela prendrait seulement 43 minutes et 20 secondes pour être précis.

Si j’écris tout cela, c’est pour tenter de montrer combien un mot de passe qui suit un même motif pour tous les utilisateurs est un mauvaise chose et encore plus lorsque la génération se base sur des données personnelles comme la date de naissance. Je peux comprendre une partie des choix qui ont pu mener à une telle situation: facilité de mémorisation pour les utilisateurs, inutilité d’un système de récupération de mot de passe puisque le motif est connu de tous, place réduite en base de donnée. Quand bien même, tous ces arguments me semblent insuffisants pour justifier une telle politique de sécurité, d’autant plus lorsqu’on manipule un minimum de données personnelles.