Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Tag: Sécurité

Niveau : Star Star Empty Empty Empty
Résumé : pam_unix sha512

Il y a quelques années, le format de stockage des mots de passe unix a changé, permettant de passer du trop simple DES à un algorithme de votre choix. Au passage le stockage s'est fait dans /etc/shadow pour limiter encore plus l'accès à la version chiffrée de ces mots de passe.

Traditionnellement on utilise un hashage MD5 des mots de passe, ce qui se reconnaît par un $1$ au début des mots de passe dans le fichier shadow.

Mais il se peut que vous n'ayez plus confiance en MD5, dans ce cas, rien ne vous empêche d'utiliser un autre algorithme. C'est très simple; il suffit de modifier /etc/pam.d/common-password ou l'équivalent de votre distribution avec la ligne suivante :

password   sufficient  pam_unix.so min=4 sha256

Et changez votre mot de passe, vous verrez alors le préfixe devenir $5$ dans le fichier shadow. Les valeurs possibles sont les suivantes :

  • md5 : $1$
  • sha265 : $5$
  • sha512 : $6$
  • blowfish : $2a$ (déclaré comme fonctionnel seulement sur certaines distributions)

Si la valeur n'est pas comprise par pam_unix c'est la valeur par défaut (MD5) qui sera utilisée.

Sinon tant que vous êtes là à regarder bêtement votre configuration pam, profitez-en pour mettre en place pam_cracklib.

Niveau : Star Star Star Star Empty
Résumé :

Suite de la formidable série :

Et après les comptes ?

Il est aussi possible d'utiliser libnss-ldap pour autre chose que pour les comptes utilisateurs. La bibliothèque NSS étant une bibliothèque générique de résolution de nom, il est possible d'utiliser le ldap pour :

  • les groupes (attention pas de login pour newrp à travers pam de prévu)
  • les noms de machine
  • et d'autres qui ne pas très intéressants pour l'instant ...

Pour cela, rien de plus facile, tout est déjà fait, il suffit de modifier la ligne correspondante dans /etc/nsswitch.conf et de commencer à peupler le LDAP.

Un groupe est définit comme suit :

objectClass: posixGroup
cn: mongroupe
description: goupe a moi
gidNumber: 1000
memberUid: peck
memberUid: amiami

Notez que la description est optionnelle et que les membres sont décrits par uid. Le userPassword est aussi disponible mais n'est pas utilisable actuellement pour les groupes du système.

Exemple de machine (plus besoin de DNS local ;-) :

objectClass: ipHost
cn: mamachine
ipNetworkNumber: 1.2.3.4

S'adapter à l'existant

Il est possible que vous ayez à gérer un serveur ldap existant avec ses comptes et son propre schéma (active directory ?). C'est pourquoi il existe des options de mapping dans la configuration de pam_ldap et nss_ldap.

Ces options sont assez simples, il s'agit de reparcourir votre schéma et pour chaque entrée nécessaire pour le système, expliquer au fichier de configuration quel est le nom de l'attribut ou de la classe que vous utilisez sur le ldap. Ceci est à faire une fois pour nss et une fois pour pam.

Les commandes sont :

  • nss_map_objectclass : changer une classe utilisée par libnss-ldap
  • nss_map_attribute : changer un attribut utilisé par libnss-ldap
  • nss_default_attribute_value : donner une valeur par défaut à un attribut (pratique pour le shell par exemple)
  • pam_login_attribute : change l'attribut servant à matcher le login pendant l'authentification
  • Il n'est pas possible de changer l'attribut utilisé par pam pour le mot de passe (normal c'est le serveur ldap qui gère).

Les classes modifiables (en restant dals le domaine utilisateur/groupe) sont

  • posixAccount : utilisateur ayant un compte unix
  • shadowAccount : utilisateur ayant un système avancé de gestion de mot de passe
  • posixGroup: groupe unix

Les attributs modifiables sont : uidNumber, gidNumber, gecos, homeDirectory, loginShell, shadowLastChange, shadowMin, shadowMax, shadowWarning, shadowInactive, shadowExpire, shadowFlag, memberUid

Plus de détails (et plus de clesses) dans la RFC 2307.

La configuration fournie avec le paquet donne des exemples pour les différents systèmes existants (aix, microsoft ...)

libnss-ldapd

Maintenant vous pourriez avoir d'autres problèmes, comme la performance ou quelques hoquets du réseau qui coupent des connexions. La solution traditionnelle pour cela est d'installer nscd qui fera un cache et vous évitera ces problèmes (et peut en créer d'autres puisque c'est un cache).

Mais il existe une autre solution : libnss-ldapd, notez bien le 'd' final. C'est une version de libnss-ldap réécrite pour parler à un démon qui est le seul à parler avec le serveur ldap. Ça a l'avantage de mutualiser les requêtes dans une seule connexion et donc d'éviter pas mal de problèmes, surtout en cas de forte charge.

Petit inconvénient pour les puristes, ça ouvre une faille potentielle (inconnue à ce jour) pour un utilisateur qui voudrait devenir administrateur via le serveur ldap, puisque ce n'est plus le noyau qui vérifie les droits root d'administration mais le démon.

Le démon se configure dans /etc/nss-ldapd.conf. La syntaxe est la même que pour pam_ldap et libnss-ldap, le contenu ne doit pas changer.

Le démon est du coup assez important, il faut vérifier qu'il est lancé (monitorer toussa ...). Il s'appelle nslcd.

Sécurité et performances

Si vous avez beaucoup d'utilisateurs vous pouvez vouloir restreindre les recherches.
Si vous avez des utilisateurs qui ne doivent pas tous avoir accès au système, vous pouvez vouloir restreindre les recherches.

C'est pourquoi nss et pam fournissent des directives pour réduire la taille des recherche et donc la liste des utilisateurs :

  • base : réduit l'accès à toutes les commandes LDAP à une seule branche
  • scope : indiquer si la recherche doit tester les sous branches ou non
  • nss_base_<map> : permet d'indiquer à ldap de se limiter à une requête pour un type donné de résolution, map peut valoir passwd, shadow ou group (ou hosts, services, networks, protocols, rpc, ethers, netmasks, bootparams, aliases ou netgroup). Exemple : nss_base_passwd ou=users,dc=linux-attitude,dc=fr?sub?uid>1000
  • pam_filter : permet de modifier la partir filtre pour pam (pas besoin de modifier le reste car il n'y a qu'une requête faite par pam). Exemple : pam_filter uid>1000

Niveau : Star Star Star Star Empty
Résumé : libmap-ldap ; passwd ; chsh ; chfn ; adduser

Désolé, j'ai oublié la suite de la série LDAP. On peut faire un peu mieux que la dernière fois.

Changer son mot de passe

Nous n'avons pas activé le changement de mot de passe. Pour cela, c'est assez simple, il suffit de modifier common-password (cela peut être ailleurs pour certaines distributions ...) :

password   sufficient   pam_unix.so nullok obscure min=4 max=8 md5
password   sufficient   pam_ldap.so

Il faut aussi modifier /etc/pam_ldap.conf pour que pam ne modifie pas directement le mot de passe, mais utilise les fonctions de ldap prévues à cet effet. Cela permet qu'il ne soit pas stocké en clair, mais avec la méthode définie dans la configuration de ldap ({SSHA} par défaut).

# A ajouter dans /etc/pam_ldap.conf
pam_password exop

Et voilà vous pouvez tester la commande passwd. Mais vous constaterez qu'il y a quelque chose qui cloche. Si vous lancez la commande en root, l'ancien mot de passe vous est quand même demandé. C'est parce que vous n'avez pas les droits d'administrateur sur le ldap.

Pour remédier à cela, il vous faut un compte ldap qui a le droit de modifier le champ password des autres utilisateurs, par exemple l'administrateur ldap. Puis renseignez ce compte dans pam_ldap.conf :

# /etc/pam_ldap.conf
# Il faudra stocker le mot de passe correspondant en clair dans /etc/pam_ldap.secret
# Ce fichier doit appartenir à root et être en mode 600
rootbinddn cn=admin,dc=linux-attitude,dc=fr

Ça y est vous êtes prêts.

chsh, chfn, adduser

Les utilisateurs ont le droit de changer leur shell ainsi que leurs informations personnelles. Pour cela ils ont les commandes chsh et chfn.

Malheureusement ces commandes manipulent /etc/passwd. Le paquet libpam-ldap fournit des script pour remplacer ces commandes, mais ils sont un peu basiques et nécessitent libnet-ldap-perl (Net::LDAP). Vous pouvez vous baser dessus pour faire les votres si vous pensez que ces fonctionnalités sont indispensables pour vos utilisateurs. On les trouve dans /usr/share/doc/libpam-ldap/examples/ . un certain nombre de paramètres sont codés en dur et peu de vérifications d'erreur sont faites.

Si vous modifiez vos binaires dans /usr/bin sur debian, faites attention la prochaine mise à jour risque de les remplacer. Pour éviter ce problème, il existe la commande dpkg-divert :

# On fait croire aux paquets qu'ils installent /usr/bin/chfn alors qu'ils installent en réalité /usr/bin/chfn.old
$ dpkg-divert --add --rename --divert /usr/bin/chfn.old /usr/bin/chfn
# voila, vous pouvez maintenant remplacer /usr/bin/chfn

Autre problème, adduser ne comprend pas non plus le ldap. Ici le problème est le même mais la solution un peu meilleure. Un paquet ldapscripts est disponible et il contient des commandes comme ldapadduser pour vous faciliter la création d'utilisateurs ldap. Les commandes sont configurées dans /etc/ldapscripts/ .

Tout ceci pourrait s'améliorer dans de prochaines versions, un patch a l'air en cours de développement pour le paquet shadow (qui contient ces outils) qui utiliserait la libnss pour effectuer ces actions. Les commandes fonctionneraient donc directement.

Quelques remarques

Depuis que nous avons des vrais comptes systèmes basés sur le ldap nous sommes contents. Mais quelques petites choses tout de même :

  • Gardez le compte root sur le système, on ne sait jamais, si le ldap tombe, vous seriez bien embêtés
  • Faites attentions aux droits dans le ldap, ils impactent les fonctionnalités des commandes systèmes (droit de lister les autres utilisateurs par exemple)
  • Avec un serveur distant, le ldap devient une forme de NIS
  • Vous pouvez utilisez la réplication ldap pour résister aux crash
  • Vous pouvez toujours garder une mot de passe local au cas où le ldap ne marche pas (pam fait les 2)
  • Vous pouvez vous connecter à un controleur de domaine windows
  • Si vous avez de problèmes les logs d'authentification sont dans /var/log/auth.log
  • Vous pouvez vouloir ne mettre en place que l'authentification via pam et ne pas utiliser libnss. Ce cas correspond à des utilisateurs virtuels (comme pour le ftp) qui n'utiliseront pas alors de vrai compte sur le système.

Niveau : Star Star Star Empty Empty
Résumé : libpam-ldap

Hier nous avons créé un compte sur le ldap. Bien, mais on ne pouvait pas en faire grand chose. Il faut maintenant pouvoir se connecter. Il est théoriquement possible de tout faire avec libnss mais c'est à la fois plus difficile et moins fonctionnel qu'avec pam.

Comme vous le savez l'authentification sous linux se fait avec PAM. Nous allons donc utiliser un module dédié : libpam-ldap (cool le paquet porte le même nom).

LDAP

Cette fois ne nous force à utiliser aucun objectClass, mais comme il sait utiliser les attributs de la classe shadowAccount, on peut utiliser celle-ci pour se simplifier la vie.

Ajoutons le mot de passe à notre utilisateur. On crée le mot de passe LDAP à la main, mais par la suite vous verrez que ce n'est pas indispensable.

$ slappasswd -s test
{SSHA}l5X6sBFomfk3tk02HEMWK4YLep7pqZDk

On ajoute ce mot de passe à l'utilisateur déjà créé :

$ ldapmodify -x -D "cn=admin,dc=linux-attitude,dc=fr" -W
dn: uid=peck,ou=people,dc=linux-attitude,dc=fr
changetype: modify
add: userPassword
userPassword: {SSHA}l5X6sBFomfk3tk02HEMWK4YLep7pqZDk

Un test de connexion avec ldapsearch vous montrera que le mot de passe est bien pris en compte :

$ ldapsearch -x -D "uid=peck,ou=people,dc=linux-attitude,dc=fr" -w test "uid=peck"

PAM

Maintenant il faut expliquer gentiment à PAM d'aller faire son authentification en utilisant LDAP. Pour cela hop, configuration dans /etc/pam.d/common-*. Certaines distributions devront peut-être configurer chaque élément un par un : su, login, ssh ...

# common-auth
# attention, le pam_unix devient sufficient
auth    sufficient    pam_unix.so nullok_secure nodelay
# use_first pass permet de ne demander qu'une fois les mot de passe pour les 2 modules
auth    sufficient    pam_ldap.so use_first_pass 
# common-account
account    required    pam_unix.so
account    sufficient   pam_ldap.so
# common-session
session    required    pam_unix.so
session    optional    pam_ldap.so

Maintenant attaquons-nous au fichier de configuration proprement dit : /etc/pam_ldap.conf. En voici l'essentiel :

# /etc/pam_ldap.conf
base ou=people,dc=linux-attitude,dc=fr
uri ldap://127.0.0.1/
ldap_version 3

Vous remarquerez que pam_ldap.conf et libnss-ldap.conf ne rentrent jamais en conflit. Vous pouvez faire un lien de l'un vers l'autre ou modifier la configuration pour que ce soient les mêmes fichiers. Cela peut vous éviter quelques désagréments.

Test

Maintenant tout marche, même l'authentification. Faites le test :

$ su - peck
Password:
$ id
peck

Youpi, ça marche même en ssh. Bon c'est bien gentil, mais c'est encore un peu léger. La prochaine fois , on voudrait pouvoir faire encore plus.

Niveau : Star Star Star Star Empty
Résumé : openssl

Vérification

Maintenant que nous avons des certificats propres et bien signés il va falloir les vérifier. Mais il ne s'agit pas simplement de vérifier que le certificat est valide et signé par une autorité de confiance. Tout d'abord, il fautde vérifier toute sa chaine de confiance. Ensuite il faut vérifier ses extensions, par exemple s'il va être utilisé pour les usages auxquels on l'a prévu.

Openssl permet de faire la vérification de la chaine de certification ainsi que la vérification de l'usage. Pour cela il faut installer l'autorité de confiance :

# On trouve le hash du certificat de l'AC
$ hash=$(openssl x509 -hash -noout -in cacert.pem)
# On installe l'AC en utilisant ce hash (il faut être root ici)
$ cp cacert.pem /etc/ssl/certs/$hash.0

Du coup la vérification d'un certificat se fait simplement :

$ openssl verify -purpose sslclient certificat.crt

Pour la vérification des extensions, il faut voir au cas par cas.

Mais contrairement à ce qu'on pense souvent, ce n'est pas tout. Il faut aussi vérifier que le certificat n'est pas révoqué, c'est-à-dire invalidé avant la date limite.

Révocation

La révocation d'un certificat se fait dans plusieurs cas :

  • perte de la clé
  • départ du propriétaire de la clé
  • renouvellement de la clé
  • ...

En gros à chaque fois qu'on veut garantir que la clé ne sera plus utilisée.

Pour révoquer un certificat alors qu'on a installé une AC openssl comme dans l'article précédent :

# On en profite pour indiquer pourquoi on le révoque
# les raisons possibles sont : unspecified, keyCompromise, CACompromise,
#   affiliationChanged, superseded, cessationOfOperation, certificateHold or removeFromCRL
$ openssl ca -config openssl.cnf -revoke client.cert -crl_reason keyCompromise

Il existe 2 mécanismes pour faire savoir au monde qu'on a révoqué un certificat : les CRL et le protocole OCSP.

Certificate Revocation List

Une liste de révocation est une liste complète de certificats révoqués qu'on télécharge. On compare ensuite le certificat à vérifier à cette liste pour savoir si le certificat n'a pas été révoqué.

Cette liste est signée par l'autorité de certification pour garantir qu'elle est valide. Au passage elle est aussi datée. Avec openssl elle se gère de la même façon que l'autorité de certification, avec le même fichier de configuration. Reprenons le fichier de configuration de l'AC et ajoutons-y les spécificités des CRL.

[ Peck_authority ]
# Numéro de série pour les CRL
crlnumber       = $dir/crlnumber

# Durée au dela de laquelle la CRL doit être mise à jour
default_crl_days= 30

# Ajouter des extensions à la CRL
crl_extensions        = crl_ext

[ crl_ext ]

# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always

On stocke les identifiants de crl :

$ echo "00" > ./peckCA/crlnumber

Après avoir révoqué un certificat (voir plus haut) générons la CRL :

$ openssl ca -config openssl.cnf -gencrl -out crl.list

Lisons le contenu de cette liste pour vérifier :

$ openssl crl -in crl.list -text

On peut ensuite vérifier que le certificat est révoqué ou non avec openssl en disposant de la CRL :

# Option non documentée
$ openssl verify -CAfile crl.list -crl_check client.cert

Pour le faire automatiquement, il faut installer la CRL de la même façon qu'on a installé le certificat de l'AC :

# Notez l'usage de r0 à la place de 0
$ hash=$(openssl x509 -hash -noout -in crl.list)
$ cp crl.list /etc/ssl/certs/$hash.r0
# Et on vérifie la révocation en même temps que la validité
$ openssl verify -crl_check client.cert

Pour ne pas devoir mettre à jour les CRL à la main chez tous les clients, il faut la publier. Pour cela, il faut d'abord la convertir, en effet, les clients traditionnels comme firefox lisent la version DER (binaire) et non PEM (ascii).

$ openssl crl -inform PEM -in crl.list -outform DER -out crl.der
# puis placez-la sur votre serveur

Et pour que le client sache où aller chercher cette CRL, il faut l'indiquer dans le certificat lorsqu'on le signe :

# Dans la openssl.cnf, utilisé lors de la signature de demandes
[ usr_cert ]

# Les url en http et en ldap sont autorisées
crlDistributionPoints           = URI:http://localhost/ca-crl.pem

Malheureusement, openssl ne sait pas aller chercher tout seul la crl pour faire cette vérification.

Enfin notez qu'il existe aussi des extensions deltaCRLindicator et FreshestCRL qui permettent de ne pas télécharger toute la CRL à chaque fois. Mais elles ne sont pas supportées par openssl.

Online Certificate Status Protocol

Le problème des CRL est qu'il nécessite un téléchargement plus ou moins lourd pour faire une seule vérification. C'est tout aussi pénalisant pour l'AC que pour le client. C'est pourquoi un protocole a été inventé pour permettre de faire une vérification au cas par cas.

Il permet de vérifier si un unique certificat est révoqué. Il a aussi l'avantage de ne pas dépendre de la date de validité des CRL qui sont générées et rechargées sur des périodes plus ou moins longues. Ici on a directement la bonne information.

Lançon un serveur OCSP sur notre AC fraîchement créée. Pour faire rapide nous utilisons le certificat de l'AC pour lancer le service, mais pour faire propre, nous aurions du lui créer un certificat spécifique signé par l'AC et ayant l'extension "extendedKeyUsage=OCSPSigning" :

# Il n'y a pas de port par défaut pour OCSP
$ openssl ocsp -index /srv/Authority/index.txt -CA /srv/Authority/cacert.pem -rsigner /srv/Authority/cacert.pem -rkey /srv/Authority/private/cakey.pem -port 1234

Vérifions ensuite si un certificat est révoqué. Notez qu'il est nécessaire de préciser le certificat ayant signé celui qu'on veut vérifier :

# Attention, précisez -issuer avant -cert
# Il est possible de vérifier plusieurs certificats d'un coup avec plusieurs -cert
$ openssl ocsp -host localhost:1234 -issuer /srv/Authority/cacert.pem -cert certificat.crt

Tout comme pour les CRL, il est possible d'indiquer dans le certificat l'endroit où se trouve le serveur OCSP permettant de vérifier la révocation. Les application qui supportent (pas openssl verifiy) l'extension vont alors automatiquement la vérifier :

# Dans la openssl.cnf, utilisé lors de la signature de demandes
[ usr_cert ]

authorityInfoAccess = OCSP;URI:http://ocsp.my.host/

Niveau : Star Star Star Star Empty
Résumé :

Après avoir vu il y a longtemps comment créer le certificat d'une autorité de certification personnelle et comment l'installer là où c'est nécessaire, nous allons voir les méthodes pour gérer une autorité de certification.

Une fois l'autorité créée, l'activité principale d'une autorité est de signer des certificats. Et plutôt que de taper tous les paramètres à la main, il est plus simple de disposer d'une configuration, ce qu'openssl nous propose. De plus, openssl dispose d'une commande permettant une gestion simplifiée d'une autorité.

Passons en revue la configuration (reprise du fichier par défaut /etc/ssl/openssl.cnf). Parlons directement de la section gérant l'autorité elle-même :

# Section pour la commande ca
[ ca ]
# Autorité par défaut (pour en gérer plusieurs)
default_ca      = Peck_authority 

# Section spécifique à mon autorité
[ Peck_authority ]

# Tous les répertoires de base
dir             = /srv/Authority        # Where everything is kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
private_key     = $dir/private/cakey.pem# The private key
RANDFILE        = $dir/private/.rand    # private random number file

Ensuite les options spécifiques :

# Mettre à no pour permettre à une entité de disposer de plusieurs certificats
#unique_subject = no

# Section contenant les extensions à utiliser
x509_extensions = usr_cert              # The extentions to add to the cert

# Format d'affichage des données en ligne de commande
name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

# Ne pas utiliser sauf à faire infiniment confiance aux demandeurs (ou tout vérifier à la main)
# copy_extensions = copy

# Durée de validité des certificats émis
default_days    = 365
# Fonction de hashage à utiliser (ne pas utiliser md5 :-)
default_md      = sha1
# Réécrire le DN passé par le demandeur
preserve        = no

# Politique à utiliser pour la vérification de la demande
policy          = policy_match

Et maintenant les sous-sections.

La politique de vérification des demandes est simpliste, elle ne sert qu'à vérifier que la demande est valide, rien de plus. C'est à l'humain / le script / la PKI de vérifier que la demande est la bonne et que le demandeur est bien celui qu'il prétend.

# Liste des champs présents dans la demande et inclus dans le certificats (les autres seront supprimés).
# match = doit être la même que cette de l'AC
# supplied = obligatoire
# optional = optionnel
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = supplied
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

Une sous-section pour définir les extensions des certificats générés :

[ usr_cert ]
# Le certificat ne peut pas servir d'autorité 
basicConstraints=CA:FALSE

# Usage autorisé du certificat (selon Netscape)
# client, server, email, objsign, reserved, sslCA, emailCA, objCA
nsCertType                    = client, email

# Usage autorisé du certificat (selon PKIX)
# digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly
keyUsage = digitalSignature

# Commentaire inclu dans le certificat
nsComment                       = "Certificate from Peck"

# Génération automatique de l'identifiant du certificat
subjectKeyIdentifier=hash
# Génération de l'identifiant de la clé de signature
authorityKeyIdentifier=keyid,issuer

# ... 

Il existe une foultitude d'autres extensions, principalement les extensions définies par Netscape et celles définies par PKIX (groupe de travail sur le x509) dans le RFC 5280 (§4.2). PKIX dispose d'une liste d'extensions (id-pe) mais leur documentation doit être recherchée dans les divers documents du groupe de travail.

Ces extensions sont à utiliser lorsque vous avez des besoins spécifiques à une application. Notez qu'une extension peut toujours être préfixée de "critical" qui indique à l'utilisateur du certificat qu'il doit le refuser s'il ne comprend pas le sens de l'extension en question (sinon elle est ignorée). Faites attention, l'usage de critical peut se retourner contre vous.

Une fois la configuration prête, créez votre autorité :

$ mkdir /srv/Authority
$ cd /srv/Authority
$ mkdir newcerts private
$ echo "00" > serial
$ touch index.txt

Placez-y votre certificat d'autorité et sa clé :

$ cp ca.crt /srv/Authority/cacert.pem
$ cp ca.key /srv/Authority/private/cakey.pem

Et voila, votre autorité est prête à être utilisée. Pour exemple, comme d'hab le client crée une demande de certificat :

# Il se débrouille pour générer une requête au bon format avec l'outil qu'il veut
$ openssl req -new

Et vous le signez après en avoir vérifié le contenu et l'auteur :

# Lecture de la demande
$ openssl req -in client.csr -text
#Signature de la demande
$ openssl ca -config openssl.cnf -in client.csr

Et le certificat signé à renvoyer au client est dans le répertoire newcerts sous le nom <serial>.pem

Vous verrez que des sauvegardes de fichier sont conservées pour permettre d'annuler la dernière opération.

Niveau : Star Star Star Star Empty
Résumé : SSH et certificats x509

Vous avez une ferme de serveurs ? Vous avez une ferme d'utilisateurs ? Vous avez déjà une infrastructure de gestion de clés pour des certificats x509 (ssl) ?

Si vous répondez oui à deux de ces points alors cet article va vous intéresser.

Sachez qu'il est possible d'utiliser des certificats x509 avec ssh. Quel intérêt ? Tout simplement vous pouvez réutiliser votre PKI si vous en avez déjà une en x509. Ensuite les certificats fonctionnent par un système de signature hiérarchisée, donc il n'est plus nécessaire de mettre en place un système de distribution des clés des utilisateurs sur les machines. Et inversement, les utilisateurs n'auront plus à se reposer sur leur pifomètre pour accepter les clés des serveurs à leur première connexion.

Maintenant qu'on sait qu'on va pouvoir vendre de la sécurité à notre boss tout en évitant de se farcir des systèmes de synchronisation, on va pouvoir se lancer dans les choses sérieuses.

Compilation

Tout d'abord il nous faut une version de ssh qui supporte ce mode de fonctionnement. On la trouvez chez un certain Roumen Petrov. Bon, petite déception, il ne fournit que des patchs. A nous d'en faire un vrai ssh compilé. Pour une fois, ça va être une peu plus difficile pour les debianistes. Pour ceux qui recompilent leur ssh directement :

# à adapter à vos besoins et à votre version
$ wget http://www.roumenpetrov.info/openssh/x509-6.1.1/openssh-5.1p1+x509-6.1.1.diff.gz
$ cd openssh-source
$ zcat ../openssh-5.1p1+x509-6.1.1.diff.gz | patch -p1
$ ./configure
$ make
$ make install

Pour ceux qui sont sur debian, il serait quand même plus sympa de se refaire un paquet. Le problème est que le patch de Roumen Petrov s'applique mal sur des sources debian. Il y a donc 3 rejets à appliquer avant de pouvoir lancer la compilation :

# récupération des sources debian
$ apt-get source openssh
# récupération du patch
$ wget http://www.roumenpetrov.info/openssh/x509-6.1.1/openssh-5.1p1+x509-6.1.1.diff.gz
$ cd openssh-5.1p1
$ zcat ../openssh-5.1p1+x509-6.1.1.diff.gz | patch -p1

# ICI relisez les fichiers .rej et appliquez à la main, c'est assez simple

# recompilation
$ dpkg-buildpackage -rfakeroot -b
# installation
$ cd ..
$ su
$ dpkg -i openssh-client*.deb openssh-server*.deb

Pour ceux qui sont toujours en etch vous pouvez soit upgrader ssh en récupérant le paquet source de lenny soit utiliser la version etch avec une version plus ancienne du patch.

Pour la méthode upgrade avec etch, modifiez le fichier debian/control qui se trouve dans le répertoire source ssh et supprimez les versions de libssl et lsb-base indiquées dans les dépendances avant de compiler. Cela marche très bien.

Pour ceux qui voudraient rester en ssh4.1, appliquez le patch de Roumen Petrov et forcez l'application du patch avec les erreurs. Ensuite listez les fichier .rej (ils sont tous à la racine) et appliquez les à la main (retrouver la bonne ligne, ajouter ...). C'est un peu plus long.

Utilisation

Ça y est le nouveau ssh est installé sur le serveur et sur le client. Maintenant utilisons un certificat pour se connecter au serveur et prouver que ça marche.

Tout d'abord il nous faut une autorité de certification, c'est à dire un certificat qui a le droit de signer d'autres certificats et que nous installerons partout (l'article explique comment la créer et l'utiliser). Avec cette autorité, il faut générer deux paires de clés : une paire pour le serveur, une paire pour le client. Rien n'oblige à avoir la même autorité pour les deux, mais cela simplifie les exemples, c'est donc ce que je vais faire.

On a donc :

  • ca.crt : le certificat de l'autorité
  • server.crt et server.key : le certificat et la clé à utiliser sur le serveur ssh
  • client.crt et client.key : le certificat et la clé à utiliser sur le client ssh

Authentification du serveur

Commençons par configurer l'authentification du serveur par le client. Le client sera sûr que le serveur est reconnue par une autorité.

Côté serveur

Installons le certificat sur le serveur ssh. On place la clé dans un nouveau fichier pour le serveur :

$ cat server.key server.crt > /etc/ssh/ssh_host_x509_key
$ chmod 600 /etc/ssh/ssh_host_x509_key
# mise à jour de la clé publique pour la forme
$ ssh-keygen -y -f /etc/ssh/ssh_host_x509_key > /etc/ssh/ssh_host_x509_key.pub

Et on ajoute cette clé à celle que le serveur envoie au client pour authentification en ajoutant une ligne/etc/ssh/sshd_config :

HostKey /etc/ssh/ssh_host_x509_key

Vous pouvez même commenter les anciennes lignes HostKey si vous voulez forcer l'utilisation de x509, mais je ne pense pas que ce soit judicieux (par contre ça permet d'être sur de ce qu'on teste). Et hop on relance le serveur.

# n'ayez pas peur cela ne coupe pas les connexions existantes
$ /etc/init.d/ssh restart

Côté client

Maintenant il faut que le client accepte cette clé. On va lui dire qu'il peut accepter tout ce qui a été signé par notre autorité. Pour cela, il suffit de copier le certificat de l'autorité dans /etc/ssh/ca/ca-bundle.crt (vous pouvez mettre toutes les autorités que vous voulez dans ce fichier) :

$ cp ca.crt /etc/ssh/ca/ca-bundle.crt

Attention : ssh vérifie que le certificat est bien signé par une autorité valide mais cela ne veut pas dire qu'il accepte directement la clé. Il passe toujours par une vérification utilisateur comme expliqué dans un autre article. Attention : ssh refuse les clés mal signées mais autorise les clés sans certificat. A l'utilisateur d'accepter ou refuser la clé. Attention : ssh vérifie la chaine de certification, mais ne vérifie pas que le cn correspond au nom du serveur.

A noter qu'il est dommage que la vérification de la chaine de certification se fasse après l'acceptation par le client.

Authentification du client

Cette fois c'est le serveur qui va vérifier que le client dispose d'un certificat vérifié par une autorité.

Côté client

Pour utiliser notre certificat comme identité, on le copie sur le client :

$ cat client.key client.crt > ~/.ssh/id_x509
$ chmod 600 ~/.ssh/id_x509
# on met a jour la clé publique
$ ssh-keygen -y -f ~/.ssh/id_x509 > ~/.ssh/id_x509.pub

Et on configure le client pour qu'il l'utilise (au choix dans /etc/ssh/ssh_config ou dans ~/.ssh/config) :

Host *
        IdentityFile ~/.ssh/id_x509

Côté serveur

Maintenant il faut que le serveur accepte cette clé. On va lui dire qu'il peut accepter tout ce qui a été signé par notre autorité. Pour cela, il suffit de copier le certificat de l'autorité dans /etc/ssh/ca/ca-bundle.crt (vous pouvez mettre toutes les autorités que vous voulez dans ce fichier) :

$ cp ca.crt /etc/ssh/ca/ca-bundle.crt

Et on adapte l'authorized_keys sur le serveur. Pour cela deux solutions, la première est de copier tel quel le fichier id_x509.pub généré précédemment. Ça marche, mais ce n'est pas très intéressant. Puisqu'on a des certificats qui sont vérifiés autant en profiter et ne donner que le DN ce qui nous permettra de le changer.

# on réutilise le certificat pour sortir le dn au bon format
$ openssl x509 -noout -subject -in client.crt -nameopt RFC2253 >> ~/.ssh/authorized_keys

Attention : ssh refuse les clés mal signées mais autorise les clés sans certificat. A l'utilisateur de remplir son authorized_keys correctement

Conclusion

Cet article commence à être long, je vais donc m'arrêter ici. Vous voyez qu'il est possible de mettre en place des certificats pour ssh sans trop se forcer. Le patch de Roumen Petrov est bien fait puisqu'il ajoute beaucoup d'options (je les ai laissé ici à leur valeur par défaut pour simplifier l'article) et surtout, elles sont toutes documentées dans les pages de manuel. De plus le support de x509 est étendu à tous les outils ssh, c'est à dire le client, le serveur, mais aussi ssh-keygen et ssh-agent. En dehors du man, la meilleure documentation c'est le README.

Tout cela est bien pratique mais seul un usage basique a été couvert ici. Un des gros avantages de l'utilisation des certificats est de pouvoir gérer des listes de révocation pour interdire automatiquement des certificat qui ne sont plus valides (l'utilisateur est parti, la clé est trop vieille ...). Nous en parlerons surement dans un prochain article.