Utilizzare ticket Kerberos per l’autenticazione a servizi remoti

0

Kerberos

Questo articolo descrive come sia possibile configurare il proprio sistema affinché si autentichi verso servizi remoti utilizzando non, come accade di consueto, informazioni di autenticazione (username e password), ma ticket Kerberos.
L’obiettivo finale è quello di creare una struttura automatizzata per la gestione di applicazioni che accedano a servizi locali sfruttando i ticket anziché i consueti meccanismi di autenticazione. In particolare l’obiettivo primario è quello di dare la possibilità ad utenti non privilegiati di richiedere ticket, memorizzarli e lanciare applicazioni ad essi associate.
Verrà inoltre trattato un caso di studio in cui, mediante l’utilizzo di strumenti di sistema come ktutil e k5start, sarà gestita la scadenza ed il rinnovo automatico dei ticket Kerberos mediante keytab locali.

Cos’è Kerberos?

Kerberos è un sistema di autenticazione per reti, creato dal MIT (http://web.mit.edu/kerberos/www/), che non prevede l’invio di credenziali da un client (inteso come “richiedente” di un accesso) verso un server (inteso come “fornitore”), ma si basa su chiavi segrete criptate.
Il principio su cui si basa questo protocollo è l’introduzione di una terza entità, definita KDC (Key Distribution Center), che fornisce ticket (letteralmente biglietti) basandosi sulla chiave criptata fornita dal dal richiedente e ne verifica la validità per i fornitori.
Questa modalità a tre vie (come tre sono le teste del cane Cerebro/Kerberos nella Divina Commedia) garantisce accessi senza la necessità della consueta verifica di username e password.
Kerberos è alla base di molti sistemi di rete che gestiscono informazione di autenticazione in forma centralizzata, uno su tutti, Microsoft Active Directory.

Kerberos in Linux

Come è stato illustrato nella serie di articoli riguardanti l’integrazione tra Linux ed Active Directory, Kerberos è parte integrante della Join che il servizio SAMBA effettua con i domini Microsoft Windows basati su Active Directory (AD).
I ragionamenti descritti in questo articolo partono quindi da quanto descritto nella serie citata, in cui una macchina Linux è in grado di autenticarsi presso un dominio AD e fornire agli utenti di dominio la possibilità di effettuare login all’interno del sistema con credenziali quali DOMINIO.IT\nomeutente.

La componente essenziale che consentirà di raggiungere l’obiettivo prefissato è pertanto Kerberos. Il principio di funzionamento è quello di richiedere un ticket identificativo al server di dominio con il quale sia possibile autenticarsi presso gli host e i servizi all’interno dello stesso.
I pacchetti necessari all’installazione di Kerberos su sistemi Red Hat / CentOS sono:

# yum install krb5-libs krb5-workstation

Mentre su i sistemi Debian / Ubuntu sono invece:

# apt-get install krb5-user krb5-clients

Il file di configurazione di riferimento è /etc/krb5.conf, il cui contenuto per ciascuna delle macchine dovrà essere qualcosa di simile a quanto segue:

[logging] 
 default = FILE:/var/log/krb5libs.log 
 kdc = FILE:/var/log/krb5kdc.log 
 admin_server = FILE:/var/log/kadmind.log 

[libdefaults] 
 default_realm = MYDOMAIN.IT 
 dns_lookup_realm = false 
 dns_lookup_kdc = false 
 ticket_lifetime = 24h 
 forwardable = yes 
 default_tkt_enctypes = aes128-cts des3-cbc-sha1 rc4-hmac arcfour-hmac-md5 des-cbc-md5 des-cbc-crc 
 default_tgs_enctypes = aes128-cts des3-cbc-sha1 rc4-hmac arcfour-hmac-md5 des-cbc-md5 des-cbc-crc 
 permitted_enctypes = rc4-hmac arcfour-hmac-md5

[realms] 
 MYDOMAIN.IT = { 
  kdc = kdc1.MYDOMAIN.it 
  kdc = kdc2.MYDOMAIN.it 
  kdc = kdc3.MYDOMAIN.it 
  admin_server = kdc1.MYDOMAIN.it 
  default_domain = MYDOMAIN.it 
 } 

[domain_realm] 
 .MYDOMAIN.it = MYDOMAIN.IT 
 MYDOMAIN.it = MYDOMAIN.IT

Particolare attenzione, sulla base di come il protocollo verrà implementato (e su quali applicazioni), va prestata alle opzioni default_tkt_enctypes, default_tgs_enctypes e permitted_enctypes, le cui valorizzazioni risultano essenziali per il corretto funzionamento del sistema.
Ad esempio, nel caso specifico trattato nell’articolo, il valore RC4-HMAC non risulta abilitato di default, ma è essenziale per il programma di test che verrà utilizzato. Esso infatti prevede che una applicazione Java possa collegarsi via Kerberos sfruttando il driver JDBC ad un database MS-SQL (si veda qui: http://msmvps.com/blogs/sp/archive/2007/06/05/integrating-java-jdbc-and-kerberos.aspx per maggiori dettagli).

Per verificare il funzionamento del sistema Kerberos è possibile richiedere un ticket generale di accesso alla rete:

[MYDOMAIN\myadmin@mymachine ~]$ kinit -V myadmin@MYDOMAIN.IT 
Password for myadmin@MYDOMAIN.IT: 
Authenticated to Kerberos v5

Una volta che l’operazione è completata con successo il ticket richiesto sarà visibile nella cache:

[MYDOMAIN\myadmin@mymachine ~]$ klist -5
Ticket cache: FILE:/tmp/krb5cc_20000 
Default principal: myadmin@MYDOMAIN.IT 

Valid starting     Expires            Service principal 
07/03/13 15:21:47  07/04/13 01:21:53  krbtgt/MYDOMAIN.IT@MYDOMAIN.IT 
	renew until 07/04/13 15:21:47

Verificare il funzionamento di Kerberos

La modalità con cui i test di funzionamento Kerberos vengono effettuati varia a seconda di quello che è l’ambito operativo. Si potrebbe scegliere di implementare Kerberos per servizi come autofs o ssh, oppure scegliere di sfruttare l’autenticazione via ticket per delle applicazioni locali.
Nel caso descritto è stata creata una semplice applicazione Java, denominata ConnectionCheck.jar che, dato un file di testo in input, effettua ogni minuto una connessione al database indicato.
I test vengono effettuati su un database MS-SQL, sfruttando le seguenti impostazioni:

connection_string = jdbc:sqlserver://dbserver;authenticationScheme=JavaKerberos;integratedSecurity=true; 
driver =  com.microsoft.sqlserver.jdbc.SQLServerDriver

Sulla macchina dbserver risponderà quindi un servizio MS-SQL in ascolto che dovrà per ovvie ragioni essere configurato all’occorrenza.
Non è nello scopo di questo articolo addentrarsi nelle configurazioni del database lato server, essenziale è che questi supporti l’autenticazione via Kerberos e che l’utente in questione (myadmin) sia abilitato all’accesso.

Se quindi esiste un ticket locale valido valido, l’applicazione così invocata:

[MYDOMAIN\myadmin@mymachine ~]$ java -jar ConnectionCheck.jar connessione.properties 
2013/10/03 12:47:19- Integrated Security *** OK *** - URL: jdbc:sqlserver://dbserver;authenticationScheme=JavaKerberos;integratedSecurity=true; - Driver: com.microsoft.sqlserver.jdbc.SQLServerDriver

Risponderà nella maniera corretta e continuerà ogni minuto a stabilire una connessione con il database in modo da verificare la validità dei ticket locali.

Integrazione di Kerberos nelle applicazioni

Nel caso descritto poco fa l’applicazione sfrutta il ticket presente nella cache, generato dalla richiesta effettuata mediante kinit. Rimane però un problema da affrontare nello standardizzare l’integrazione dell’autenticazione Kerberos all’interno delle applicazioni: la natura stessa del protocollo. In particolare il fatto che i ticket su cui Kerberos si basa hanno una scadenza.
Il limite appare evidente. Partendo dal presupposto di riuscire a integrare la richiesta di un ticket da parte di un utente non privilegiato in fase di login, rendendo quindi l’operazione trasparente, questo avrebbe comunque una scadenza. Una volta raggiunta la fatidica data, l’accesso da parte dell’utente (e dell’applicazione da esso lanciata) verrebbe negato.

Riporto quanto presente nella documentazione di Kerberos (disponibile all’indirizzo http://www.kerberos.org/software/tutorial.html):

Each ticket has an expiration (generally 10 hours). This is essential since the authentication server no longer has any control over an already issued ticket. Even though the realm administrator can prevent the issuing of new tickets for a certain user at any time, it cannot prevent users from using the tickets they already possess. This is the reason for limiting the lifetime of the tickets in order to limit any abuse over time.

L’unica filosofia applicabile a questo limite (in realtà quindi una vera e propria caratteristica di Kerberos) è gestire la scadenza dei ticket, chiaramente in maniera automatica.
Le soluzioni relative all’integrazione nelle applicazioni sono quindi due:

  1. Lunga scadenza (o lungo rinnovo): i ticket richiesti e generati dal server non rispettano scadenze standard, tipicamente sette giorni, ma tempi molto più lunghi (nell’ordine di mesi o anni).
  2. Creazione automatica: grazie a un automatismo quando un ticket è in prossimità della scadenza viene richiesto nuovamente.

Ticket con data rinnovo a lunga scadenza

La prima modalità è di facile applicazione e riguarda il server KDC (ossia il Domain Controller). Essa prevede l’impostazione per i profili delle macchine che richiedono i ticket, o più in generale per tutte le entità del dominio (qualsiasi Principal), una scadenza più ampia rispetto al default (7 giorni).
Il problema di questo approccio è che se il ticket con scadenza dilatata nel tempo dovesse andar smarrito, sottratto o banalmente copiato rimarrebbe comunque valido. In termini di sicurezza questo non è auspicabile.
Perciò la soluzione migliore è quella di lasciare la scadenza al valore di default e innalzare invece il periodo di renewal.
La finestra del periodo di renewal consente infatti di richiedere al server un aggiornamento del ticket senza che questo sia nuovamente generato e, soprattutto, senza che venga richiesta una password.
Il comando che rende possibile tutto questo è il seguente:

[MYDOMAIN\myadmin@mymachine ~]$ kinit -V -r 365d MYADMIN 
Password for MYADMIN@MYDOMAIN.IT: 
Authenticated to Kerberos v5

Il ticket generato potrà essere così visualizzato:

[MYDOMAIN\myadmin@mymachine ~]$ klist -5 
Ticket cache: FILE:/tmp/krb5cc_20000 
Default principal: MYADMIN@MYDOMAIN.IT 

Valid starting     Expires            Service principal 
10/03/13 15:23:52  10/04/13 01:23:57  krbtgt/MYDOMAIN.IT@MYDOMAIN.IT 
	renew until 10/04/14 1:23:57

La voce renew until indica come fino a quella data per il ticket potrà essere richiesto un renewal, mediante il comando:

[MYDOMAIN\myadmin@mymachine ~]$ kinit -V -R MYADMIN 
Authenticated to Kerberos v5

A questo punto, non essendo ancora sopraggiunta la scadenza del ticket, questo viene rinnovato:

[MYDOMAIN\myadmin@mymachine ~]$ klist -5
Ticket cache: FILE:/tmp/krb5cc_20000 
Default principal: MYADMIN@MYDOMAIN.IT 

Valid starting     Expires            Service principal 
10/03/13 15:27:20  10/04/13 01:27:25  krbtgt/MYDOMAIN.IT@MYDOMAIN.IT 
	renew until 10/04/14 1:23:527

È inutile aggiungere come al raggiungimento della data di renewal il ticket dovrà per forza di cose essere richiesto nuovamente. Automatizzare questo processo risulterebbe chiaramente problematico, perciò bisognerebbe prevedere un sistema di controllo dei ticket (ad esempio Nagios) che a fronte di una scadenza imminente avverta chi di dovere. La persona ingaggiata dovrà richiedere un nuovo ticket con la procedura riportata sopra.

Lato KDC è possibile aumentare la soglia di default di renewal seguendo le istruzioni contenute nel documento “Maximum lifetime for user ticket renewal” all’indirizzo http://technet.microsoft.com/en-us/library/jj852196%28v=ws.10%29.aspx.

Creazione automatica Ticket mediante keytab

Una modalità differente per affrontare la questione è quella di richiedere un nuovo ticket in prossimità della scadenza sfruttando non l’indicazione di una password, ma le entry criptate contenute in un file locale denominato keytab.
Dalla knowledge base di Kerberos (http://kb.iu.edu/data/aumh.html) il concetto di keytab viene così definito:

A keytab is a file containing pairs of Kerberos principals and encrypted keys (these are derived from the Kerberos password). You can use this file to log into Kerberos without being prompted for a password. The most common personal use of keytab files is to allow scripts to authenticate to Kerberos without human interaction, or store a password in a plaintext file.

Pertanto una volta generato il file locale è possibile utilizzarlo per autenticarsi verso il server KDC e richiedere un nuovo ticket.
Il comando per la creazione di un keytab è ktutil e può essere invocato come una shell interattiva:

[MYDOMAIN\myadmin@mymachine ~]$ /usr/kerberos/sbin/ktutil 
ktutil:  addent -password -p MYADMIN@MYDOMAIN.IT -k 1 -e rc4-hmac 
Password for MYADMIN@MYDOMAIN.IT: 
ktutil:  wkt myadmin.keytab 
ktutil:  exit

Il contenuto del file keytab è visionabile mediante il seguente comando:

[MYDOMAIN\myadmin@mymachine ~]$ klist -ekt ./myadmin.keytab 
Keytab name: FILE:./myadmin.keytab 
KVNO Timestamp         Principal 
---- ----------------- -------------------------------------------------------- 
   1 10/03/13 16:02:12 MYADMIN@MYDOMAIN.IT (ArcFour with HMAC/md5)

Una volta creato quindi il file myadmin.keytab è possibile utilizzarlo per richiedere dei ticket al server:

[MYDOMAIN\myadmin@mymachine ~]$ kinit -k -t ./myadmin.keytab -V MYADMIN@MYDOMAIN.IT 
Authenticated to Kerberos v5

Il comando kinit viene quindi invocato per l’utente MYADMIN@MYDOMAIN.IT utilizzando le credenziali registrate nel file keytab locale (-k -t ./myadmin.keytab).
La lista dei ticket disponibili mostra come il ticket sia stato ottenuto con successo:

[MYDOMAIN\myadmin@mymachine ~]$ klist -5
Ticket cache: FILE:/tmp/krb5cc_20000 
Default principal: MYADMIN@MYDOMAIN.IT 

Valid starting     Expires            Service principal 
10/03/13 16:02:35  10/04/13 02:02:35  krbtgt/MYDOMAIN.IT@MYDOMAIN.IT 
	renew until 10/04/13 16:02:35

Posto quindi che la richiesta del ticket è fattibile mediante keytab, non rimane che automatizzare il processo. Per fare questo è possibile utilizzare un programma denominato k5start che consente di monitorare costantemente lo stato di un ticket, richiedendo all’occorrenza il suo aggiornamento.
K5start possiede inoltre la capacità di lanciare al tempo stesso un comando (che verosimilmente necessita dell’autenticazione Kerberos per funzionare).
La modalità per invocare il comando è la seguente:

[MYDOMAIN\myadmin@mymachine ~]$ k5start -f /home/MYDOMAIN/myadmin/myadmin.keytab -b -K 1 -l 5m -L -U

Il comando illustrato sopra, ha le seguenti caratteristiche:

  • Fa riferimento al keytab myadmin.keytab (-f);
  • Funzionerà in background (-b);
  • Ogni minuto (-K 1), se il ticket è in scadenza, ne richiederà uno nuovo;
  • Il nuovo ticket scadrà dopo cinque minuti (-l 5m);
  • Il programma scriverà i log di eventuali errori mediante syslog (-L);
  • Il ticket richiesto sarà tale per la prima utenza letta nel keytab (-U);

Monitorando mediante klist lo stato del ticket si potrà così osservare come questo verrà costantemente aggiornato.

Creare uno script che automatizzi il lancio del programma

A completamento del progetto è possibile creare uno script che comprenda l’avvio di k5start e di un programma da esso dipendente, sulla falsa riga del seguente codice:

#!/bin/bash 
# 
# k5start:      Keeps Kerberos 5 ticket-granting ticket active indefinitely 
# Author:       Mark R. Bannister <cambridge@users.sourceforge.net> 
# 
# chkconfig: 345 19 74 
# description:  Keeps Kerberos 5 ticket-granting ticket active indefinitely 
# 
# processname: /usr/bin/k5start 
# config: /etc/krb5.conf 
# pidfile: /var/run/k5start.pid 

# Sanity checks. 
[ -f /etc/krb5.conf ] || exit 0 
[ -x /usr/bin/k5start ] || exit 0 

# Source function library. 
. /etc/init.d/functions 

K5START_KEYTAB=/etc/krb5.keytab 
K5START_MINUTES=30 
K5START_OPTIONS= 

# Source an auxiliary options file if we have one 
# This can override K5START_KEYTAB, K5START_MINUTES and K5START_OPTIONS 
# It can also set DAEMON_COREFILE_LIMIT and NICELEVEL 
[ -r /etc/sysconfig/k5start ] && . /etc/sysconfig/k5start 

RETVAL=0 

start() { 
    echo -n $"Starting k5start: " 
    daemon /usr/bin/k5start -f $K5START_KEYTAB -bLK$K5START_MINUTES \ 
                        -p /var/run/k5start.pid $K5START_OPTIONS -U 
    RETVAL=$? 
    echo 
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/k5start 
    return $RETVAL 
} 

stop() { 
    echo -n $"Stopping k5start: " 
    killproc -p /var/run/k5start.pid k5start 
    RETVAL=$? 
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/k5start 
    echo 
    return $RETVAL 
} 

restart() { 
    stop 
    start 
} 

# See how we were called. 
case "$1" in 
    start) 
        start 
        RETVAL=$? 
        ;; 
    stop) 
        stop 
        RETVAL=$? 
        ;; 
    status) 
        status -p /var/run/k5start.pid k5start 
        RETVAL=$? 
        ;; 
    restart) 
        restart 
        RETVAL=$? 
        ;; 
    try-restart | condrestart) 
        [ -e /var/lock/subsys/k5start ] && restart 
        RETVAL=$? 
        ;; 
    force-reload | reload) 
        echo -n $"Refreshing k5start ticket cache: " 
        killproc /usr/bin/k5start -ALRM 
        RETVAL=$? 
        echo 
        ;; 
    *) 
        echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}" 
        RETVAL=1 
        ;; 
esac 
exit $RETVAL

Lo script illustrato è stato originariamente pubblicato dagli autori a questo indirizzo: http://www.unix.com/shell-programming-scripting/169363-kstart-k5start-start-up-script.html

Conclusioni

Le potenzialità di quanto descritto, in termini di sicurezza e funzionalità, sono pressoché infinite. Sebbene infatti l’argomento in se risulti piuttosto ostico in quanto diversi sono i temi da trattare, i concetti da comprendere e le configurazioni da eseguire, a conti fatti Kerberos risulta una soluzione efficace (ed in certi ambiti irrinunciabile) per rendere più sicura l’accesso ai servizi della propria rete.

Da sempre appassionato del mondo open-source e di Linux nel 2009 ho fondato il portale Mia Mamma Usa Linux! per condividere articoli, notizie ed in generale tutto quello che riguarda il mondo del pinguino, con particolare attenzione alle tematiche di interoperabilità, HA e cloud.
E, sì, mia mamma usa Linux dal 2009.