Cette page est en lecture seule. Vous pouvez afficher le texte source, mais ne pourrez pas le modifier. Contactez votre administrateur si vous pensez qu'il s'agit d'une erreur. ====== Authentification Kerberos (SASL GSSAPI) ====== ===== Installation ===== On installe le paquet ''krb5-user'' pour avoir les commandes utiles : <code bash>apt-get install krb5-user</code> On crée ensuite le fichier ''/etc/krb5.conf'' : <code ini> [libdefaults] default_realm = DOMAIN.TLD dns_lookup_realm = false dns_lookup_kdc = false ticket_lifetime = 24h renew_lifetime = 7d forwardable = true [realms] DOMAIN.TLD = { kdc = AD1.DOMAIN.TLD admin_server = AD1.DOMAIN.TLD } [domain_realm] .DOMAIN.TLD = DOMAIN.TLD DOMAIN.TLD = DOMAIN.TLD </code> <note important>**Le domaine doit être en majuscule partout** pour que cela fonctionne.</note> On peut alors tenter une première authentification : <code bash>kinit myuser@DOMAIN.TLD</code> On vérifie ensuite le ticket obtenu : <code bash>klist</code> Pour la suite, on supprime le ticket obtenu : <code bash>kdestroy</code> ===== Les fichiers keytab & ccache ===== ==== Fichier Keytab (Key Table) ==== Un fichier Keytab est un fichier binaire contenant une ou plusieurs clés secrètes dérivées du mot de passe d'un utilisateur ou d'un service (//Principal// dans le vocable Kerberos). Il sert de //"coffre-fort de clés"// permettant une authentification automatisée sans interaction humaine et sans stockage du mot de passe en clair. * **Rôle :** Il remplace la saisie manuelle du mot de passe lors de la commande ''kinit''. * **Persistance : Permanent** => Tant que le mot de passe du compte n'est pas modifié sur le contrôleur de domaine (KDC), le fichier reste valide. * **Sécurité : Critique** => Toute personne possédant un accès en lecture à ce fichier peut usurper l'identité du Principal associé. Il doit être protégé par des permissions restreintes (ex: ''chmod 600''). ==== Fichier ccache (Credential Cache) ==== Un fichier //ccache// (Credential Cache) est un espace de stockage temporaire utilisé par Kerberos pour conserver les tickets de session //(TGT - Ticket Granting Ticket)// et les tickets de service obtenus après une authentification réussie. Il est généralement référencé par la variable d'environnement ''$KRB5CCNAME'' et vaut par défaut ''/tmp/krb5cc_[uidnumber de l'utilisateur]''. * **Rôle :** Il sert de "badge d'accès" temporaire. Une fois le ticket présent dans le cache, les applications l'utilisent directement pour prouver leur identité sans solliciter à nouveau le mot de passe ou le fichier ''keytab''. * **Persistance : Temporaire** => Sa durée de vie est limitée par celle du ticket Kerberos (généralement 10 heures). * **Sécurité :** Moindre que le //keytab//, mais **sensible**. Il permet d'accéder aux ressources tant que les tickets ne sont pas expirés. Il doit également être protégé par des permissions restreintes (ex: ''chmod 600''). ===== Création d'un fichier keytab & ccache ===== On crée le fichier ''keytab'' (par exemple ''/etc/myuser.keytab'') en lançant la commande ''ktutil'' (mode interactif) et on entre les commandes suivantes : <code> ktutil: addent -password -p myuser@DOMAIN.TLD -k 1 -e RC4-HMAC Password for myuser@DOMAIN.TLD: ktutil: wkt /etc/myuser.keytab ktutil: q </code> On récupère ensuite un ticket en utilisant le fichier //keytab// et on le stocke dans un fichier //ccache// (par exemple ''/etc/myuser.ccache''): <code bash>kinit -c /etc/myuser.ccache -k -t /etc/myuser.keytab myuser@DOMAIN.TLD</code> On vérifie ticket obtenu : <code bash>klist -c /etc/myuser.ccache</code> <note tip>Pour un génération automatisé du fichier ''keytab'' : <code base> PRINCIPAL=myuser@DOMAIN.TLD KEYTAB=/etc/myuser.keytab PASSWORD=secret printf "addent -password -p %s -k 1 -e RC4-HMAC\n%s\nwkt %s\nq\n" "$PRINCIPAL" "$PASSWORD" "$KEYTAB" | ktutil </code></note> ===== Renouvellement automatique ===== En cas de besoin de renouvellement automatique (=token utilisé par un cron/service par exemple), un cron doit être mis en place : - Créer le fichier ''/usr/local/bin/renew-keytab'': <code bash> #!/bin/bash DEBUG=0 CREATE=0 OWNER= KEYTAB= DROP_KEYTAB=1 KRB5CCNAME= PRINCIPAL= usage() { [[ "$#" -gt 0 ]] && echo "$*" >&2 echo "Usage: $(realname "$0") krb5ccname principal [-k KEYTAB] [-o owner] [-c] [-d]" echo " [krb5ccname] Path of the KRB5 credential cache file (REQUIRED)" echo " [principal] Principal of the user (REQUIRED)" echo " -k keytab Path of the keytab file" echo " -o/--owner Owner of the manipulated files (defaut: the owner of the existing KRB5 credential cache or keytab file)" echo " -d/--debug Enable debug log" echo " -c/--create Enable create mode (do not expect that keytab & krb5ccname files already exists)" [[ "$#" -gt 0 ]] && exit 1 exit 0 } idx=1 while [[ $idx -le $# ]]; do opt=${!idx} case "$opt" in -d|--debug) DEBUG=1 ;; -c|--create) [[ -t 1 ]] || usage "Create mode is only possible in interactive mode" CREATE=1 ;; -k|--keytab) ((idx++)) KEYTAB="${!idx}" DROP_KEYTAB=0 ;; -o|--owner) ((idx++)) OWNER="${!idx}" ;; *) if [[ -z "$KRB5CCNAME" ]]; then KRB5CCNAME=$opt elif [[ -z "$PRINCIPAL" ]]; then PRINCIPAL=$opt else usage "Invalid argument '$opt'" fi esac ((idx++)) done [[ -z "$KRB5CCNAME" ]] && usage "Keytab file not specified" [[ -z "$PRINCIPAL" ]] && usage "Principal not specified" debug() { [[ $DEBUG -eq 1 ]] && echo -e "[DEBUG] $*"; } info() { [[ -t 1 ]] && echo -e "[INFO] $*" && return; debug "$@"; } error() { echo -e "$*" >&2; } if [[ -z "$OWNER" ]]; then if [[ -n "$KEYTAB" ]] && [[ -e "$KEYTAB" ]]; then OWNER="$(stat -c %U "$KEYTAB")" elif [[ -e "$KRB5CCNAME" ]]; then OWNER="$(stat -c %U "$KRB5CCNAME")" else OWNER=$( id -un ) fi debug "Auto-detected owner: $OWNER" fi if [[ "$( whoami )" != "$OWNER" ]]; then info "This script must be run as $OWNER: rerun it as $OWNER" debug "Run: sudo -u $OWNER $(realpath "$0") $*" sudo -u "$OWNER" "$(realpath "$0")" "$@" exit $? fi [[ -z "$KEYTAB" ]] && \ KEYTAB=$(mktemp -u "/tmp/$(basename "$0")_keytab_of_${OWNER}_for_${PRINCIPAL}.XXX") function create_keytab() { local password NEW_KEYTAB=$(mktemp -u "/tmp/$(basename "$0")_temp_keytab_of_${OWNER}_for_${PRINCIPAL}.XXX") debug "Temporary file: $NEW_KEYTAB" while [[ "${password:-null}" == "null" ]]; do read -srp "Please enter password of $PRINCIPAL: " password echo [[ "${password:-null}" == "null" ]] && error "Password required!" done debug "Password: '$password'" if ! printf "addent -password -p %s -k 1 -e RC4-HMAC\n%s\nwkt %s\nq\n" "$PRINCIPAL" "$password" "$NEW_KEYTAB" | ktutil; then rm -f "$NEW_KEYTAB" error "Failed to create a keytab for $PRINCIPAL" return 1 fi debug "Keytab file created, install it" if cat "$NEW_KEYTAB" > "$KEYTAB"; then if [[ "$DROP_KEYTAB" -eq 0 ]]; then info "Keytab file installed in $KEYTAB" else debug "Keytab file installed in $KEYTAB" fi rm -f "$NEW_KEYTAB" return 0 fi rm -f "$NEW_KEYTAB" error "Failed to install keytab file in $KEYTAB" return 1 } function init_kbr5cc() { local new_keytab=0 a if [[ ! -e "$KEYTAB" ]]; then info "No existing keytab, create it" create_keytab || return 1 new_keytab=1 fi if ! kinit -c "$KRB5CCNAME" -k -t "$KEYTAB" "$PRINCIPAL"; then error "Failed to initialize a KRB5 credential cache for $PRINCIPAL" while [[ "${a^}" != "Y" ]] && [[ "${a^}" != "N" ]]; do if [[ $new_keytab -eq 1 ]]; then echo "May be the entered password is incorrect?" read -rp "Retry [Y/n]? " a else echo "May be the password of $PRINCIPAL have be changed (or is expired?)." read -rp "Drop existing keytab file & retry with a new password [Y/n]? " a fi [[ -z "$a" ]] && a=Y done if [[ "${a^}" == "Y" ]]; then rm -f "$KEYTAB" init_kbr5cc && return 0 return 1 elif [[ "$DROP_KEYTAB" -eq 1 ]]; then rm -f "$KEYTAB" debug "Keytab file removed ($KEYTAB)" else debug "Keytab file keep ($KEYTAB)" fi return 1 fi info "Keytab initialized for $PRINCIPAL:\n$( klist -c "$KRB5CCNAME" 2>&1 )" return 0 } [[ "$CREATE" -eq 1 ]] && { init_kbr5cc; exit $?; } if [[ -e "$KEYTAB" ]]; then info "Keytab file available, obtain a new ticket for $PRINCIPAL using it" if kinit -c "$KRB5CCNAME" -k -t "$KEYTAB" "$PRINCIPAL"; then info "New ticket obtainned for $PRINCIPAL:\n$( klist -c "$KRB5CCNAME" 2>&1 )" exit 0 fi error "Failed to obtain new ticket for $PRINCIPAL using keytab file" \ "($KEYTAB). May be the password of $PRINCIPAL have been changed?" [[ -e "$KRB5CCNAME" ]] || exit 1 info "Renew existing ticket for $PRINCIPAL in KRB5 credential cache file ($KRB5CCNAME)" fi if [[ -e "$KRB5CCNAME" ]]; then [[ -e "$KEYTAB" ]] || \ info "No keytab file available, renew existing ticket for $PRINCIPAL in KRB5 credential cache file ($KRB5CCNAME)" if kinit -c "$KRB5CCNAME" -R "$PRINCIPAL"; then info "Ticket for $PRINCIPAL renewed:\n$( klist -c "$KRB5CCNAME" 2>&1 )" exit 0 fi error "Failed to renew existing ticket for $PRINCIPAL in KRB5 credential cache file" \ "($KRB5CCNAME). Probably its renewing period expired." else info "KRB5 credential cache file not found ($KRB5CCNAME)." \ "No existing ticket for $PRINCIPAL to renew." fi if [[ -t 1 ]]; then echo "Try to obtain a ticket for $PRINCIPAL interactively" init_kbr5cc && exit 0 else error "Please rerun this script interactively to reobtain a ticket for $PRINCIPAL:\n sudo -u '$OWNER' $*" fi exit 1 </code> - rendez le exécutable : <code bash>chmod 750 /usr/local/bin/renew-keytab</code> **Exemple d'utilisation :** <code bash> # Renouveller un TGT existant renew-keytab /etc/myuser.ccname myuser@DOMAIN.TLD # Renouvellement avec un fichier keytab (nouvelle authentification) renew-keytab /etc/myuser.ccname myuser@DOMAIN.TLD -k /etc/myuser.keytab </code> **Arguments optionnels :** * ''-k'' ou ''--keytab'' : le fichier ''keytab'' à utiliser pour une nouvelle authentification * ''-o'' ou ''--owner'' : préciser le propriétaire des fichiers manipulés. Cela permet de s'assurer que le script est lancé en tant que le bon utilisateur. Si ce n'est pas le cas, le script sera automatiquement réappeler en tant que le bon utilisateur (via ''sudo''). Si le paramètre n'est pas fourni, le propriétaire sera automatiquement déterminer à partir du fichier ''keytab'' existant ou à défaut du fichier ''ccache'' existant. * ''-c'' ou ''--create'' : active le mode création. Dans ce mode, un fichier //ccache// sera recréé via une authentification à partir d'un fichier //keytab// et ce dernier sera créé s'il n'existe pas. Ce mode peut-être utilisé pour faciliter la création d'un couple de fichiers //keytab / ccache//. * ''-d'' ou ''--debug'' : active le mode debug ===== Connexion via ldapsearch ===== On installe pour commencer les dépendances pour l'authentification SASL GSSAPI : <code bash>apt-get install sasl2-bin libsasl2-2 libsasl2-modules libsasl2-modules-gssapi-mit</code> On peut ensuite interroger l'annuaire LDAP avec ''ldapsearch'' : <code bash>KRB5CCNAME=/path/to/file.ccname ldapsearch -H ldaps://AD1.domain.tld -Y GSSAPI -U 'myuser@DOMAIN.TLD' -b DC=domain,DC=tld -s base dc</code> <note warning>En cas de soucis d'authentification sur un AD via LDAPS (mais pas en LDAP), tenter d'ajouter la ligne suivante dans le fichier ''/etc/ldap/ldap.conf'' : <code>sasl_secprops minssf=0,maxssf=0</code> **Source :** [[https://bugs.launchpad.net/ubuntu/+source/cyrus-sasl2/+bug/1015819]]</note> ===== Connexion en python (avec python-ldap) ===== <code python> import os import ldap # Auth using keytab file # os.environ['KRB5_CLIENT_KTNAME'] = '/etc/myuser.keytab' # Auth using ccache file os.environ['KRB5CCNAME'] = '/etc/myuser.ccache' ldap_conn = ldap.initialize('ldaps://AD1.domain.tld') ldap_conn.sasl_non_interactive_bind_s('GSSAPI') print(ldap_conn.whoami_s()) </code> ===== Connexion en PHP ===== <code php> <?php $conn = ldap_connect("ldaps://AD1.domain.tld", 636) or die("Failed to connect to LDAP host"); ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($conn, LDAP_OPT_REFERRALS, 0); // Auth using keytab file // putenv("KRB5_KTNAME=/path/to/file.keytab"); // Auth using ccache file putenv("KRB5CCNAME=/path/to/file.ccname"); ldap_sasl_bind($conn, null, null, 'GSSAPI') or die("Kerberos auth failure: ".ldap_error($conn)); printf("Connected as %s\n", ldap_exop_whoami($conn)); </code>