Project

General

Profile

Actions

Centralisation et distribution des clés RSA publiques

L'intégration ldap-pam-nss permet de provisionner les comptes d'utilisateurs sur les serveurs, et de contrôler quel utilisateur peut ouvrir une session sur quelle machine.
Si les clés RSA publiques des utilisateurs étaient également centralisées sur le LDAP, on disposerait d'un système complet de contrôle d'accès aux machines.

Il existe un patch pour openssh qui répond exactement à ce besoin: OpenSSH LDAP Public Key patch (http://code.google.com/p/openssh-lpk). Le serveur openssh patché interroge un annuaire ldap pour connaître les clés autorisées.

Je ne suis pas très chaud pour sortir openssh de la gestion automatique des paquets. En attendant que cette fonctionnalité soit éventuellement intégrée au code mainstream, on va donc faire autrement. Un simple script shell exécuté en cron devrait pouvoir faire l'affaire.

Le script devra parcourir le ldap et installer ou mettre à jour les clés des utilisateurs autorisés à se connecter.

Le script ne devra pas supprimer les clés des comptes locaux destinés à se connecter en cas de défaillance du ldap.

Note: j'ai cru voir quelque part qu'on peut installer les clés des users hors de leur home. Cela permettrait d'installer la clé d'un user qui ne s'est pas encore loggé et n'a donc pas encore eu son /home créé par pam.
Edit: oui, utiliser AuthorizedKeysFile dans /etc/ssh/sshd_config (http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&sektion=5 - exemple: AuthorizedKeysFile %h/.ssh/authorized_keys /var/ldap_authorized_keys/%u)
Edit2: FIXME WHEEZY - dans squeeze, AuthorizedKeysFile ne permet de spécifier qu'un seul emplacement => on utilise à la place le paramètre AuthorizedKeysFile2, qui disparaît avec l'arrivée de Wheezy => penser à faire la modification lors du passage de Squeeze à Wheezy.

Stockage des clés dans l'annuaire

Le schéma d'openssh-lpk fournit une classe et un attribut servant à stocker des clés RSA.

openssh-lpk.schema

#
# LDAP Public Key Patch schema for use with openssh-ldappubkey
# Author: Eric AUGE <eau@phear.org>
# 
# Based on the proposal of : Mark Ruijter
#

# octetString SYNTAX
attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' 
    DESC 'MANDATORY: OpenSSH Public key' 
    EQUALITY octetStringMatch
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )

# printableString SYNTAX yes|no
objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
    DESC 'MANDATORY: OpenSSH LPK objectclass'
    MAY ( sshPublicKey $ uid ) 
    )

openssh-lpk.ldif

dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: {0}( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DES
 C 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.
 1.1466.115.121.1.40 )
olcObjectClasses: {0}( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DESC
  'MANDATORY: OpenSSH LPK objectclass' SUP top AUXILIARY MAY ( sshPublicKey $ 
 uid ) )

Stockage local des fichiers authorized_keys

Les fichiers sont stockés dans /var/ldap_authorized_keys/, sous le nom du user concerné, avec owner root, en 644.

Configurer AuthorizedKeysFile [ou AuthorizedKeysFile2 sous Squeeze] dans /etc/ssh/sshd_config pour que cet emplacement supplémentaire soit pris en compte:
Version Wheezy

#AuthorizedKeysFile     %h/.ssh/authorized_keys
AuthorizedKeysFile      %h/.ssh/authorized_keys /var/ldap_authorized_keys/%u

Version Squeeze
#AuthorizedKeysFile     %h/.ssh/authorized_keys
AuthorizedKeysFile      %h/.ssh/authorized_keys
AuthorizedKeysFile2     /var/ldap_authorized_keys/%u

service ssh restart

Script

Note: penser à modifier le paramètre thishost.

#!/bin/bash
# ------------------------------------------------------------------------------
# ldap_authorized_files
# ------------------------------------------------------------------------------
# synchronisation des clés publiques avec le LDAP
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Paramètres
# ------------------------------------------------------------------------------
# emplacement du fichier pid
pidfile=/var/run/ldap_authorized_files.pid

# uri de l'annuaire ldap
ldapuri=ldap://127.0.0.1
# Note: c'est le "127.0.0.1" qu'on retrouve dans les ACL. Si on remplace par
# "localhost", ça ne fonctionne plus en IPv6, ou alors il faudrait
# ajouter ::1 dans les ACL

# base de recherche des données dns sur l'annuaire
ldapusersbase="ou=users,ou=unix,o=ILLYSE,l=Villeurbanne,st=RHA,c=FR" 
ldapgroupsbase="ou=groups,ou=unix,o=ILLYSE,l=Villeurbanne,st=RHA,c=FR" 

# nom d'hôte
thishost=rigel

# chemin des fichiers allowed_keys
allowed_keys_path=/var/ldap_authorized_keys

# ------------------------------------------------------------------------------
# Logs
# ------------------------------------------------------------------------------
erreur=0

date_log () {
  echo -n "`date +\"%d/%m/%y-%X\"`: " 
  echo $1
}

sortie_erreur () {
  date_log "exécution terminée avec le code d'erreur $erreur" 
  exit $erreur
}

# ------------------------------------------------------------------------------
# Vérification du pid
# ------------------------------------------------------------------------------
if [ -f $pidfile ]; then
  pid=`cat $pidfile`
  if [ $? != 0 ]; then
    date_log "lecture du fichier $pidfile impossible - pas d'exécution" 
    erreur=13
    sortie_erreur
  fi

  ps -p $pid 1>/dev/null 2>/dev/null
  if [ $? == 0 ]; then
    date_log "lancement impossible - script en cours d'exécution sous le pid $pid" 
    erreur=10
    sortie_erreur
  fi
fi

echo $$ >$pidfile

# ------------------------------------------------------------------------------
# Traitement
# ------------------------------------------------------------------------------

for cn in $( ldapsearch -P 3 -H $ldapuri -x -b "ou=groups,ou=unix,o=ILLYSE,l=Villeurbanne,st=RHA,c=FR" "(&(objectClass=posixGroup)(|(cn=login$thishost)(cn=loginall)(cn=sudo$thishost)(cn=sudoall)))" memberUid | grep "memberUid:" | sed 's/memberUid: \(.*\)/\1/' )
do
  date_log "cn $cn" 

  allowed_keys_file=$allowed_keys_path/$cn

  if [ ! -f $allowed_keys_file.ok ]; then
    tempfile=$( mktemp )

    touch $tempfile
    chmod 644 $tempfile

    IFS=$'\n'
    for key in $(ldapsearch -P 3 -H $ldapuri -x -b "cn=$cn,$ldapusersbase" -LLL "(&(objectClass=posixAccount)(cn=$cn))" sshPublicKey | sed 's/^ //g' | tr -d '\n' | sed -e "s/sshPublicKey: /\n/g" | tail -n +2 )
    do
      date_log "key $key" 
      echo $key >>$tempfile
    done
    unset $IFS

    diff -q $tempfile $allowed_keys_file 1>/dev/null 2>&1
    if [ "$?" != "0" ]; then
      mv $allowed_keys_file $allowed_keys_file.old 1>/dev/null 2>&1
      mv $tempfile $allowed_keys_file
    else
      rm $tempfile
    fi
    touch $allowed_keys_file.ok
  fi
done

rm $allowed_keys_path/*.ok

exit 0

Cron

# m h  dom mon dow   command

# installation des clés publiques depuis le ldap
# (http://www.illyse.org/projects/illyseinfra/wiki/Ldap-authorized_keys)
0 * * * * /usr/local/bin/ldap_authorized_keys 1>/dev/null 2>&1

Updated by Pierre-Arnaud Poudret over 7 years ago · 1 revisions