Ai LNF e' stata implementata la rete wireless INFN-dot1x secondo le specifiche del progetto TRIP per l'accesso ai servizi informatici dedicato ai dipendenti dell'INFN.
La soluzione tecnica prevede l'annuncio di una rete WL con SSID INFN-dot1x su VLAN dedicata a cui e' attribuita una network IP pubblica "di classe C", avente gli stessi diritti di accesso alle risorse informatiche fornite dai Laboratori, rispetto ai nodi connessi alla LAN in modalita' wired.
Questo documento fornisce una guida per l'installazione e la configurazione di un radius server locale per l'autorizzazione alla connessione sulla rete wireless INFN-dot1x. Seguendo le indicazioni si potra' costruire un server radius in grado di demandare (via proxy) tutte le autenticazioni di tipo EAP-TTLS e PEAP ai realm INFN diversi da lnf.infn.it, ma anche in grado di autenticare autonomamente tutte le richieste di tipo EAP-TLS (purche' l'utente si presenti con un certificato personale rilasciato dalla CA TERENA), ma anche di tipo EAP-TTLS e PEAP-MSCHAPv2 tramite l'inserimento delle credenziali kereberos dei LNF.
radiusd.conf
potrebbe essere utile impostare opportunamente la sezione log per inviare i log ad un logserver remoto, invece che su file locale:
log { ... destination = syslog ... syslog_facility = local6 ... }
eap
eap { ... default_eap_type = ttls ... tls { ... ##private_key_password = whatever ## Inserire i nomi dei files rispettivamente della chiave privata e del certificato x509 (rilasciato dalla CA INFN) private_key_file = ${certdir}/myradius-key.pem certificate_file = ${certdir}/myradius-cert.pem ... ## inserire il file contenente tutti i certificati pubblici delle seguenti CA: INFN e TERENA ## Attenzione!: per potersi autenticare con i certificati TERENA e' necessario aggiungere anche i certificati ## di COMODO (root CA) e UTN-USERFirst-Client-Authentication-and-Email (Intermediate CA) [Vedi file INFN-TERENA-CA.pem allegato] ca_file = ${cadir}/myradius-chain.pem ... fragment_size = 1200 check_cert_cn = %{User-Name} ... } ... ttls { ... ##default_eap_type = md5 ... copy_request_to_tunnel = yes ... use_tunneled_reply = yes ... virtual_server = "inner-tunnel" ... } ... peap { ... default_eap_type = mschapv2 ... copy_request_to_tunnel = yes ... use_tunneled_reply = yes ... virtual_server = "inner-tunnel" ... } ... }
Allegato: INFN-TERENA-CA.pem
proxy.conf
home_server localhost { ... secret = mysecret1 ... } home_server radius.infn.it { type = auth+acct ipaddr = 193.206.190.118 port = 1812 secret = mysecret2 require_message_authenticator = yes response_window = 20 zombie_period = 40 revive_interval = 60 status_check = status-server check_interval = 30 num_answers_to_alive = 3 max_outstanding = 65536 coa { irt = 2 mrt = 16 mrc = 5 mrd = 30 } } home_server radius2.infn.it { type = auth+acct ipaddr = 193.206.144.38 port = 1812 secret = mysecret2 require_message_authenticator = yes response_window = 20 zombie_period = 40 revive_interval = 60 status_check = status-server check_interval = 30 num_answers_to_alive = 3 max_outstanding = 65536 coa { irt = 2 mrt = 16 mrc = 5 mrd = 30 } } home_server_pool radius-infn-pool { type = load-balance home_server = radius.infn.it home_server = radius2.infn.it } ... realm LOCAL { # If we do not specify a server pool, the realm is LOCAL, and # requests are not proxied to it. } ... realm NULL { authhost = LOCAL accthost = LOCAL nostrip } ... realm lnf.infn.it { authhost = LOCAL accthost = LOCAL strip } ... realm "~^(.+\\.|)infn\\.it$" { pool = radius-infn-pool nostrip } ... realm DEFAULT { authhost = LOCAL accthost = LOCAL nostrip }
clients.conf
client localhost { ... secret = mysecret ... } ... # # Definizione relativa al server stesso (radius.lnf.infn.it) # client radius.lnf.infn.it { secret = mysecret shortname = radius nastype = other } # # Definizione relativa al radius server di autenticazione dei LNF su rete wireless eduroam (edugw.lnf.infn.it) # client edugw.lnf.infn.it { secret = mysecret shortname = edugw nastype = other } # # Definizione (eventuale) del server di monitoring (Nagios) # client 193.206.84.50 { secret = mysecret shortname = nagios nastype = other } # # Proxy INFN (TRIP per SSID INFN-dot1x) # client radius.infn.it { ipaddr = 193.206.190.118 secret = mysecret2 shortname = TRIP-INFN-dot1x-1 nastype = other } client radius2.infn.it { ipaddr = 193.206.144.38 secret = mysecret2 shortname = TRIP-INFN-dot1x-2 nastype = other } # # Definizione del server proxy del GARR (DEFAULT GARR) # #client 192.84.145.15 { # secret = mysecret2 # shortname = eduroam # nastype = other #} # # VPNBOX dei LNF # client 193.206.84.7 { secret = mysecret shortname = vpnbox nastype = cisco } # # Captive Portal # client 193.205.228.106 { secret = mysecret shortname = tino nastype = other } # # Definizione del Wireless LAN Controller WLC4400 AP Ports IP # client 172.16.14.18 { secret = mysecret shortname = wlc4400 nastype = cisco } client 172.16.14.19 { secret = mysecret shortname = wlc4400ap nastype = cisco } # # Elenco degli aironet dei LNF ordinati per IP Address (o N. di edificio) # client 172.16.1.2 { secret = mysecret shortname = ed1ap1 nastype = cisco } ... etc. ...
In alternativa alla definizione dell'elenco dei client ad uno ad uno, dovrebbe essere possibile anche inserire tutta una network (soluzione non testata):
client 172.16.0.0/16 { secret = mysecret shortname = accesspoint nastype = cisco }
modules/logger-accept e modules/logger-reject
exec logger-accept { wait = yes #radiuspid = "%{exec:/bin/cat ${pidfile}}" outlog = "Access Accepted in Post-Auth session for user [%{User-Name}] [Issuer: %{TLS-Client-Cert-Issuer} - Subject: %{TLS-Client-Cert-Subject}]" program = "/usr/bin/logger -p local6.info -t radiusd[$pid$] ${outlog}" input_pairs = request output_pairs = reply #packet_type = Access-Accept shell_escape = yes }
exec logger-reject { wait = yes #radiuspid = "%{exec:/bin/cat ${pidfile}}" outlog = "Access Rejected in Post-Auth session for user [%{User-Name}] due to local policy [Issuer: %{TLS-Client-Cert-Issuer} - Subject: %{TLS-Client-Cert-Subject}]" program = "/usr/bin/logger -p local6.info -t radiusd[$pid$] ${outlog}" input_pairs = request output_pairs = reply #packet_type = Access-Accept shell_escape = yes }
sites-enabled/default
... authorize { ... preprocess ... chap ... mschap ... suffix ... eap { ok = return } ... files ... expiration logintime ... pap ... } ... Authenticate { ... Auth-Type PAP { pap } ... Auth-Type CHAP { chap } ... Auth-Type MS-CHAP { mschap } ... Auth-Type Kerberos { krb5 } ... ntlm_auth ... eap ... }
... post-auth { ... # update reply { # Reply-Message += "%{TLS-Cert-Serial}" # Reply-Message += "%{TLS-Cert-Expiration}" # Reply-Message += "%{TLS-Cert-Subject}" # Reply-Message += "%{TLS-Cert-Issuer}" # Reply-Message += "%{TLS-Cert-Common-Name}" # # Reply-Message += "%{TLS-Client-Cert-Serial}" # Reply-Message += "%{TLS-Client-Cert-Expiration}" # Reply-Message += "%{TLS-Client-Cert-Subject}" # Reply-Message += "%{TLS-Client-Cert-Issuer}" # Reply-Message += "%{TLS-Client-Cert-Common-Name}" # } ... ### if ( TLS-Client-Cert-Subject !~ /^$/ ) { ### if ( TLS-Client-Cert-Issuer != "/C=NL/O=TERENA/CN=TERENA Personal CA" ) { ### logger-reject ### reject ### } ### if ( TLS-Client-Cert-Subject !~ /^\/C=IT\/O=Istituto Nazionale di Fisica Nucleare\/CN=.+$/ ) { ### logger-reject ### reject ### } ### else { ### logger-accept ### } ### } ### if ( TLS-Client-Cert-Subject !~ /^$/ ) { if ( TLS-Client-Cert-Issuer != "/C=NL/O=TERENA/CN=TERENA Personal CA" && TLS-Client-Cert-Issuer != "/C=NL/ST=Noord-Holland/L=Amsterdam/O=TERENA/CN=TERENA Personal CA 3" ) { logger-reject reject } if ( TLS-Client-Cert-Subject !~ /^\/C=IT.*\/O=Istituto Nazionale di Fisica Nucleare\/CN=.+$/ ) { logger-reject reject } else { logger-accept } } ... }
sites-enabled/inner-tunnel
Il server inner-tunnel viene chiamato in causa nel caso di richiesta di autenticazione passata all'interno di un tunnel (PEAP o EAP-TTLS)
server inner-tunnel { ... authorize { ... chap ... mschap ... suffix ... ... eap { ok = return } ... files ... expiration logintime ... pap ... # if ( "%{outer.request:User-Name}" != "%{User-Name}" ) { # fail # } } ... Authenticate { ... Auth-Type PAP { pap } ... Auth-Type CHAP { chap } ... Auth-Type MS-CHAP { mschap } ... Auth-Type Kerberos { krb5 } ... ntlm_auth ... eap ... } ... }
modules/krb5
chmod g-r,o-r krb/.radius.keytab
krb5 { keytab = ${raddbdir}/krb/.radius.keytab service_principal = radius/radius.lnf.infn.it }
users
test Auth-Type := Local, Cleartext-Password := "mypassword" DEFAULT Auth-Type = Kerberos, Realm == "lnf.infn.it" DEFAULT Proxy-To-Realm := "~^(.+\\.|)infn\\.it$", Realm == "~^(.+\\.|)infn\\.it$" DEFAULT Auth-Type := Reject, Realm == "DEFAULT" $INCLUDE users.guests DEFAULT Auth-Type = Kerberos, Realm == "NULL" Fall-Through = Yes
Se non si vuole abilitare l'autenticazione PEAP-EAP/TLS saltare al paragrafo successivo (Start del server radius).
Il protocollo di autenticazione PEAP-EAP/TLS e' utilizzato esclusivamente dai supplicant con sistema operativo MS-Windows. Windows permette di configrare nativamente tale protocollo, che consente di effettuare l'autenticazione tramite presentazione del certificato, come per EAP/TLS, ma dentro un tunnel PEAP che seguirebbe quindi la strada dei proxy, in funzione del realm della username. Ovvero l'autenticazione, invece di essere effettuata dal server radius locale (della WLAN a cui ci si sta connettendo), verrebbe effettuata dal server della sede di appartenenza.
Volendo abilitare l'autenticazione PEAP-EAP/TLS, occorre fare in modo che radius generi una seconda istanza di eap (inner-eap); seguire i seguenti passi:
modules/inner-eap
eap inner-eap { ... default_eap_type = tls ... mschapv2 { } ... tls { ... certdir = ${confdir}/certs cadir = ${confdir}/certs ... ##private_key_password = whatever ## Inserire i nomi dei files rispettivamente della chiave privata e del certificato x509 (rilasciato dalla CA INFN) private_key_file = ${certdir}/myradius-cainfn-key.pem certificate_file = ${certdir}/myradius-cainfn-cert.pem ... ## inserire il file contenente tutti i certificati pubblici delle seguenti CA: INFN e TERENA ## Attenzione!: per potersi autenticare con i certificati TERENA e' necessario aggiungere anche i certificati ## di COMODO (root CA) e UTN-USERFirst-Client-Authentication-and-Email (Intermediate CA) [Vedi file INFN-TERENA-CA.pem allegato] CA_file = ${cadir}/INFN-TERENA-CA.pem ... ## Attenzione il fragment size deve essere inferiore a quello definito nel file eap.conf (valori consigliati ## sono: 1200 in eap.conf e 1024 in modules/inner-eap fragment_size = 1024 ... ## attenzione: commentare la seguente riga: ##check_cert_cn = %{User-Name} ... } ... }
eap.conf
eap { ... tls { ... ## attenzione: commentare la seguente riga: ##check_cert_cn = %{User-Name} ... } ... }
sites-enabled/inner-tunnel
server inner-tunnel { ... authorize { ... inner-eap { ok = return } ... } ... Authenticate { ... inner-eap ... } ... post-auth { ... /C=NL/ST=Noord-Holland/L=Amsterdam/O=TERENA/CN=TERENA Personal CA 3 if ( TLS-Client-Cert-Subject !~ /^$/ ) { if ( TLS-Client-Cert-Issuer != "/C=NL/O=TERENA/CN=TERENA Personal CA" && TLS-Client-Cert-Issuer != "/C=NL/ST=Noord-Holland/L=Amsterdam/O=TERENA/CN=TERENA Personal CA 3" ) { logger-reject reject } if ( TLS-Client-Cert-Subject !~ /^\/C=IT.*\/O=Istituto Nazionale di Fisica Nucleare\/CN=.+$/ ) { logger-reject reject } else { logger-accept } } ... }
chown -R radiusd:radiusd * chmod -R u+rw,g+r,o+r * chmod -R g-r,o-r certs/*
/usr/custom/freeradius/sbin/radiusd -X
oppure (piu' verboso):
/usr/custom/freeradius/sbin/radiusd -Xxx
/etc/init.d/radiusd start
chkconfig --add radiusd chkconfig --level 35 radiusd on
/usr/custom/freeradius/bin/radtest test mypassword localhost 10 mysecret
(verificare il file di log /var/log/messages)
Per le sedi che usano l'applicazione a valenza nazionale per la Gestione Ospiti (o piu' propriamente Gestione Visitatori), e' possibile sincronizzare sul radius server le username che possono avere accesso alla rete INFN-Web tramite il Captive Portal tino.
'--post-data='USER=<sede>&PASS=<GOpassword_for_sede>&USER_FORMAT=#USER##TAB##TAB#Auth-Type+:=+Local,+Cleartext-Password+:=+"#PWD#"#NL#' \
'*/10 * * * * /usr/custom/bin/GOsync.sh
Per le sedi che usano kerberosV come protocollo di autenticazione, e' possibile fare in modo che l'autenticazione di tipo PEAP-MSCHAPv2 venga demandata a kerberosV. PEAP-MSCHAPv2 e' il metodo standard di autenticazione dei supplicant Windows ed e' supportato anche dalle piattaforme Mac OS X e Linux.
Nota: perche' cio' sia possibile occorre che il kdc sia configurato per supportare l'encryption "arcfour-hmac:normal" (che corrisponde all'hash della password utilizzato da NTLM). Se non lo fosse occorre aggiungere tale encryption nel kdc.conf e forzare un cambio password, per gli utenti interessati, per poter utilizzare l'encryption appena inserita.
Per implementare tale metodo di autenticazione c'e' necessita' di creare una nuova struttura di tipo client-server basata sul servizio kcrap. Kcrap e' un s/w free realizzato da Jonathan Chen e modificato da Dan Fuhry per adattarlo all'uso con freeradius
Riferimenti:
Il s/w con le modifiche di Dan Fuhry, e' stato installato e testato ai LNF da Sandro Angius. Tuttavia, per eliminare qualche piccolo malfunzionamento e per adattarlo alle esigenze dell'INFN, Sandro ha dovuto effettuare alcune modifiche, di cui le piu' importanti sono:
Alcune di queste modifiche sono state inglobate da Dan Fuhry nella sua versione del s/w. A seguito di queste modifiche sono stati creati due nuovi pacchetti di distribuzione (by Sandro Angius):
Il primo e' da utilizzare per chi ha kerberosV MIT versione 1.6, il secondo e' da utilizzare per chi ha kerberosV MIT versione 1.9. Il primo tarball dovrebbe funzionare anche con kerberosV versione 1.7, ma non e' stato testato con tale versione. Infatti soltanto nella versione 1.9 di kerberosV cambia il metodo di accesso al DB delle credenziali, ed e' per questo che il kcrap server e' stato modificato per quella versione.
Il s/w kcrap server va installato su un kdc. Per motivi di sicurezza, e' caldamente consigliato di utilizzare un secondary kdc. Per installare kcrap server e' necessario rispettare i seguenti prerequisiti:
Per l'installazione seguire i seguenti passi:
mkdir /root/kcrap cd /root/kcrap wget http://web.mit.edu/kerberos/dist/krb5/1.6/krb5-1.6.1-signed.tar tar xf krb5-1.6.1-signed.tar tar xzf krb5-1.6.1.tar.gz cd krb5-1.6.1/src/ ./configure
tar xzf kcrap-0.2.3-LNF.tgz cd kcrap-0.2.3-LNF/ ./configure --prefix=/usr/custom/kcrap --with-mit-krb5-src=/root/kcrap/krb5-1.6.1/src/include/ --with-server CFLAGS=-I/usr/include/et LIBS=-lkadm5srv make make install ln -s /usr/custom/kcrap/lib/libkcrap.so /usr/lib/libkcrap.so ldconfig
[kcrap_server] port = 1999 realm = LNF.INFN.IT [realms] LNF.INFN.IT = { database_name = /var/kerberos/krb5kdc/principal key_stash_file = /var/kerberos/krb5kdc/.k5.LNF.INFN.IT }
... [logging] kcrap_server = SYSLOG:INFO:LOCAL3 ## metodo consigliato: i log vengono inviati al SYSLOG con severity INFO e facility LOCAL3 ##kcrap_server = FILE:/var/log/kcrap.log ## metodo sconsigliato, alcuni errori non avrebbero il formato con data e ora, etc. ...
... [realms] LNF.INFN.IT = { ... ... kcrap = kcrapsrv1.lnf.infn.it:1999 } ...
iptables -A INPUT -m state --state NEW -s 193.206.84.29/255.255.255.255 -m udp -p udp --dport 1999 -j ACCEPT
(ovviamente prima della necessaria direttiva di drop finale dei pacchetti udp)
#!/bin/bash # # kcrap_server Start and stop the KCRAP server. # # chkconfig: - 35 65 # description: Kerberos Challenge Response Authentication Protocol Daemon. # processname: kcrap_server # config: /etc/sysconfig/kcrap_server # # Get config. . /etc/sysconfig/network # Get config. [ -r /etc/sysconfig/kcrap_server ] && . /etc/sysconfig/kcrap_server # Source function library. . /etc/rc.d/init.d/functions RETVAL=0 prog="KCRAP Server" kcrap_server=/usr/custom/kcrap/sbin/kcrap_server # Sheel functions to cut down on useless shell instances. start() { [ -x $kcrap_server ] || exit 5 echo -n $"Starting $prog: " daemon ${kcrap_server} RETVAL=$? echo [ $RETVAL = 0 ] && touch /var/lock/subsys/kcrap_server } stop() { echo -n $"Stopping $prog: " killproc ${kcrap_server} RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/kcrap_server } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart) stop start ;; status) status ${kcrap_server} RETVAL=$? ;; condrestart) if [ -f /var/lock/subsys/kcrap_server ] ; then stop start fi ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart}" RETVAL=2 ;; esac exit $RETVAL
/etc/init.d/kcrap start
chkconfig --add kcrap chkconfig --level 35 kcrap on
Procedure di postinstallazione:
Per testare che tutto funzioni, creare temporaneamente l'utente kerberos con username user e password SecREt01, quindi lanciare il seguente comando:
## /usr/custom/kcrap/bin/kcrapclient <username> <challenge> <response> /usr/custom/kcrap/bin/kcrapclient user 0123456789abcdef 25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6
e verificare che la stringa di ritorno sia:
NT_KEY: 3f373ea8e4af954f14faa506f8eebdc4
verificare anche che cambiando una cifra (esadecimale) della challenge o della response l'autenticazione fallisce.
Note:
Se il syslog e' configurato per i livelli a partire da info il log sara' del tipo:
Mar 27 17:21:18 kdcs3 kcrap[8649]: Kcrap server started. Mar 27 17:21:26 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.206.84.123 succeeded. Mar 27 17:21:28 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.206.84.123 failed. Mar 27 17:27:07 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.205.228.102 succeeded. Mar 27 17:27:08 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.205.228.102 failed.
mentre con i livelli da debug si ottiene:
Mar 27 17:27:09 kdcs3 kcrap[8649]: Datagram of 600 bytes received from address: 193.206.84.123. Mar 27 17:27:09 kdcs3 kcrap[8649]: Request for username 'user' from 193.206.84.123 with challenge 0xbd50a3fccd292811. Mar 27 17:27:09 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.206.84.123 succeeded. Mar 27 17:27:10 kdcs3 kcrap[8649]: Datagram of 600 bytes received from address: 193.206.84.123. Mar 27 17:27:10 kdcs3 kcrap[8649]: Request for username 'user' from 193.206.84.123 with challenge 0xbd50a3fccd292811. Mar 27 17:27:10 kdcs3 kcrap[8649]: Authentication for username 'user' from 193.206.84.123 succeeded.
Per installare kcrapclient sul server radius e' necessario rispettare i seguenti prerequisiti:
Quindi seguire le istruzioni:
tar xzf kcrap-0.2.3-LNF.tgz cd kcrap-0.2.3-LNF/ ./configure --prefix=/usr/custom/kcrap CFLAGS=-I/usr/include/et LIBS=-lkadm5srv make make install ln -s /usr/custom/kcrap/lib/libkcrap.so /usr/lib/libkcrap.so ldconfig
... [realms] LNF.INFN.IT = { ... ... kcrap = kcrapsrv1.lnf.infn.it:1999 kcrap = kcrapsrv2.lnf.infn.it:1999 kcrap = kcrapsrv3.lnf.infn.it:1999 } ...
chmod a+rx /usr/custom/kcrap/bin/kcrapclient
e dare i diritti di lettura a radiusd (owner del radius daemon):
chown root:radiusd /etc/krb5.keytab chmod g+r /etc/krb5.keytab
Procedure di postinstallazione:
Per testare che tutto funzioni, lanciare il seguente comando:
## /usr/custom/kcrap/bin/kcrapclient <username> <challenge> <response> /usr/custom/kcrap/bin/kcrapclient user 0123456789abcdef 25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6
e verificare che la stringa di ritorno sia:
NT_KEY: 3f373ea8e4af954f14faa506f8eebdc4
verificare anche che cambiando una cifra (esadecimale) della challenge o della response l'autenticazione fallisce.
Integrazione con freeradius
Una volta installato kcrap (server e client) si puo' fare in modo che l'autenticazione PEAP-MSCHAPv2 venga demandata da freeradius a kerberos tramite kcrap:
* Modificare il file modules/mschap
... mschap { ... ## ntlm_auth = "/path/to/ntlm_auth --request-nt-key --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}" ntlm_auth = "/usr/custom/kcrap/bin/kcrapclient %{%{Stripped-User-Name}:-%{%{User-Name}:-None}} %{%{mschap:Challenge}:-00} %{%{mschap:NT-Response}:-00}" ... }
* Quindi far ripartire freeradius
/etc/init.d/radiusd restart
A questo punto si puo' provare a configurare un supplicant affinche' utilizzi il protocollo di autenticazione PEAP-MSCHAPv2, e tentare l'autenticazione con le proprie credenziali kerberos
Nota: quando tutto funziona, ricordarsi di rimuovere la username user da kerberos!