Przejdź to tekstu

Konfiguracja WPA2 Enterprise z wykorzystaniem serwera RADIUS

Kategoria: Artykuły, etykiety: ca, certyfikat, eap, eap-tls, eap-ttls, freeradius, mysql, openwrt, peap, radius, wifi, wpa2 enterprise, wpasupplicant

Dodany przez: morfik, 2014-09-20 14:47 (zmodyfikowany: 2014-09-20 22:59)

Wyświetleń: 13491

Poniższy artykuł ma na celu stworzenie infrastruktury wifi w oparciu o oprogramowanie freeradius zainstalowane na debianowym serwerze. Projekt zakłada wykorzystanie osobnego urządzenia NAS (AP), w tym przypadku jest to router TP-Link TL-WR1043N/ND v2 , na którym jest zainstalowany OpenWRT Barrier Breaker (r42420). W oparciu o te dwie maszyny spróbujemy skonfigurować protokół WPA2 Enterprise z obsługą trzech metod uwierzytelniania -- EAP-TLS, EAP-TTLS oraz PEAP (v0) . Będziemy również potrzebować kilku certyfikatów, w tym też CA, które sobie stworzymy ręcznie.

W mojej sieci testowej router ma adres 192.168.1.1 , a serwer z debianem 192.168.1.166 . W tym przypadku będzie jedna maszyna kliencka (laptop) z adresem 192.168.1.150. Zarówno do serwera debiana jak i do routera będziemy się łączyć przez ssh. Debian został zaktualizowany z wydania stabilnego do testinga, nie posiada środowiska graficznego, jedynie podstawowy system + serwer ssh. Dodatkowo został wymieniony init, bo domyślnie debian ma systemd, a że ja tego inita praktycznie nie znam i do tego mam drobne problemy z openrc, to wgrałem starego poczciwego sysvinit. Można oczywiście zostawić systemd, tylko trzeba brać poprawki przy pewnych poleceniach.

Instalacja komponentów

Na serwerze debiana będziemy instalować oprogramowanie freeradius wraz z modułem obsługującym mysql. Potrzebny nam też będzie sam serwer bazy danych mysql. Logujemy się zatem na serwer debiana via ssh i instalujemy tam poniższe pakiety:

morfik:~$ ssh root@192.168.1.166
...
root@radius:~# aptitude install freeradius freeradius-mysql freeradius-utils mysql-server
...

Nie zaleca się odpalania daemona freeradius z uprawnieniami użytkownika root. W debianie, wszystkie niezbędne kroki, takie jak zmiana uprawnień plików w katalogu /etc/freeradius/ , czy sama konfiguracja użytkownika/grupy freerad, zostały przeprowadzone przez opiekuna pakietu i my nie musimy sobie tym głowy zawracać. Warto jednak prześledzić jak wygląda cały setup poinstalacyjny. Poniżej znajduje się listing plików konfiguracyjnych wraz z ich uprawnieniami:

root@radius:~# tree -u -g -p /etc/freeradius/
/etc/freeradius/
├── [-rw-r----- root     freerad ]  acct_users
├── [-rw-r----- root     freerad ]  attrs
├── [-rw-r----- root     freerad ]  attrs.access_challenge
├── [-rw-r----- root     freerad ]  attrs.access_reject
├── [-rw-r----- root     freerad ]  attrs.accounting_response
├── [-rw-r----- root     freerad ]  attrs.pre-proxy
├── [drwxr-s--x freerad  freerad ]  certs
│   ├── [lrwxrwxrwx root     freerad ]  ca.pem -> /etc/ssl/certs/ca-certificates.crt
│   ├── [-rw-r--r-- root     freerad ]  dh
│   ├── [lrwxrwxrwx root     freerad ]  server.key -> /etc/ssl/private/ssl-cert-snakeoil.key
│   └── [lrwxrwxrwx root     freerad ]  server.pem -> /etc/ssl/certs/ssl-cert-snakeoil.pem
├── [-rw-r----- root     freerad ]  clients.conf
├── [-rw-r--r-- root     freerad ]  dictionary
├── [-rw-r----- root     freerad ]  eap.conf
├── [-rw-r----- root     freerad ]  experimental.conf
├── [-rw-r----- root     freerad ]  hints
├── [-rw-r----- root     freerad ]  huntgroups
├── [-rw-r----- root     freerad ]  ldap.attrmap
├── [drwxr-xr-x root     root    ]  modules
│   ├── [-rw-r--r-- root     root    ]  acct_unique
│   ├── [-rw-r--r-- root     root    ]  always
│   ├── [-rw-r--r-- root     root    ]  attr_filter
│   ├── [-rw-r--r-- root     root    ]  attr_rewrite
│   ├── [-rw-r--r-- root     root    ]  chap
│   ├── [-rw-r--r-- root     root    ]  checkval
│   ├── [-rw-r--r-- root     root    ]  counter
│   ├── [-rw-r--r-- root     root    ]  cui
│   ├── [-rw-r--r-- root     root    ]  detail
│   ├── [-rw-r--r-- root     root    ]  detail.example.com
│   ├── [-rw-r--r-- root     root    ]  detail.log
│   ├── [-rw-r--r-- root     root    ]  digest
│   ├── [-rw-r--r-- root     root    ]  dynamic_clients
│   ├── [-rw-r--r-- root     root    ]  echo
│   ├── [-rw-r--r-- root     root    ]  etc_group
│   ├── [-rw-r--r-- root     root    ]  exec
│   ├── [-rw-r--r-- root     root    ]  expiration
│   ├── [-rw-r--r-- root     root    ]  expr
│   ├── [-rw-r--r-- root     root    ]  files
│   ├── [-rw-r--r-- root     root    ]  inner-eap
│   ├── [-rw-r--r-- root     root    ]  ippool
│   ├── [-rw-r--r-- root     root    ]  krb5
│   ├── [-rw-r--r-- root     root    ]  ldap
│   ├── [-rw-r--r-- root     root    ]  linelog
│   ├── [-rw-r--r-- root     root    ]  logintime
│   ├── [-rw-r--r-- root     root    ]  mac2ip
│   ├── [-rw-r--r-- root     root    ]  mac2vlan
│   ├── [-rw-r--r-- root     root    ]  mschap
│   ├── [-rw-r--r-- root     root    ]  ntlm_auth
│   ├── [-rw-r--r-- root     root    ]  opendirectory
│   ├── [-rw-r--r-- root     root    ]  otp
│   ├── [-rw-r--r-- root     root    ]  pam
│   ├── [-rw-r--r-- root     root    ]  pap
│   ├── [-rw-r--r-- root     root    ]  passwd
│   ├── [-rw-r--r-- root     root    ]  perl
│   ├── [-rw-r--r-- root     root    ]  policy
│   ├── [-rw-r--r-- root     root    ]  preprocess
│   ├── [-rw-r--r-- root     root    ]  radutmp
│   ├── [-rw-r--r-- root     root    ]  realm
│   ├── [-rw-r--r-- root     root    ]  redis
│   ├── [-rw-r--r-- root     root    ]  rediswho
│   ├── [-rw-r--r-- root     root    ]  replicate
│   ├── [-rw-r--r-- root     root    ]  smbpasswd
│   ├── [-rw-r--r-- root     root    ]  smsotp
│   ├── [-rw-r--r-- root     root    ]  soh
│   ├── [-rw-r--r-- root     root    ]  sql_log
│   ├── [-rw-r--r-- root     root    ]  sqlcounter_expire_on_login
│   ├── [-rw-r--r-- root     root    ]  sradutmp
│   ├── [-rw-r--r-- root     root    ]  unix
│   └── [-rw-r--r-- root     root    ]  wimax
├── [-rw-r----- root     freerad ]  policy.conf
├── [-rw-r----- root     freerad ]  policy.txt
├── [-rw-r----- root     freerad ]  preproxy_users
├── [-rw-r----- root     freerad ]  proxy.conf
├── [-rw-r----- root     freerad ]  radiusd.conf
├── [drwxr-s--x freerad  freerad ]  sites-available
│   ├── [-rw-r--r-- root     root    ]  README
│   ├── [-rw-r--r-- root     root    ]  buffered-sql
│   ├── [-rw-r--r-- root     root    ]  coa
│   ├── [-rw-r--r-- root     root    ]  control-socket
│   ├── [-rw-r--r-- root     root    ]  copy-acct-to-home-server
│   ├── [-rw-r--r-- root     root    ]  decoupled-accounting
│   ├── [-rw-r--r-- root     root    ]  default
│   ├── [-rw-r--r-- root     root    ]  dhcp
│   ├── [-rw-r--r-- root     root    ]  dynamic-clients
│   ├── [-rw-r--r-- root     root    ]  example
│   ├── [-rw-r--r-- root     root    ]  inner-tunnel
│   ├── [-rw-r--r-- root     root    ]  originate-coa
│   ├── [-rw-r--r-- root     root    ]  proxy-inner-tunnel
│   ├── [-rw-r--r-- root     root    ]  robust-proxy-accounting
│   ├── [-rw-r--r-- root     root    ]  soh
│   ├── [-rw-r--r-- root     root    ]  status
│   ├── [-rw-r--r-- root     root    ]  virtual.example.com
│   └── [-rw-r--r-- root     root    ]  vmps
├── [drwxr-s--x freerad  freerad ]  sites-enabled
│   ├── [lrwxrwxrwx root     freerad ]  default -> ../sites-available/default
│   └── [lrwxrwxrwx root     freerad ]  inner-tunnel -> ../sites-available/inner-tunnel
├── [-rw-r----- root     freerad ]  sql.conf
├── [-rw-r--r-- root     root    ]  sqlippool.conf
├── [-rw-r--r-- root     root    ]  templates.conf
└── [-rw-r--r-- root     root    ]  users

4 directories, 96 files

Niżej zaś znajduje się konfiguracja użytkownika freerad :

root@radius:~# cat /etc/group | grep freerad
shadow:x:42:freerad
freerad:x:109:
ssl-cert:x:110:freerad

root@radius:~# cat /etc/passwd | grep freerad
freerad:x:105:109::/etc/freeradius:/bin/false

root@radius:~# cat /etc/shadow | grep freerad
freerad:*:16321:0:99999:7:::

Jeśli kogoś interesuje opis konkretnych pól w każdym z powyższych plików, można go znaleźć w dokumentacji tych plików.

Trzeba zaznaczyć, że użytkownik freerad jest członkiem grupy shadow i to umożliwia mu odczyt zahashowanych haseł użytkowników systemowych, co jest przydatne np. przy korzystaniu z modułu unix.

Freeradius w katalogu /etc/freeradius/certs/ tworzy linki do istniejących już w systemie certyfikatów. Są to testowe certyfikaty dla usług, które ich wymagają do poprawnego działania i pochodzą one z pakietu ssl-cert . Świetnie się one nadają do celów testowych i dobrze jest sprawdzić czy instalacja freeradiusa z tymi certyfikatami działa jak należy. Te certyfikaty nie są zalecane na serwerach produkcyjnych, dlatego też w późniejszym etapie konfiguracji wygenerujemy sobie własne certyfikaty.

Podstawowa konfiguracja

W pliku /etc/freeradius/radiusd.conf musimy dostosować odpowiednie parametry dotyczące interfejsu, adresu, portów, użytkownika i grupy na jakich będzie operował freeradius:

...
user = freerad
group = freerad
...
listen {
        type = auth
        port = 0
        ipaddr = 192.168.1.166
        interface = eth0
...
listen {
        type = acct
        port = 0
        ipaddr = 192.168.1.166
        interface = eth0
...

Port 0 w tym wypadku oznacza port domyślny, a jego wartość jest czytana z pliku /etc/services -- są to 1812 (auth) i 1813 (acct).

Zapisujemy zmiany i testujemy wstępnie serwer odpalając go w trybie debug via freeradius -XXX . Wygeneruje to dość spory log, na którego końcu powinniśmy ujrzeć:

Wed Sep 10 11:47:51 2014 : Debug: Listening on authentication interface eth0 address 192.168.1.166 port 1812
Wed Sep 10 11:47:51 2014 : Debug: Listening on accounting interface eth0 address 192.168.1.166 port 1813
Wed Sep 10 11:47:51 2014 : Debug: Listening on authentication address 127.0.0.1 port 18120 as server inner-tunnel
Wed Sep 10 11:47:51 2014 : Debug: Listening on proxy address 192.168.1.166 port 1814
Wed Sep 10 11:47:51 2014 : Info: Ready to process requests.

Firewall

Poniższy krok nie jest konieczny, w końcu jakby nie patrzeć w swojej sieci lokalnej raczej wszystkie maszyny mamy zaufane i nie trzeba dodatkowo dozbrajać serwera RADIUS pod kątem restrykcji dostępu. My jednak zaprojektujemy prosty filter, w którym to musimy przepuścić ruch na porcie 22/tcp (domyślny ssh) z konkretnego adresu IP, ruch na portach 1812/udp, 1813/udp, 1814/udp (domyślne porty RADIUSa) oraz zapytania do serwera bazy danych mysql, który operuje standardowo na porcie 3306/tcp . Jeśli chodzi o samego RADIUSa jeszcze, to portem 1812 lecą zapytania uwierzytelniające, portem 1813 pakiety dotyczące wykorzystania zasobów sieci, a port 1814 odpowiada za forwardowanie zapytań (proxy). Domyślnie freeradius nasłuchuje na wszystkich tych portach i jeśli nie potrzebujemy np. informacji na temat czasu trwania sesji danego użytkownika, czy bajtów ile przesłał, lub zwyczajnie nie korzystamy z proxy, to możemy zahashować w pliku /etc/freeradius/radiusd.conf odpowiednią sekcję.

Tworzymy zatem prosty plik firewalla w /etc/init.d/firewall i wklejamy do niego poniższą zawartość:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    mountkernfs $local_fs
# Required-Stop:     $local_fs
# Should-Start:
# Should-Stop:
# Default-Start:     S
# Default-Stop:      0 6
# X-Start-Before:
# X-Stop-After:
# Short-Description: firewall configuration
# Description:       firewall configuration
### END INIT INFO

do_start ()
{
        iptables -t filter -P INPUT DROP
        iptables -t filter -P FORWARD DROP
        iptables -t filter -P OUTPUT ACCEPT
        iptables -t filter -N tcp
        iptables -t filter -N udp
        iptables -t filter -N icmp_in

        iptables -t filter -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
        iptables -t filter -A INPUT -i lo -j ACCEPT
        iptables -t filter -A INPUT -m conntrack --ctstate INVALID -j DROP
        iptables -t filter -A INPUT -p icmp -m conntrack --ctstate NEW -j icmp_in
        iptables -t filter -A INPUT -p udp -m conntrack --ctstate NEW -j udp
        iptables -t filter -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j tcp
        iptables -t filter -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
        iptables -t filter -A INPUT -p tcp -j REJECT --reject-with tcp-reset

        iptables -t filter -A icmp_in -p icmp -i eth0 -s 192.168.1.0/24 -d 192.168.1.166 -j ACCEPT

        iptables -t filter -A udp -p udp --dport 1812 -j ACCEPT -m comment --comment "RADIUS AUTH"
        iptables -t filter -A udp -p udp --dport 1813 -j ACCEPT -m comment --comment "RADIUS ACCT"
        iptables -t filter -A udp -p udp --dport 1814 -j ACCEPT -m comment --comment "RADIUS PROXY"
        iptables -t filter -A udp -p udp --dport 68 -j ACCEPT -m comment --comment "Allow-DHCP-Renew"

        iptables -t filter -A tcp -p tcp --dport 3306 -j ACCEPT -m comment --comment "MySQL"
        iptables -t filter -A tcp -p tcp --dport 22 -s 192.168.1.150 -j ACCEPT -m comment --comment "SSH"
}

do_stop ()
{
        iptables -t filter -P INPUT ACCEPT
        iptables -t filter -P FORWARD ACCEPT
        iptables -t filter -P OUTPUT ACCEPT
        iptables -t filter -F
        iptables -t filter -X
}

case "$1" in
        start)
                do_start
        ;;
        stop)
                do_stop
        ;;
        force-reload|restart)
                do_stop && sleep 1
                do_start
        ;;
        *)
                echo "Usage: firewall {start|stop|restart}"
                exit 1
        ;;
esac

exit 0

Dodajemy ten skrypt do autostartu systemu i odpalamy go:

root@radius:~# update-rc.d firewall defaults
root@radius:~# /etc/init.d/firewall start

Sprawdzamy jeszcze czy reguły zostały poprawnie utworzone:

root@radius:~# iptables -nvL
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
   29  2012 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID
    0     0 icmp_in    icmp --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW
    0     0 udp        udp  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW
    0     0 tcp        tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:0x17/0x02 ctstate NEW
    0     0 REJECT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with tcp-reset

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 15 packets, 1336 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain icmp_in (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     icmp --  eth0   *       192.168.1.0/24       192.168.1.166

Chain tcp (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:3306 /* MySQL */
    0     0 ACCEPT     tcp  --  *      *       192.168.1.150        0.0.0.0/0            tcp dpt:22 /* SSH */

Chain udp (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:1812 /* RADIUS AUTH */
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:1813 /* RADIUS ACCT */
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:1814 /* RADIUS PROXY */
    0     0 ACCEPT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:68 /* Allow-DHCP-Renew *

Jeśli widzimy log podobny do tego powyżej, znaczy, że filtr działa poprawnie.

CA i inne certyfikaty

Certyfikaty można tworzyć samemu, albo skorzystać z przygotowanego do tego celu narzędzi zawartych w pakiecie freeradius. Ja generowałem certyfikaty ręcznie ale równie dobrze można skorzystać z instrukcji zawartej w pliku /usr/share/doc/freeradius/examples/certs/README . W każdym razie poniżej są opisane oba sposoby tworzenia certyfikatów, z tym, że w późniejszym etapie konfiguracji sieci, będę operował na plikach stworzonych ręcznie.

Po tym jak zainstalowaliśmy pakiet freeradius, kilka plików zostało utworzonych w katalogu /usr/share/doc/freeradius/examples/certs , są to:

root@radius:/usr/share/doc/freeradius/examples/certs# ls -al
total 60
drwxr-xr-x 2 root root  4096 Sep  9 09:45 .
drwxr-xr-x 4 root root  4096 Sep  8 18:28 ..
-rw-r--r-- 1 root root  4274 Aug 15 22:24 Makefile
-rw-r--r-- 1 root root  7821 Aug 15 22:24 README
-rwxr-xr-x 1 root root  2693 Aug 15 22:24 bootstrap
-rw-r--r-- 1 root root  1288 Aug 15 22:24 ca.cnf
-rw-r--r-- 1 root root  1109 Aug 15 22:24 client.cnf
-rw-r--r-- 1 root root  1123 Aug 15 22:24 server.cnf
-rw-r--r-- 1 root root   578 Aug 15 22:24 xpextensions

W zależności od konfiguracji freeradiusa przy jego kompilacji, podczas uruchomienia serwera jako root w trybie debug (freeradius -X), plik bootstrap może zostać wykonywany i zostaną stworzone w ten sposób certyfikaty dla określonych maszyn. W przypadku debiana, to jednak nie ma miejsca ale wszystkie potrzebne nam pliki są dostarczone i możemy skrypt wywołać ręcznie z takim samym efektem.

Po tym jak wygenerujemy własne certyfikaty (metoda bez znaczenia), musimy wyedytować plik /etc/freeradius/eap.conf i wykomentować make_cert_command z podsekcji tls , tak by wyglądało to jak poniżej:

...
    tls {
    ...
#   make_cert_command = "${certdir}/bootstrap"
...   

Generowanie certyfikatów z wykorzystaniem bootstrap

Przechodzimy do katalogu /usr/share/doc/freeradius/examples/certs/ , gdzie będą wykonywane kolejne polecenia. Następnie edytujemy kolejno pliki ca.cnf , client.cnf i server.cnf -- są to kopie pliku konfiguracyjnego OpenSSL, który zwykle jest dostępny pod /etc/ssl/openssl.cnf . Każdy z tych plików został zmieniony tak by maksymalnie uprościć procedurę generowania certyfikatów i w sumie nasza ingerencja sprowadza się do edycji linijek zawierających input_password oraz output_password . Można oczywiście dostosować szereg innych parametrów, np. termin ważności certyfikatu, czy długość klucza RSA -- dobrze jest przejrzeć te pliki.

Wszystkie certyfikaty generowane za pomocą skryptu dostarczonego z pakietem freeradius będą domyślnie używać MD5 jako funkcji skrótu (hash). Dlatego warto zaznaczyć w tym miejscu by zmienić wartość parametru default_md w każdym z trzech powyższych plików z md5 na sha1 lub nawet sha256.

Poniżej prezentują się moje pliki .cnf .

Plik ca.cnf :

[ ca ]
default_ca              = CA_default

[ CA_default ]
dir                     = ./
certs                   = $dir
crl_dir                 = $dir/crl
database                = $dir/index.txt
new_certs_dir           = $dir
certificate             = $dir/ca.pem
serial                  = $dir/serial
crl                     = $dir/crl.pem
private_key             = $dir/ca.key
RANDFILE                = $dir/.rand
name_opt                = ca_default
cert_opt                = ca_default
default_days            = 3650
default_crl_days        = 30
default_md              = sha1
preserve                = no
policy                  = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
prompt                  = no
distinguished_name      = certificate_authority
default_bits            = 4096
input_password          = jakies-haslo-ca
output_password         = jakies-haslo-ca
x509_extensions         = v3_ca

[certificate_authority]
countryName             = US
stateOrProvinceName     = California
localityName            = Los Angeles
organizationName        = NSA
emailAddress            = ca@nsa.com
commonName              = "CA"

[v3_ca]
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer:always
basicConstraints        = CA:true

Plik server.cnf :

[ ca ]
default_ca              = CA_default

[ CA_default ]
dir                     = ./
certs                   = $dir
crl_dir                 = $dir/crl
database                = $dir/index.txt
new_certs_dir           = $dir
certificate             = $dir/server.pem
serial                  = $dir/serial
crl                     = $dir/crl.pem
private_key             = $dir/server.key
RANDFILE                = $dir/.rand
name_opt                = ca_default
cert_opt                = ca_default
default_days            = 3650
default_crl_days        = 30
default_md              = sha1
preserve                = no
policy                  = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
prompt                  = no
distinguished_name      = server
default_bits            = 4096
input_password          = jakies-haslo-server
output_password         = jakies-haslo-server

[server]
countryName             = US
stateOrProvinceName     = California
localityName            = Los Angeles
organizationName        = NSA
emailAddress            = nsa@ca.com
commonName              = "server"

Plik client.cnf :

[ ca ]
default_ca              = CA_default

[ CA_default ]
dir                     = ./
certs                   = $dir
crl_dir                 = $dir/crl
database                = $dir/index.txt
new_certs_dir           = $dir
certificate             = $dir/server.pem
serial                  = $dir/serial
crl                     = $dir/crl.pem
private_key             = $dir/server.key
RANDFILE                = $dir/.rand
name_opt                = ca_default
cert_opt                = ca_default
default_days            = 3650
default_crl_days        = 30
default_md              = sha1
preserve                = no
policy                  = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
prompt                  = no
distinguished_name      = client
default_bits            = 4096
input_password          = jakies-haslo-client
output_password         = jakies-haslo-client

[client]
countryName             = US
stateOrProvinceName     = California
localityName            = Los Angeles
organizationName        = NSA
emailAddress            = morfik@nsa.com
commonName              = morfik_laptop

W przypadku klientów, ważne jest by odpowiednio dobrać parametr commonName , bo to on zostanie później użyty przy uwierzytelnianiu podczas podłączania się klienta do sieci wifi.

Odpalamy skrypt:

root@radius:/usr/share/doc/freeradius/examples/certs# ./bootstrap
openssl dhparam -out dh 1024
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
......+......................................................................................+.
................................................................................................
...................+.............................................................................
.........................+.........................................+............................
..+............++*++*++*
openssl req -new  -out server.csr -keyout server.key -config ./server.cnf
Generating a 4096 bit RSA private key
.................................++
.........................................................................................................++
writing new private key to 'server.key'
-----
openssl req -new -x509 -keyout ca.key -out ca.pem \
        -days `grep default_days ca.cnf | sed 's/.*=//;s/^ *//'` -config ./ca.cnf
Generating a 4096 bit RSA private key
.................................++
.........................++
writing new private key to 'ca.key'
-----
openssl ca -batch -keyfile ca.key -cert ca.pem -in server.csr  -key `grep output_password ca.cnf | sed 's/.*=//;s/^ *//'` -out server.crt -extensions xpserver_ext -extfile xpextensions -config ./server.cnf
Using configuration from ./server.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Sep 10 19:13:33 2014 GMT
            Not After : Sep  7 19:13:33 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            organizationName          = NSA
            commonName                = server
            emailAddress              = nsa@ca.com
        X509v3 extensions:
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
Certificate is to be certified until Sep  7 19:13:33 2024 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12  -passin pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'`
openssl pkcs12 -in server.p12 -out server.pem -passin pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password server.cnf | sed 's/.*=//;s/^ *//'`
MAC verified OK
openssl verify -CAfile ca.pem server.pem
server.pem: OK
openssl x509 -inform PEM -outform DER -in ca.pem -out ca.der

Zostały utworzone certyfikaty dla CA i serwera. Dodatkowo musimy stworzyć certyfikaty dla klientów:

root@radius:/usr/share/doc/freeradius/examples/certs# make client
openssl req -new  -out client.csr -keyout client.key -config ./client.cnf
Generating a 4096 bit RSA private key
................................................................................................................++
....................................++
writing new private key to 'client.key'
-----
openssl ca -batch -keyfile ca.key -cert ca.pem -in client.csr  -key `grep output_password ca.cnf | sed 's/.*=//;s/^ *//'` -out client.crt -extensions xpclient_ext -extfile xpextensions -config ./client.cnf
Using configuration from ./client.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Sep 10 19:17:53 2014 GMT
            Not After : Sep  7 19:17:53 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            organizationName          = NSA
            commonName                = morfik_laptop
            emailAddress              = morfik@nsa.com
        X509v3 extensions:
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
Certificate is to be certified until Sep  7 19:17:53 2024 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12  -passin pass:`grep output_password client.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password client.cnf | sed 's/.*=//;s/^ *//'`
openssl pkcs12 -in client.p12 -out client.pem -passin pass:`grep output_password client.cnf | sed 's/.*=//;s/^ *//'` -passout pass:`grep output_password client.cnf | sed 's/.*=//;s/^ *//'`
MAC verified OK
cp client.pem `grep emailAddress client.cnf | grep '@' | sed 's/.*=//;s/^ *//'`.pem

Teraz powinniśmy mieć już komplet plików. Listując katalog roboczy możemy dostrzec, że pliki mają różne rozszerzenia. Czym zatem różnią się te pliki między sobą?

Plik .crt to certyfikat główny. W osobnym pliku .key znajduje się klucz prywatny od tego certyfikatu -- można rozpoznać po "-----BEGIN ENCRYPTED PRIVATE KEY-----" i "-----END ENCRYPTED PRIVATE KEY-----". W przypadku gdy nie potrzebujemy oddzielać certyfikatu od klucza, możemy posłużyć się plikiem .p12 lub plikiem .pem -- mogą one zawierać zarówno certyfikat jak i klucz prywatny i zamiast dwóch osobnych plików jest tylko jeden. Różnica między nimi polega na tym, że plik .p12 jest używany głównie na windowsach, ten drugi zaś przez OpenSSL. Plik .csr jest prośbą o podpisanie certyfikatu (certificate request) wysyłaną przez aplikację do CA (Certification Authority) -- można rozpoznać po linijkach "-----BEGIN NEW CERTIFICATE REQUEST-----" oraz "-----END NEW CERTIFICATE REQUEST-----". Dodatkowo, mamy plik binarny certyfikatu .der zakodowany w DER. Nie ma jednak możliwości umieszczenia w tym pliku klucza prywatnego, czy też certyfikatów ze struktury certyfikatów (certification path). Wygenerowany on został głównie na potrzeby windowsów.

Freeradius do konfiguracji potrzebuje server.key , server.pem oraz ca.pem . Pliki trzeba przenieść do katalogu /etc/freeradius/certs/ . Z kolei na kliencie potrzebne nam będą ca.pem jako certyfikat CA i client.p12, albo client.key i client.crt.

Ręczne generowanie certyfikatów

Poniżej również przeprowadzimy generowanie certyfikatów, z tą różnicą, że stworzymy je sami, a do tego potrzebna nam jest odpowiednia struktura katalogów:

root@radius:~# mkdir /etc/CA
root@radius:~# mkdir /etc/CA/signed_certs
root@radius:~# mkdir /etc/CA/private
root@radius:~# chmod 700 /etc/CA/private
root@radius:~# cp /etc/ssl/openssl.cnf /etc/CA
root@radius:~# touch /etc/CA/index.txt

W katalogu signed_certs będą przechowywane kopie wszystkich certyfikatów, które podpiszemy naszym CA -- w takim przypadku gdy zajdzie potrzeba cofnięcia jakiegoś certyfikatu, będziemy mieć jego kopię lokalnie. Z kolei w katalogu private będzie trzymany klucz prywatny naszego CA i bardzo ważną rzeczą jest by trzymać ten klucz w sekrecie, bo jeśli zostanie skompromitowany, jest niemal pewne, że zostanie użyty do podpisania niezaufanych certyfikatów, a to doprowadzi do sytuacji gdzie użytkownik prześle poufne dane do podejrzanych maszyn. Plik index.txt jest to tekstowa baza danych wykorzystywana do śledzenia podpisanych certyfikatów. Został również skopiowany plik openssl.cnf , w którym to zostanie skonfigurowany szereg parametrów pod certyfikaty.

Przechodzimy do edycji pliku /etc/CA/openssl.cnf i zmieniamy w nim poniższe linijki:

...
[ CA_default ]

dir             = /etc/CA               # Where everything is kept
certs           = $dir                  # Where the issued certs are kept
...
new_certs_dir   = $dir/signed_certs     # default place for new certs.
...
default_days    = 3650                  # how long to certify for
...
default_md      = sha256                # use public key default MD
...
[ req ]
default_bits    = 4096
...
[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = US
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = California

localityName                    = Locality Name (eg, city)
localityName_default            = Los Angeles

0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = NSA

# we can do this but it is not needed normally :-)
#1.organizationName             = Second Organization Name (eg, company)
#1.organizationName_default     = World Wide Web Pty Ltd

organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  =

commonName                      = Common Name (e.g. server FQDN or YOUR name)
commonName_max                  = 64

emailAddress                    = Email Address
emailAddress_default            = morfik@nsa.com
emailAddress_max                = 64

Dodatkowo na końcu pliku dodajemy poniższe wpisy:

[ xpclient_ext]
extendedKeyUsage = 1.3.6.1.5.5.7.3.2

[ xpserver_ext]
extendedKeyUsage = 1.3.6.1.5.5.7.3.1

Na dobrą sprawę nie testowałem tego i nie mam pojęcia czy te rozszerzenia odnoszą się tylko do windowsów xp czy w ogóle do wszystkich windowsów, w każdym razie nawet w certyfikatach generowanych przez bootstrap freeradiusa, te rozszerzenia są uwzględnione i prawdopodobnie bez nich windosowskie klienty odmówią współpracy z naszym serwerem RADIUSa.

Mając przygotowane środowisko, możemy przejść do tworzenia CA. Wchodzimy zatem do katalogu /etc/CA i tworzymy certyfikat dla naszego CA:

root@radius:~# cd /etc/CA
root@radius:/etc/CA# openssl req -new -keyout private/cakey.pem -out careq.pem -config ./openssl.cnf
Generating a 4096 bit RSA private key
......................................++
.................................................++
writing new private key to 'private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [California]:
Locality Name (eg, city) [Los Angeles]:
Organization Name (eg, company) [NSA]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:CA
Email Address [morfik@nsa.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Musimy go jeszcze sami sobie podpisać by stał się CA:

root@radius:/etc/CA# openssl ca -create_serial -out cacert.pem -keyfile private/cakey.pem -selfsign -extensions v3_ca -config ./openssl.cnf -in careq.pem
Using configuration from ./openssl.cnf
Enter pass phrase for private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 16319793529210310063 (0xe27b8dbfb44c8daf)
        Validity
            Not Before: Sep  9 10:44:36 2014 GMT
            Not After : Sep  6 10:44:36 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            organizationName          = NSA
            commonName                = CA
            emailAddress              = morfik@nsa.com
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                B0:62:C3:AD:97:E5:EF:D1:D2:77:AE:74:81:28:11:28:60:51:5E:B8
            X509v3 Authority Key Identifier:
                keyid:B0:62:C3:AD:97:E5:EF:D1:D2:77:AE:74:81:28:11:28:60:51:5E:B8

            X509v3 Basic Constraints:
                CA:TRUE
Certificate is to be certified until Sep  6 10:44:36 2024 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

W powyższym poleceniu użyliśmy parametru -create_serial , który tworzy hexalny numer seryjny dla tego klucza. Z kolei -extensions określa sekcję w pliku openssl.cnf , której należy szukać w celu odnalezienia określonych rozszerzeń, które zostaną dodane do nowo stworzonego certyfikatu (klucza publicznego). W tym przypadku użyliśmy sekcji v3_ca , która zawiera między innymi basicConstraints = CA:true , co pozwala na podpisywanie innych kluczy, czyli by ten klucz zachowywał się jak CA.

Ostatnim krokiem jest stworzenie kopi certyfikatu CA zakodowanej w formacie DER, no bo windowsy lubią tylko tego typu binarne certyfikaty:

root@radius:/etc/CA# openssl x509 -inform PEM -outform DER -in cacert.pem -out cacert.der

No to tworzenie certyfikatu CA mamy z głowy. Trzeba nam jeszcze wygenerować parę kluczy dla serwerów i klientów. W przypadku gdy nie zamierzamy korzystać z metody uwierzytelniania EAP-TLS, możemy odpuścić sobie generowanie certyfikatów dla klientów. My jednak mamy zamiar zaimplementować sobie taki ficzer. Zatem do dzieła, tworzymy klucze na potrzeby serwera freeradius:

root@radius:/etc/CA# openssl req -new -config ./openssl.cnf -keyout server_key.pem -out server_req.pem
Generating a 4096 bit RSA private key
..............................................................................................
.................................................................................++
.............................................................................................
.............................................................................................
..................................................++
writing new private key to 'server_key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [California]:
Locality Name (eg, city) [Los Angeles]:
Organization Name (eg, company) [NSA]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:server
Email Address [morfik@nsa.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Podpisujemy ten certyfikat naszym CA:

root@radius:/etc/CA# openssl ca -config ./openssl.cnf -in server_req.pem -out server_cert.pem
Using configuration from ./openssl.cnf
Enter pass phrase for /etc/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 16319793529210310064 (0xe27b8dbfb44c8db0)
        Validity
            Not Before: Sep  9 10:49:41 2014 GMT
            Not After : Sep  6 10:49:41 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            organizationName          = NSA
            commonName                = server
            emailAddress              = morfik@nsa.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                4B:58:8E:3F:C4:16:A0:09:42:48:7E:49:B8:CA:C0:2D:26:E1:86:FB
            X509v3 Authority Key Identifier:
                keyid:B0:62:C3:AD:97:E5:EF:D1:D2:77:AE:74:81:28:11:28:60:51:5E:B8

Certificate is to be certified until Sep  6 10:49:41 2024 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Jeśli byśmy mieli w zamiarze wykorzystywać windowsa do zarządzania połączeniami wifi, to trzeba użyć rozszerzeń X509v3 i wtedy powyższa linijka będzie miała poniższą postać:

openssl ca -config ./openssl.cnf -extensions xpserver_ext -in server_req.pem -out server_cert.pem

Klucze dla klientów tworzy się w taki sam sposób jak dla serwera wyżej:

root@radius:/etc/CA# openssl req -new -config ./openssl.cnf -keyout morfik_laptop_key.pem -out morfik_laptop_req.pem
Generating a 4096 bit RSA private key
....................++
....................++
writing new private key to 'morfik_laptop_key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [California]:
Locality Name (eg, city) [Los Angeles]:
Organization Name (eg, company) [NSA]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:morfik_laptop
Email Address [morfik@nsa.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Ważne jest by ustawić tutaj odpowiednią nazwę Common Name , bo to ona będzie brana pod uwagę przy uwierzytelnianiu klientów do sieci wifi. Teraz jeszcze zostało nam podpisanie certyfikatu klienta:

root@radius:/etc/CA# openssl ca -config ./openssl.cnf -in morfik_laptop_req.pem -out morfik_laptop_cert.pem
Using configuration from ./openssl.cnf
Enter pass phrase for /etc/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 16319793529210310065 (0xe27b8dbfb44c8db1)
        Validity
            Not Before: Sep  9 10:52:11 2014 GMT
            Not After : Sep  6 10:52:11 2024 GMT
        Subject:
            countryName               = US
            stateOrProvinceName       = California
            organizationName          = NSA
            commonName                = morfik_laptop
            emailAddress              = morfik@nsa.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                FD:57:A3:BB:85:31:13:69:96:0D:73:20:8F:47:34:98:66:43:38:0D
            X509v3 Authority Key Identifier:
                keyid:B0:62:C3:AD:97:E5:EF:D1:D2:77:AE:74:81:28:11:28:60:51:5E:B8

Certificate is to be certified until Sep  6 10:52:11 2024 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Jeśli mamy zamiar używać certyfikatów na windowsie, w powyższym poleceniu musimy uwzględnić rozszerzenia X509v3 i zmienić powyższą linijkę by wyglądała tak jak tak poniżej:

openssl ca -config ./openssl.cnf -extensions xpclient_ext -in morfik_laptop_req.pem -out morfik_laptop_cert.pem

Jeśli chodzi jeszcze o windowsy to musimy przekuć klucz prywatny i certyfikat klienta w plik PKCS#12 :

root@radius:/etc/CA# openssl pkcs12 -export -clcerts -in morfik_laptop_cert.pem -inkey morfik_laptop_key.pem -out morfik_laptop.p12
Enter pass phrase for morfik_laptop_key.pem:
Enter Export Password:
Verifying - Enter Export Password:

Powyższe polecenie wykorzystuje narzędzie pkcs12 oferowane przez OpenSSL do wyeksportowania (-export) nowego pliku PKCS#12. Z kolei -clcerts mówi OpenSSL aby wyeksportował jedynie certyfikat i jego klucz prywatny, bo w innych konfiguracjach można upchnąć wiele certyfikatów i kluczy w jednym pliku PKCS#12. Tego typu certyfikat również jest czytany na maszynach linuxowych.

Porównanie plików

Podczas pisania tego tekstu nie miałem pojęcia o tym, że te certyfikaty można wygenerować specjalnie zaprojektowanym do tego celu skryptem dostarczanym razem z pakietem freeradius. Dopiero pod koniec natrafiłem na tę dokumentację i dlatego nazwy plików się nieco różnią -- nie chciało mi się już od nowa generować i przepisywać wszystkiego. W każdym razie jeśli ktoś ma problemy z wywnioskowaniem, które pliki należy gdzie skopiować, to poniżej znajduje się ich porównanie:

metoda ręczna          |          skrypt freeradiusa
-----------------------------------------------------
cacert.pem                          ca.pem
server_cert.pem                     server.pem
server_key.pem                      server.key
morfik_laptop.p12                   client.p12
morfik_laptop_cert.pem              client.crt
morfik_laptop_key.pem               client.key

Performance

W tym przypadku wykorzystaliśmy klucze o rozmiarze 4096 bitów. Warto zrobić sobie test i zobaczyć jak radzi sobie nasza maszyna w kalkulacjach RSA. Taki benchmark można przeprowadzić wydając poniższe polecenie:

root@radius:~# openssl speed rsa
...
                  sign    verify    sign/s verify/s
rsa  512 bits 0.000196s 0.000016s   5099.1  61558.5
rsa 1024 bits 0.000837s 0.000049s   1194.1  20247.7
rsa 2048 bits 0.005616s 0.000173s    178.1   5772.5
rsa 4096 bits 0.041405s 0.000668s     24.2   1496.4

Z powyższego wyniku interesuje nas głównie kolumna sign/s -- jest to ilość jednoczesnych prób uwierzytelniania klientów wifi jakie serwer jest w stanie obsłużyć wykorzystując mechanizm EAP-TLS, EAP-TTLS albo PEAP.

Konfiguracja certyfikatów

Narobiliśmy trochę plików i teraz trzeba je rozdzielić pomiędzy określone maszyny. Na początek kopiujemy wszystko co dotyczy serwera RADIUS do katalogu /etc/freeradius/certs/ . Domyślnie jednak znajdują się tam certyfikaty stworzone po instalacji pakietu freeradius -- usuwamy i kopiujemy nasze certyfikaty:

root@radius:/etc/CA# rm /etc/freeradius/certs/*
root@radius:/etc/CA# cp cacert.pem server_cert.pem server_key.pem /etc/freeradius/certs/

Przechodzimy do katalogu z certyfikatami dla RADIUSa i generujemy przy pomocy OpenSSL plik dh -- zawiera on ogromną liczbę pierwszą (w tym przypadku składającą się z 2048 bitów), która jest używana przy negocjowaniu kluczy dla sesji TLS. Standardowo ta liczba ma 1024 bity, z tym, że certyfikaty mają domyślnie 2048 bitów, a jako, że my stworzyliśmy certyfikaty 4096 bitowe, to zwiększymy również dwukrotnie rozmiar (w bitach) dla tej liczby pierwszej:

root@radius:/etc/freeradius/certs# openssl dhparam -out dh 2048
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
....................................+.............................................
..............................+....................................................
........................................................+..........+................
.................................................+...............++*++*

By skonfigurować klienta, który będzie korzystał z wifi, musimy przekopiować cacert.pem oraz morfik_laptop.p12 (morfik_laptop_cert.pem i morfik_laptop_key.pem) na maszynę kliencką:

morfik:~$ su
Password:
root:~# mkdir -p /etc/wifi_cert/radius_192.168.1.166
root:~# scp root@192.168.1.166:/etc/CA/morfik* /etc/wifi_cert/radius_192.168.1.166/
root@192.168.1.166's password:
morfik_laptop.p12                                              100% 4229     4.1KB/s   00:00
morfik_laptop_cert.pem                                         100% 7234     7.1KB/s   00:00
morfik_laptop_key.pem                                          100% 3406     3.3KB/s   00:00
morfik_laptop_req.pem                                          100% 1716     1.7KB/s   00:00
root:~# scp root@192.168.1.166:/etc/CA/cacert.pem /etc/wifi_cert/radius_192.168.1.166/
cacert.pem                                              100% 7234     9.1KB/s   00:00

Na linuxach, do konfiguracji posłuży nam narzędzie wpasupplicant dostępne w pakiecie pod tą samą nazwą. Można oczywiście skorzystać z wicd albo network-managera jeśli ktoś nie przepada za ręczą edycją plików tekstowych.

W zależności od wybranej metody uwierzytelniania, nieco inaczej będzie wyglądać konfiguracja suplikanta (klienta wifi). U mnie konfiguracje wifi są trzymane w pliku /etc/wpa_supplicant/wpa_supplicant.conf . Samą konfigurację wifi z wykorzystaniem wpasupplicant opisałem dokładnie pod tym linkiem i jeśli coś jest niezrozumiałego, to zachęcam do zajrzenia pod podany link.

EAP

Jest wiele metod uwierzytelniania EAP i nas interesować będą trzy z nich EAP-TLS, EAP-TTLS i PEAP. Metoda EAP-TLS, w odróżnieniu od EAP-TTLS i PEAP, wymaga wzajemnej weryfikacji certyfikatów na linii serwer-klient. Z kolei EAP-TTLS i PEAP zbytnio się nie różnią między sobą, no może za wyjątkiem wsparcia -- ten drugi jest rozwijany przez MS. Obie te metody wykorzystują szyfrowany tunel TLS, który jest również podstawą dla uwierzytelniania przy metodzie EAP-TLS i by korzystać z EAP-TTLS lub PEAP, trzeba po części skonfigurować uwierzytelnianie EAP-TLS. Same certyfikaty klienckie w przypadku EAP-TTLS i PEAP są nieobowiązkowe i uwierzytelnianie klienta odbywać się będzie przy pomocy loginu i hasła, co znacznie upraszcza wdrożenie protokołu WPA2 Enterprise.

Jeśli chodzi jeszcze o porównanie EAP-TTLS/PEAP w stosunku do EAP-TLS, to proces uwierzytelniania w przypadku tych pierwszych odbywa się w dwóch fazach. EAP-TLS ma tylko jedną fazę, podczas której wymieniane są klucze publiczne i zestawiany jest tunel TLS. Sam tunel tworzony jest na podobnej zasadzie co w przypadku SSL na stronach www. W EAP-TTLS/PEAP występują dwie tożsamości -- zewnętrzna i wewnętrzna (szyfrowana wewnątrz tunelu TLS). Zewnętrzna tożsamość w EAP-TTLS/PEAP odpowiada tożsamości w TLS i w obu przypadkach ta tożsamość widnieje w pakietach sieciowych. Dlatego jeśli zależy nam na anonimowości klientów, nie możemy korzystać z EAP-TLS. Niemniej jednak ta metoda jest najbezpieczniejsza ze wszystkich dostępnych na rynku.

W protokołach EAP-TTLS/PEAP, wewnątrz tunelu TLS, wykorzystywane są inne protokoły EAP, którymi przesyłane są login i hasło. Trzeba rozróżnić te dwie kwestie -- wykorzystanie np. EAP-MD5 przy uwierzytelnianiu jest poważnym ryzykiem, bo hasło w postaci hasha md5 jest przesyłane przez sieć, a ten protokół w obecnych czasach nie daje już gwarancji bezpieczeństwa. Natomiast jeśli wykorzystujemy tunel TLS, to typ zastosowanej metody wewnątrz tunelu nie ma już dla nas większego znaczenia. Nawet możemy puścić hasło otwartym tekstem i tunel TLS daje nam gwarancję poufności danych wewnątrz niego i dlatego nie trzeba tych danych dodatkowo szyfrować.

Jeśli chodzi o samą konfigurację klientów, to windowsy standardowo nie działają z metodą EAP-TTLS i muszą używać PEAP, co trzeba wziąć pod uwagę, przy konfiguracji serwera freeradius.

W związku z powyższym, ustawmy domyślny protokół EAP na PEAP, zakładając, że najwięcej klientów będzie właśnie go używać. Konfiguracja EAP odbywa się w pliku /etc/freeradius/eap.conf . Przechodzimy zatem do jego edycji:

...
default_eap_type = peap
...

Konfiguracja tunelu TLS

Jeśli już wszystkie pliki są wgrane tam gdzie powinny, możemy przejść do konfigurowania tunelu TLS pod uwierzytelnianie EAP-TLS, EAP-TTLS i PEAP. Dokonujemy tego w pliku /etc/freeradius/eap.conf w sekcji tls :

...
tls {
    certdir = ${confdir}/certs
    cadir = ${confdir}/certs
    
    private_key_password = jakies-haslo-server
    
#   private_key_file = ${certdir}/server.key
    private_key_file = ${certdir}/server_key.pem
    
#   certificate_file = ${certdir}/server.pem
    certificate_file = ${certdir}/server_cert.pem
    
#   CA_file = ${cadir}/ca.pem
    CA_file = ${cadir}/cacert.pem
    
    dh_file = ${certdir}/dh
    random_file = /dev/urandom
    CA_path = ${cadir}
...

By się zalogować do sieci wifi wykorzystując EAP-TLS, potrzebujemy odpowiedniej konfiguracji dla wpasupplicanta na kliencie:

network={
    id_str="home_wifi_static"
    priority=10
    ssid="Winter Is Coming"
    bssid=E8:94:F6:68:79:EF
    proto=RSN
    key_mgmt=WPA-EAP
    eap=TLS
    pairwise=CCMP
    group=CCMP
    auth_alg=OPEN
    identity="linux_laptop"
    ca_cert="/etc/wifi_cert/radius_192.168.1.166/cacert.pem"
    client_cert="/etc/wifi_cert/radius_192.168.1.166/morfik_laptop_cert.pem"
    private_key="/etc/wifi_cert/radius_192.168.1.166/morfik_laptop_key.pem"
#   private_key="/etc/wifi_cert/radius_192.168.1.166/morfik_laptop.p12"
    private_key_passwd="jakies-haslo-client"
    scan_ssid=0
    disabled=0
}

Interesują nas głównie linijki zawierające ca_cert , client_cert , private_key , private_key_passwd . Powyżej mamy wykomentowaną jedną linijkę -- jeśli chcemy wykorzystać plik .p12, który zawiera certyfikat i klucz prywatny, musimy wykomentować dwie linijki wyżej.

Konfiguracja EAP-TTLS

Mając tunel TLS, możemy przejść do konfiguracji EAP-TTLS -- jest trochę niżej, również w pliku /etc/freeradius/eap.conf:

...
ttls {
   default_eap_type = md5
   copy_request_to_tunnel = no
   use_tunneled_reply = no
   virtual_server = "inner-tunnel"
}
...

Powyższa konfiguracja jest domyślna i nie musimy zbytnio nic w niej zmieniać. By podłączyć się do sieci z wykorzystaniem protokołu EAP-TTLS, wrzucamy poniższą zwrotkę do pliku konfiguracyjnego wpasupplicanta:

network={
    id_str="home_wifi_static"
    priority=10
    ssid="Winter Is Coming"
    bssid=E8:94:F6:68:79:EF
    proto=RSN
    key_mgmt=WPA-EAP
    eap=TTLS
    pairwise=CCMP
    group=CCMP
    auth_alg=OPEN
    phase1="peaplabel=1"
    phase2="auth=MD5"
    anonymous_identity="anonymous"
    identity="morfik"
    password="super-tajne+haslo"
    ca_cert="/etc/wifi_cert/radius_192.168.1.166/cacert.pem"
    scan_ssid=0
    disabled=0
}

EAP-TTLS nie jest obsługiwany przez systemy operacyjne spod znaku MS -- te korzystają z rozwijanego przez MS protokołu PEAP. Jeśli się chce korzystać z EAP-TTLS, to albo trzeba zmienić system, albo klienta wifi, na taki który potrafi obsłużyć EAP-TTLS jako metodę uwierzytelniania.

Konfiguracja protokołu PEAP

Jeszcze niżej w pliku /etc/freeradius/eap.conf mamy konfigurację dla PEAP:

peap {
   default_eap_type = mschapv2
   copy_request_to_tunnel = no
   use_tunneled_reply = no
   virtual_server = "inner-tunnel"
}

By się podłączyć do sieci używając protokołu PEAP, tworzymy poniższe wpisy w pliku konfiguracyjnym wpasupplicanta:

network={
    id_str="home_wifi_static"
    priority=15
    ssid="Winter Is Coming"
    bssid=E8:94:F6:68:79:EF
    proto=RSN
    key_mgmt=WPA-EAP
    eap=PEAP
    pairwise=CCMP
    group=CCMP
    auth_alg=OPEN
    phase2="auth=MSCHAPV2"
    anonymous_identity="anonymous"
    identity="morfik"
    password="super-tajne+haslo"
    ca_cert="/etc/wifi_cert/radius_192.168.1.166/cacert.pem"
    scan_ssid=0
    disabled=0
}

Anonimowa tożsamość

W przypadku protokołów EAP-TTLS oraz PEAP mamy możliwość sprecyzowania w pliku konfiguracyjnym wpasupplicanta parametru anonymous_identity -- zwykle przyjmuje on wartość anonymous . Jest to nic innego jak zewnętrzna tożsamość używana do zestawiania tunelu TLS. W przypadku gdybyśmy nie podali w konfiguracji tego parametru, zostanie użyta realna tożsamość -- ta, która służy do logowania do sieci. Poniżej jest krótki wycinek z logu freeradiusa:

rad_recv: Access-Request packet from host 192.168.1.1 port 59500, id=220, length=283
        User-Name = "anonymous"
...
Sat Sep 20 12:09:25 2014 : Info: [ttls] Authenticate
Sat Sep 20 12:09:25 2014 : Info: [ttls] processing EAP-TLS
...
Sat Sep 20 12:09:25 2014 : Info: [ttls] Session established.  Proceeding to decode tunneled attributes.
...
Sat Sep 20 12:09:25 2014 : Info: [ttls] Got tunneled request
...
Sat Sep 20 12:09:25 2014 : Info: [ttls] Got tunneled identity of morfik
...

Powyższe ustawienia dla protokołów EAP-TTLS i PEAP dają gwarancję, że nie da się podsłuchać realnych tożsamości używanych przy logowaniu się do sieci wifi -- atakujący jedyne co zobaczy węsząc w sieci, to login anonymous, z którym nic nie da się zrobić.

Weryfikacja certyfikatu serwera

W przypadku protokołów EAP-TTLS oraz PEAP nie trzeba definiować parametru ca_cert w konfiguracji wpasupplicanta. W przypadku gdy tego nie zrobimy, nie zweryfikujemy tym samym certyfikatu serwera RADIUS. Jeśli mamy podaną ścieżkę do certyfikatu serwera w konfiguracji, to w przypadku niezgodności certyfikatu serwera, nie zostaniemy podłączeni do sieci i zostanie nam zwrócony poniższy komunikat (wpasupplicant):

SSL: SSL_connect:SSLv3 read server hello A
TLS: Certificate verification failed, error 19 (self signed certificate in certificate chain) depth 1 for '/C=US/ST=California/O=NSA/CN=CA/emailAddress=morfik@nsa.com'
wlan0: CTRL-EVENT-EAP-TLS-CERT-ERROR reason=1 depth=1 subject='/C=US/ST=California/O=NSA/CN=CA/emailAddress=morfik@nsa.com' err='self signed certificate in certificate chain'
SSL: (where=0x4008 ret=0x230)
SSL: SSL3 alert: write (local SSL3 detected an error):fatal:unknown CA
SSL: (where=0x1002 ret=0xffffffff)
SSL: SSL_connect:error in SSLv3 read server certificate B
OpenSSL: openssl_handshake - SSL_connect error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
SSL: 7 bytes pending from ssl_out
SSL: Failed - tls_out available to report error
SSL: 7 bytes left to be sent out (of total 7 bytes)

Krótko mówiąc -- nie udało się zweryfikować certyfikatu serwera. W przypadku nieprzeprowadzenia weryfikacji certyfikatu serwera (brak parametru ca_cert), zostaniemy podłączeni do sieci jak gdyby nigdy nic. Jeśli ktoś w takiej sytuacji spróbuje postawić AP/NAS, na którym będzie miał taką samą sieć jak nasza - ten sam BSSID i ESSID -- istnieje spore prawdopodobieństwo, że podłączymy się do infrastruktury tego kogoś i wszystkie dane logowania do naszego konta podamy mu jak na tacy.

Konfiguracja klientów serwera freeradius

W tym przypadku słowo klient nie jest równoznaczne ze słowem klient gdy mówimy o sieci wifi. Tamten klient jest obsługiwany przez AP, natomiast w przypadku serwera RADIUS, klientem dla niego jest nasz AP, w tym wypadku router. Musimy zdefiniować to urządzenie w konfiguracji freeradiusa w pliku /etc/freeradius/clients.conf . Domyślnie we wspomnianym pliku widnieje klient localhost i możliwe jest przetwarzanie jedynie zapytań lokalnych, np. w przypadku gdyby oprogramowanie freeradiusa było zainstalowane na urządzeniu AP/NAS. My musimy dodać do tego pliku nasz router:

...
client router_1043nd {
   ipaddr = 192.168.1.1
   netmask = 32
   secret = "tajna-fraza"
   require_message_authenticator = yes
   nastype = other
}
...

Jeśli w powyższym pliku byśmy nie zdefiniowali odpowiedniego urządzenia lub byśmy zrobili to błędnie, serwer RADIUSa zwróci nam poniższy komunikat:

Error: Ignoring request to authentication interface eth0 address 192.168.1.166 port 1812 from unknown client 192.168.1.1 port 41805

Na podstawie parametru secret, serwer RADIUSa decyduje czy ma obsłużyć tego klienta. W konfiguracji klienta (routera) trzeba będzie również uzupełnić ten parametr by wskazywał dokładnie tę samą wartość, w przeciwnym wypadku serwer RADIUSa zrzuci wszystkie zapytania otrzymane od tego konkretnego AP/NAS co uniemożliwi podłączenie się użytkownikom do sieci. W logu freeradius zaś będzie widoczny poniższy komunikat:

rad_recv: Access-Request packet from host 192.168.1.1 port 41805, id=10, length=177
Thu Sep 18 11:54:59 2014 : Info: Received packet from 192.168.1.1 with invalid Message-Authenticator!  (Shared secret is incorrect.) Dropping packet without response.

Konfiguracja routera (AP/NAS)

Logujemy się na router po ssh i edytujemy plik /etc/config/wireless , w którym to trzeba zdefiniować parametry serwera RADIUS:

...
config wifi-iface
   option device 'radio0'
   option network 'lan'
   option mode 'ap'
   option ssid 'Winter Is Coming'
   option encryption 'wpa2+aes'
   option key 'tajna-fraza'
   option server '192.168.1.166'
   option port '1812'
   option disabled '0'
   option hidden '0'
   option isolate '0'
...

Parametr key odpowiada za secret ustawiony w /etc/freeradius/clients.conf .

Musimy także wymienić nieco oprogramowania, bo domyślnie zainstalowany pakiet wpad-mini nie zawiera obsługi WPA2 Enterprise. Jeśli chcemy mieć obsługę WPA2 Enterprise, to musimy doinstalować pakiet wpad uprzednio usuwając pakiet wpad-mini:

root@router:~#  opkg remove wpad-mini
root@router:~#  opkg install wpad

Teraz już tylko resetujemy wifi na routerze:

root@router:~# wifi

Konfiguracja użytkowników

By użytkownik mógł się podłączyć do sieci, potrzebne są mu login i hasło, a te zaś, na serwerze RADIUS, mogą być przechowywane w różny sposób. Domyślnie freeradius trzyma konfigurację użytkowników w pliku /etc/freeradius/users . Innym rozwiązaniem jest zaprzęgnięcie do obsługi użytkowników bazy danych, definiując w niej min. parametry logowania.

W przypadku jednoczesnego włączenia szeregu modułów, może się zdarzyć sytuacja, w której dany użytkownik zostanie zdefiniowany, powiedzmy, dwa razy. W takiej sytuacji będzie decydować kolejność wywołania modułów w pliku konfiguracyjnym i ostatni moduł przetwarzający danego użytkownika będzie decydował czy przyznać mu dostęp.

Warto także pamiętać o tym, że każdy moduł posiada swój plik konfiguracyjny w katalogu /etc/freeradius/modules/ , za wyjątkiem kilku podstawowych modułów, które posiadają swoją konfigurację w odpowiednich plikach w katalogu /etc/freeradius/ .

Moduł files

W przypadku tego modułu, użytkowników definiuje się w pliku /etc/freeradius/users . Dodajmy na końcu tego pliku dwóch użytkowników -- jeden z nich przetestuje uwierzytelnianie EAP-TTLS/PEAP , a drugi EAP-TLS:


"morfik"          Cleartext-Password := "morfik-ma-kota"
                  Reply-Message = "Witaj, %{User-Name}"

"morfik_laptop"   Auth-type := EAP
                  Reply-Message = "Witaj, Morfiku!"

DEFAULT           Auth-type := Reject
                  Reply-Message := "SPADAJ NA DRZEWO!"

Freeradius przeszukuje powyższy plik w poszukiwaniu dopasowań i domyślnie jeśli jakieś znajdzie, kończy szukanie. Takie zachowanie można dostosować ale na razie zostawmy tak jak jest. Jeśli użytkownik nie zostanie odnaleziony, zadziała linijka z DEFAULT , czyli zapytanie zostanie odrzucone z odpowiednim komunikatem. Przy czy, jeśli zamierzamy korzystać z anonimowej tożsamości (przy EAP-TTLS/PEAP), to nie możemy sprecyzować domyślnego zrzucania zapytań, bo wtedy serwer RADIUSa nie będzie chciał rozmmawiać z użytkownikiem anonimowym.

Użytkownicy posiadający Cleartext-Password będą się łączyć do sieci przy pomocy protokołów EAP-TTLS/PEAP. Z kolei ci posiadający Auth-type := EAP -- przy pomocy jednej z pozostałych metod EAP, w tym też EAP-TLS, w którym to nazwa zdefiniowana jako użytkownik zależy od pola CommonName certyfikatu.

Moduł unix

Jeśli chodzi o samych użytkowników, nie jest wymagane korzystanie z zewnętrznego oprogramowania bazodanowego -- możemy skorzystać z bazy danych użytkowników debiana. Po to właśnie freeradius ma dostęp do pliku /etc/shadow by móc czytać zahashowane hasła użytkowników systemowych. Jeśli jakiś klient wifi posiada konto w systemie i jest ono aktywne, będzie mógł wykorzystać jego login i hasło jako dane dostępowe do sieci wifi.

Trzeba jednak pamiętać, że to z jakiego modułu będziemy korzystać przy weryfikacji użytkowników, ma wpływ na dostępne dla nas metody uwierzytelniania. Protokół PAP może być użyty ze wszystkimi dostępnymi metodami -- przesyła hasła zwykłym tekstem. Podobnie protokół CHAP. Z kolei MS-CHAP wymaga, albo haseł podanych zwykłym tekstem, albo hashów NT-Password. Dokładną rozpiskę można znaleźć tutaj. W przypadku haseł debiana, musimy skorzystać z protokołu PAP. Nie ma zbytnio dla nas znaczenia czy przesyłanie haseł jest jakoś zabezpieczone przez dany protokół, bo i tak wszystko wleci w kanał TLS. Jeśli zdecydujemy się zmienić domyślne zarządzanie hasłami we freeradiusie, trzeba zmodyfikować odpowiednie sekcje w odpowiednich plikach konfiguracyjnych. Edytujemy zatem pliki /etc/freeradius/sites-enabled/default oraz /etc/freeradius/sites-enabled/inner-tunnel i w sekcji authorize zmieniamy sposób obsługiwania użytkowników:

...
authorize {
    ...
    unix
   ...
#   files
...

Oczywiście można korzystać z obu tych modułów jednocześnie, tylko trzeba pilnować by dany użytkownik nie powtarzał się, co może powodować problemy z uwierzytelnianiem. Dodatkowo by zabezpieczyć nieco sam serwer, należy wyłączyć możliwość używania shella oraz zmienić katalog domowy w kontach używanych przez freeradiusa. Na sam początek edytujemy plik /etc/default/useradd i zmieniamy w nim poniższe linijki:

...
#SHELL=/bin/sh
SHELL=/usr/sbin/nologin_freeradius
...
HOME=/var/run
...

Shell /usr/sbin/nologin_freeradius jest kopią /usr/sbin/nologin i jak każdy inny shell, który nie jest faktycznie shellem, zostanie potraktowany przez freeradiusa jako błędny shell i freeradius nie będzie chciał korzystać z takiego shella. Wyjściem jest dodanie powyższego shella do listy poprawnych -- dodajemy po prostu ścieżkę /usr/sbin/nologin_freeradius do pliku /etc/shells :

root@radius:~# echo "/usr/sbin/nologin_freeradius" >> /etc/shells

root@radius:~# cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/sbin/nologin_freeradius

Tworzymy testowe konto użytkownika:

root@radius:/# useradd morfikanin

root@radius:/# passwd morfikanin
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

Sprawdźmy czy jego konfiguracja jest poprawna:

root@radius:/# cat /etc/passwd | grep morfikanin
morfikanin:x:1001:1001::/var/run/morfikanin:/usr/sbin/nologin_freeradius

root@radius:/# cat /etc/group | grep morfikanin
morfikanin:x:1001:

root@radius:/# cat /etc/shadow | grep morfikanin
morfikanin:$6$ztDybjKr$w29hLJJIemqD940VyQBOw/VDRtXoxotWsE.DigutD3UjRa9.Zh/5LZ9fcILJ00iZQ4dCycwfL9gd5FOzmWlbA0:16324:0:99999:7:::

I to z grubsza tyle, przynajmniej jeśli chodzi o konfigurację modułu unix na serwerze RADIUSa. Trzeba także zmienić nieco konfigurację wpasupplicanta na stacji klienckiej -- chodzi o odpowiednie dostosowanie fazy drugiej połączenia, tej wewnątrz tunelu:

...
phase2="auth=PAP"
...

Restrykcje czasowe

Freeradius pozwala także na ustanowienie limitów czasu połączenia, np. jeśli chcemy by dany użytkownik miał dostęp do sieci w godzinach 18-22. Do tego celu służy moduł logintime -- jest domyślnie włączony. By ustawić limit użytkownikom, musimy wyedytować plik /etc/freeradius/users i zmienić nieco domyślne wpisy użytkowników, które ustawiliśmy wyżej, przykładowo:

"morfik"        Cleartext-Password := "morfik-ma-kota", Login-Time :='Al1800-2200'

Parametr Login-Time składa się z trzech części. Dwie z nich to godziny, które precyzują czas (format 24 godzinny) w jakim użytkownik może być zalogowanym do sieci -- start o 18:00 (1800) i koniec o 22:00 (2200). Ostatnia z trzech części to, w tym wypadku Al (małe L) -- oznacza to all, czyli wszystkie dni tygodnia. Można sprecyzować też szereg innych wartości: Mo, Tu, We, Th, Fr, Sa, Su -- dwie pierwsze litery angielskiej nazwy dnia tygodnia. Można również określić przedział, np. od poniedziałku do piątku -- Mo-Fr . Dodatkowo można stosować kombinacje, przykładowo: "Mo-We1300-1400,Sa,Su2305-0630" , co oznacza, że użytkownik może się zalogować do sieci od poniedziałku do środy w godzinach 13-14. Dodatkowo może być zalogowany w sobotę cały czas i w niedzielę w nocy od 23:05 do 6:30 rano w poniedziałek.

Przy logowaniu się do sieci po godzinie 22, serwer RADIUSa zwróci poniższy komunikat:

Sun Sep 14 22:29:25 2014 : Debug: rlm_logintime: Checking Login-Time: 'Al1800-2200'
Sun Sep 14 22:29:25 2014 : Debug: rlm_logintime: timestr returned reject
Sun Sep 14 22:29:25 2014 : Info: [logintime]    expand: You are calling outside your allowed timespan   -> You are calling outside your allowed timespan
Sun Sep 14 22:29:25 2014 : Info: ++[logintime] returns reject

A my nie będziemy w stanie się podłączyć do sieci. W przypadku gdyby użytkownik zalogował się wcześniej, np. o godzinie 21:34, wtedy zostanie wyliczony timeout w sekundach, po którym użytkownik wyleci z sieci -- zostanie rozłączony, przykładowo:

Sun Sep 14 21:34:19 2014 : Debug: rlm_logintime: Checking Login-Time: 'Al1800-1900'
Sun Sep 14 21:34:19 2014 : Debug: rlm_logintime: timestr returned accept
Sun Sep 14 21:34:19 2014 : Debug: rlm_logintime: Session-Timeout set to: 1620
Sun Sep 14 21:34:19 2014 : Info: ++[logintime] returns ok

Session-Timeout set to: 1620 to 1620s, czyli 27min. O godzinie 22:01:19 powinno nastąpić rozłączenie.

Z tym, że taka uwaga -- rozłączanie w oparciu o Session-Timeout nie zadziała jeśli nie zmienimy domyślnej konfiguracji EAP w pliku /etc/freeradius/eap.conf . Musimy przepisać poniższe parametry:

...
ttls {
    ...
    copy_request_to_tunnel = yes
    use_tunneled_reply = yes
...
peap {
...
   copy_request_to_tunnel=yes
   use_tunneled_reply = yes
...

Liczba sesji podłączonych do sieci

Zwykle użytkownik nawiązuje jedno połączenie do sieci. Może ich nawiązać więcej, np. podłączyć drugie urządzenie i wpisać w nim dokładnie takie same dane do logowania. Może także odsprzedać swój login komuś trzeciemu i mogą oni we dwóch korzystać z naszej sieci. Możemy taki proceder ukrócić przez ustawienie limitu jednoczesnych zalogowań do sieci per user.

Jeśli interesuje nas ograniczanie ilości aktywnych sesji użytkownika, edytujemy plik /etc/freeradius/users i zmieniamy użytkownika odpowiednio:

"morfik"        Cleartext-Password := "morfik-ma-kota", Simultaneous-Use := '1'

Jeśli teraz ten użytkownik spróbuje się zalogować na drugim urządzeniu mając przy tym już jedną aktywną sesję, nowa sesja nie zostanie ustanowiona.

Filtrowanie nazw użytkowników

W celu zapewnienia rozsądnych nazw użytkowników używanych przy logowaniu do sieci wifi, możemy wykorzystać moduł filter_username . Jeśli trafią się zapytania, które będą pasować do wzorca zdefiniowanego w /etc/freeradius/policy.conf , freeradius ich nie zaakceptuje i zrzuci je z automatu. Jeśli chcemy taką weryfikację, to w pliku /etc/freeradius/sites-enabled/default w sekcji authorize musimy odkomentować:

...
authorize {
   filter_username
...

Jeśli nie zadowalają nas domyślne reguły filtra ustawione w /etc/freeradius/policy.conf -- możemy część z nich wykomentować, a także zdefiniować własne filtry. Ja korzystam z tego poniższego:

filter_username {
        #
        #  reject mixed case
        #  e.g. "UseRNaMe"
        #
        if (User-Name != "%{tolower:%{User-Name}}") {
                reject
        }

        #
        #  reject all whitespace
        #  e.g. "user@ site.com", or "us er", or " user", or "user "
        #
        if (User-Name =~ / /) {
                update reply {
                        Reply-Message += "Rejected: Username contains whitespace"
                }
                reject
        }

        #
        #  reject Multiple @'s
        #  e.g. "user@site.com@site.com"
        #
        if(User-Name =~ /@.*@/ ) {
                update reply {
                        Reply-Message += "Rejected: Multiple @ in username"
                }
                reject
        }

        #
        #  reject double dots
        #  e.g. "user@site..com"
        #
        if (User-Name =~ /\\.\\./ ) {
                update reply {
                        Reply-Message += "Rejected: Username comtains ..s"
                }
                reject
        }

        #
        #  must have at least 1 string-dot-string after @
        #  e.g. "user@site.com"
        #
        if (User-Name !~ /@(.+)\\.(.+)$/)  {
                update reply {
                        Reply-Message += "Rejected: Realm does not have at least one dot seperator"
                }
                reject
        }

        #
        #  Realm ends with a dot
        #  e.g. "user@site.com."
        #
        if (User-Name =~ /\\.$/)  {
                update reply {
                        Reply-Message += "Rejected: Realm ends with a dot"
                }
                reject
        }

        #
        #  Realm begins with a dot
        #  e.g. "user@.site.com"
        #
        if (User-Name =~ /@\\./)  {
                update reply {
                        Reply-Message += "Rejected: Realm begins with a dot"
                }
                reject
        }
}

Baza danych i moduł sql

Baza danych przyda nam się nie tylko do przechowywania informacji o użytkownikach, którym zezwolimy na dostęp do naszej sieci ale także do prowadzenia statystyk. Najpierw musimy jednak włączyć i skonfigurować moduł sql. By włączyć moduł, edytujemy plik /etc/freeradius/radiusd.conf i usuwamy # z poniższych linijek:

...
$INCLUDE sql.conf
...
$INCLUDE sql/mysql/counter.conf
...

Teraz przechodzimy do edycji pliku /etc/freeradius/sql.conf i ustawiamy odpowiednią konfigurację dla bazy danych MySQL. Na dobrą sprawę domyślna konfiguracja jest w porządku, trzeba się jednak upewnić, że ustawiliśmy odpowiedni typ bazy danych, port, adres, użytkownika i hasło. Z grubsza to by było:

sql {
...
   database = "mysql"
   ...
   server = "localhost"
   port = 3306
   ...
   login = "radius"
   password = "radpass"
   ...
   read_groups = yes
   ...
   readclients = yes
   ...

W powyższym pliku jest również zdefiniowana konfiguracja tabel, jeśli nie odpowiadają nam domyślne nazwy, można oczywiście zmienić wedle uznania.

Dodatkowo, musimy pozmieniać odpowiednie linijki w plikach /etc/freeradius/sites-enabled/default i /etc/freeradius/sites-enabled/inner-tunnel :

...
authorize {
    ...
    sql
    ...

accounting {
   ...
#  detail
   ...
#  unix
   ...
#  radutmp
    ...
    sql
    ...

session {
   ...
#   radutmp
   ...
   sql
...

Jeśli potrzebujemy zalogować wszelkie zapytania z próbami logowania, możemy także odhashować moduł sql w sekcji post-auth . Nie jest to jednak zalecane na produkcyjnych serwerach, bo ten log zawierać może dość dużo informacji, a to może z kolei bardzo obciążyć serwer jak i zjeść sporo miejsca na dysku. W każdym razie jeśli potrzebujemy tej funkcjonalności do testów, to usuwamy hasha z poniższej linijki:

post-auth {
   ...
   sql
   ...

W przypadku wykorzystania bazy danych, mamy także możliwość ogarnięcia ilości jednoczesnych sesji jakie może nawiązać dany użytkownik. W tym celu trzeba edytować plik /etc/freeradius/sql/mysql/dialup.conf i odhashować poniższe linijki:

simul_count_query = "SELECT COUNT(*) \
   FROM ${acct_table1} \
   WHERE username = '%{SQL-User-Name}' \
   AND acctstoptime IS NULL"

Tworzenie bazy danych na potrzeby freeradiusa

By stworzyć bazę danych i użytkownika, który będzie mógł na tej bazie operować, możemy posłużyć się gotowymi plikami, które znajdują się w katalogu /etc/freeradius/sql/mysql/ -- zostały utworzone za sprawą pakietu freeradius-mysql. Interesują nas z grubsza trzy pliki -- /etc/freeradius/sql/mysql/schema.sql , /etc/freeradius/sql/mysql/nas.sql oraz /etc/freeradius/sql/mysql/admin.sql . Poniżej jest zawartość wszystkich trzech plików.

Plik /etc/freeradius/sql/mysql/schema.sql :

#
# Table structure for table 'radacct'
#

CREATE TABLE radacct (
  radacctid bigint(21) NOT NULL auto_increment,
  acctsessionid varchar(64) NOT NULL default '',
  acctuniqueid varchar(32) NOT NULL default '',
  username varchar(64) NOT NULL default '',
  groupname varchar(64) NOT NULL default '',
  realm varchar(64) default '',
  nasipaddress varchar(15) NOT NULL default '',
  nasportid varchar(15) default NULL,
  nasporttype varchar(32) default NULL,
  acctstarttime datetime NULL default NULL,
  acctstoptime datetime NULL default NULL,
  acctsessiontime int(12) default NULL,
  acctauthentic varchar(32) default NULL,
  connectinfo_start varchar(50) default NULL,
  connectinfo_stop varchar(50) default NULL,
  acctinputoctets bigint(20) default NULL,
  acctoutputoctets bigint(20) default NULL,
  calledstationid varchar(50) NOT NULL default '',
  callingstationid varchar(50) NOT NULL default '',
  acctterminatecause varchar(32) NOT NULL default '',
  servicetype varchar(32) default NULL,
  framedprotocol varchar(32) default NULL,
  framedipaddress varchar(15) NOT NULL default '',
  acctstartdelay int(12) default NULL,
  acctstopdelay int(12) default NULL,
  xascendsessionsvrkey varchar(10) default NULL,
  PRIMARY KEY  (radacctid),
  KEY username (username),
  KEY framedipaddress (framedipaddress),
  KEY acctsessionid (acctsessionid),
  KEY acctsessiontime (acctsessiontime),
  KEY acctuniqueid (acctuniqueid),
  KEY acctstarttime (acctstarttime),
  KEY acctstoptime (acctstoptime),
  KEY nasipaddress (nasipaddress)
) ;

#
# Table structure for table 'radcheck'
#

CREATE TABLE radcheck (
  id int(11) unsigned NOT NULL auto_increment,
  username varchar(64) NOT NULL default '',
  attribute varchar(64)  NOT NULL default '',
  op char(2) NOT NULL DEFAULT '==',
  value varchar(253) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY username (username(32))
) ;

#
# Table structure for table 'radgroupcheck'
#

CREATE TABLE radgroupcheck (
  id int(11) unsigned NOT NULL auto_increment,
  groupname varchar(64) NOT NULL default '',
  attribute varchar(64)  NOT NULL default '',
  op char(2) NOT NULL DEFAULT '==',
  value varchar(253)  NOT NULL default '',
  PRIMARY KEY  (id),
  KEY groupname (groupname(32))
) ;

#
# Table structure for table 'radgroupreply'
#

CREATE TABLE radgroupreply (
  id int(11) unsigned NOT NULL auto_increment,
  groupname varchar(64) NOT NULL default '',
  attribute varchar(64)  NOT NULL default '',
  op char(2) NOT NULL DEFAULT '=',
  value varchar(253)  NOT NULL default '',
  PRIMARY KEY  (id),
  KEY groupname (groupname(32))
) ;

#
# Table structure for table 'radreply'
#

CREATE TABLE radreply (
  id int(11) unsigned NOT NULL auto_increment,
  username varchar(64) NOT NULL default '',
  attribute varchar(64) NOT NULL default '',
  op char(2) NOT NULL DEFAULT '=',
  value varchar(253) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY username (username(32))
) ;


#
# Table structure for table 'radusergroup'
#

CREATE TABLE radusergroup (
  username varchar(64) NOT NULL default '',
  groupname varchar(64) NOT NULL default '',
  priority int(11) NOT NULL default '1',
  KEY username (username(32))
) ;

#
# Table structure for table 'radpostauth'
#

CREATE TABLE radpostauth (
  id int(11) NOT NULL auto_increment,
  username varchar(64) NOT NULL default '',
  pass varchar(64) NOT NULL default '',
  reply varchar(32) NOT NULL default '',
  authdate timestamp NOT NULL,
  PRIMARY KEY  (id)
) ;

Plik /etc/freeradius/sql/mysql/nas.sql :

#
# Table structure for table 'nas'
#
CREATE TABLE nas (
  id int(10) NOT NULL auto_increment,
  nasname varchar(128) NOT NULL,
  shortname varchar(32),
  type varchar(30) DEFAULT 'other',
  ports int(5),
  secret varchar(60) DEFAULT 'secret' NOT NULL,
  server varchar(64),
  community varchar(50),
  description varchar(200) DEFAULT 'RADIUS Client',
  PRIMARY KEY (id),
  KEY nasname (nasname)
);

Plik /etc/freeradius/sql/mysql/admin.sql :

#
#  Create default administrator for RADIUS
#
CREATE USER 'radius'@'localhost';
SET PASSWORD FOR 'radius'@'localhost' = PASSWORD('radpass');

# The server can read any table in SQL
GRANT SELECT ON radius.* TO 'radius'@'localhost';

# The server can write to the accounting and post-auth logging table.
#
#  i.e.
GRANT ALL on radius.radacct TO 'radius'@'localhost';
GRANT ALL on radius.radpostauth TO 'radius'@'localhost';

O ile w przypadku dwóch pierwszych plików nie trzeba zbytnio nic zmieniać, o tyle plik tworzący użytkownika bazy danych wymaga przejrzenia i zmienienia odpowiednich parametrów, np. hasła. Ja nie korzystałem z powyższego pliku by ustawić prawa do tabel użytkownikowi radius i zwyczajnie przyznałem wszystkie prawa do określonej bazy przy pomocy poniższych linijek:

CREATE USER 'radius'@'localhost';
SET PASSWORD FOR 'radius'@'localhost' = PASSWORD('radpass');
GRANT USAGE ON *.* TO 'radius'@'localhost' IDENTIFIED BY 'radpass';
GRANT ALL PRIVILEGES ON `radius`.* TO 'radius'@'localhost';

Czego efektem jest:

mysql> SHOW GRANTS FOR radius@localhost;
+---------------------------------------------------------------------------------------------------------------+
| Grants for radius@localhost                                                                                   |
+---------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'radius'@'localhost' IDENTIFIED BY PASSWORD '*B4DE392161EF46F985AFA6C49CB2F1254B3F3BF2' |
| GRANT ALL PRIVILEGES ON `radius`.* TO 'radius'@'localhost'                                                    |
+---------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

Moduł sql wykorzystuje dwa zestawy tabel z atrybutami przy autoryzacji. Jeden zestaw (tabele radcheck i radreply) jest stosowany do określonego użytkownika, drugi (tabele radgroupcheck i radgroupreply) obsługuje członków określonych grup. Tabela usergroup dostarcza listy grup wraz z użytkownikami będącymi ich członkami, jak i również pole priorytetu odpowiedzialne za kontrolę kolejności w jakiej grupy są przetwarzane.

Gdy zapytanie przychodzi do serwera i jest przetwarzane przez moduł sql, cały proces wygląda mniej więcej tak:

1. Najpierw jest przeszukiwana tabela radcheck w poszukiwaniu określonych atrybutów sprawdzających dla użytkownika.
2. Jeśli jakieś atrybuty sprawdzające zostaną odnalezione i dopasowane, wyciągane są atrybuty odpowiedzi z tabeli radreply dla tego użytkownika i to je widzimy w zwracanej przez serwer odpowiedzi.
3. Następnie zachodzi przetwarzanie grup, jeśli są spełnione poniższe warunki:
- użytkownik nie jest odnaleziony w tabeli radcheck
- użytkownik jest odnaleziony w tabeli radcheck ale żaden z atrybutów sprawdzających nie może zostać dopasowany
- użytkownik jest odnaleziony w tabeli radcheck, atrybuty sprawdzające zostały dopasowane i Fall-Through jest ustawiony w tabeli radreply
- użytkownik jest odnaleziony w tabeli radcheck, atrybuty sprawdzające zostały dopasowane i dyrektywa read_groups jest ustawiona na "yes".
4. W przypadku gdy użytkownik podlega pod przetwarzanie grup, listowane są wszystkie grupy, których członkiem jest dany użytkownik, posortowane według priorytetu. Ten priorytet zezwala na kontrolę kolejności w jakiej grupy są przetwarzane, co może mieć znaczenie w wielu przypadkach.
5. Dla każdej z grup, do której użytkownik należy, odpowiadające atrybuty sprawdzające są wyciągane z tabeli radgroupcheck i porównywane z zapytaniem. Jeśli coś zostanie dopasowane, atrybuty odpowiedzi dla tej grupy są wyciągane z tabeli radgroupreply , po czym są aplikowane.
6. Następnie przetwarzana jest kolejna grupa jeśli:
- nie było dopasowania dla atrybutu sprawdzającego dla poprzedniej grupy
- Fall-Through został ustawiony na ostatniej pozycji w atrybutach odpowiedzi danej grupy.
7. Jeśli użytkownik ma ustawiony atrybut User-Profile, albo też w pliku sql.conf jest ustawiona opcja Default Profile, kroki 4-6 są powtarzane dla grup, których członkiem jest ten profil.

Mając już odpowiednio przygotowane wszystkie pliki, wklepujemy w terminal poniższe linijki:

mysql -u root -p radius < /etc/freeradius/sql/mysql/schema.sql
mysql -u root -p radius < /etc/freeradius/sql/mysql/nas.sql
mysql -u root -p radius <  /etc/freeradius/sql/mysql/admin.sql

Sprawdźmy czy baza i tabele zostały utworzone zgodnie z naszymi oczekiwaniami:

root@radius:~# mysql -u root -p
Enter password:
...
mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| radius             |
+--------------------+
4 rows in set (0.00 sec)

mysql> USE radius;
Database changed

mysql> SHOW TABLES;
+------------------+
| Tables_in_radius |
+------------------+
| nas              |
| radacct          |
| radcheck         |
| radgroupcheck    |
| radgroupreply    |
| radpostauth      |
| radreply         |
| radusergroup     |
+------------------+
8 rows in set (0.00 sec)

Wygląda dobrze. Dodajmy testowego użytkownika do bazy -- do tabeli radcheck . Musimy tylko poznać jeszcze strukturę tej tabeli:

mysql> DESCRIBE radcheck;
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| username  | varchar(64)      | NO   | MUL |         |                |
| attribute | varchar(64)      | NO   |     |         |                |
| op        | char(2)          | NO   |     | ==      |                |
| value     | varchar(253)     | NO   |     |         |                |
+-----------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Teraz wpisujemy odpowiednie wartości:

mysql> INSERT INTO `radcheck` VALUES (NULL, 'test', 'Cleartext-Password', ':=', 'test');
Query OK, 1 row affected (0.03 sec)

mysql> SELECT * FROM radcheck;
+----+----------+--------------------+----+-------+
| id | username | attribute          | op | value |
+----+----------+--------------------+----+-------+
|  1 | test     | Cleartext-Password | := | test  |
+----+----------+--------------------+----+-------+
1 row in set (0.00 sec)

I w tej chwili powinniśmy być wstanie zalogować się do sieci z wykorzystaniem nazwy użytkownika test i hasła test, które należy podać w konfiguracji wpasupplicanta. Z tym, że urządzenia AP/NAS są czytane z pliku clients.conf , a my wyżej stworzyliśmy tabelę nas , w której będziemy przechowywać dane klientów RADIUSa. Dlatego też musimy odhaczyć przetwarzanie pliku client.conf i sprecyzować odpowiednie urządzenia w bazie danych. Przechodzimy zatem do edycji pliku /etc/freeradius/radiusd.conf i wstawiamy hasha w poniższej linijce:

# $INCLUDE clients.conf

I definiujemy wpisy dla localhost i dla naszego AP/NAS w bazie danych:

root@radius:/etc/freeradius# mysql -u radius -p radius
...
mysql> DESCRIBE nas;
+-------------+--------------+------+-----+---------------+----------------+
| Field       | Type         | Null | Key | Default       | Extra          |
+-------------+--------------+------+-----+---------------+----------------+
| id          | int(10)      | NO   | PRI | NULL          | auto_increment |
| nasname     | varchar(128) | NO   | MUL | NULL          |                |
| shortname   | varchar(32)  | YES  |     | NULL          |                |
| type        | varchar(30)  | YES  |     | other         |                |
| ports       | int(5)       | YES  |     | NULL          |                |
| secret      | varchar(60)  | NO   |     | secret        |                |
| server      | varchar(64)  | YES  |     | NULL          |                |
| community   | varchar(50)  | YES  |     | NULL          |                |
| description | varchar(200) | YES  |     | RADIUS Client |                |
+-------------+--------------+------+-----+---------------+----------------+
9 rows in set (0.00 sec)

mysql> INSERT INTO nas VALUES (NULL, '192.168.1.1', 'router_1043nd', 'other', NULL , 'tajna-fraza', NULL, NULL, 'RADIUS Client');
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO nas VALUES (NULL, '127.0.1.1', 'localhost', 'other', NULL , 'test123', NULL, NULL, 'RADIUS Client');
Query OK, 1 row affected, 1 warning (0.04 sec)

mysql> SELECT * FROM nas;
+----+-------------+---------------+-------+-------+-------------+--------+-----------+---------------+
| id | nasname     | shortname     | type  | ports | secret      | server | community | description   |
+----+-------------+---------------+-------+-------+-------------+--------+-----------+---------------+
|  1 | 192.168.1.1 | router_1043nd | other |  NULL | tajna-fraza | NULL   | NULL      | RADIUS Client |
+----+-------------+---------------+-------+-------+-------------+--------+-----------+---------------+
|  2 | 127.0.0.1   | localhost     | other |  NULL | test123     | NULL   | NULL      | RADIUS Client |
+----+-------------+---------------+-------+-------+-------------+--------+-----------+---------------+
2 rows in set (0.00 sec)

Odpalamy serwer RADIUSa i logujemy się do sieci wifi ze stacji klienckiej w celu sprawdzenia czy użytkownik test zostanie wpuszczony -- nie powinno być problemów z zalogowaniem się.

Ogólnie rzecz biorąc, schemat bazy danych odzwierciedla układ pliku users. Dlatego też informacje na temat atrybutów sprawdzających (check items) jak i atrybutów odpowiedzi (reply items) można znaleźć w "man 5 users" oraz w przykładach w pliku users, a co za tym idzie, nie będę tutaj opisywał jeszcze raz tych wszystkich rzeczy, które zostały poruszone podczas opisywania pliku users -- wystarczy ctrl+c odpowiedniego parametru z pliku users i ctrl+v do odpowiedniego pola w bazie danych. Poniżej tylko zamieszczam dla porównania konfigurację użytkowników przeniesioną z pliku users do bazy danych:

INSERT INTO `radcheck` VALUES
(3,'morfik','Cleartext-Password',':=','test'),
(4,'morfik','Simultaneous-Use',':=','2'),
(5,'morfik','Login-Time',':=','Mo-Th0800-2000,Fr,Sa,Su'),
(6,'morfik_laptop','Auth-type',':=','EAP');

INSERT INTO `radreply` VALUES
(3,'morfik','Reply-Message','=','Czolem panie kapitanie!'),
(4,'morfik','Fall-Through','=','No'),
(5,'morfik_laptop','Reply-Message','=','Witaj, Morfiku!'),
(6,'morfik_laptop','Fall-Through','=','No'),;

Co owocuje poniższymi tabelami:

mysql> select * from radcheck;
+----+---------------+--------------------+----+-------------------------+
| id | username      | attribute          | op | value                   |
+----+---------------+--------------------+----+-------------------------+
|  3 | morfik        | Cleartext-Password | := | test                    |
|  4 | morfik        | Simultaneous-Use   | := | 2                       |
|  5 | morfik        | Login-Time         | := | Mo-Th0800-2000,Fr,Sa,Su |
|  6 | morfik_laptop | Auth-type          | := | EAP                     |
+----+---------------+--------------------+----+-------------------------+
4 rows in set (0.00 sec)

mysql> select * from radreply;
+----+---------------+---------------+----+-------------------------+
| id | username      | attribute     | op | value                   |
+----+---------------+---------------+----+-------------------------+
|  3 | morfik        | Reply-Message | =  | Czolem panie kapitanie! |
|  4 | morfik        | Fall-Through  | =  | No                      |
|  5 | morfik_laptop | Reply-Message | =  | Witaj, Morfiku!         |
|  6 | morfik_laptop | Fall-Through  | =  | No                      |
+----+---------------+---------------+----+-------------------------+
4 rows in set (0.00 sec)

Testowanie konfiguracji

Zaleca się by testować niewielkie zmiany dokonywane w konfiguracji radiusa. Także jeśli coś z powyższego nie działa, najlepiej powrócić domyślne ustawienia całej konfiguracji i jeszcze raz wprowadzić zmiany, tym razem mniejsze i po każdej zmianie dokonać sprawdzenia poprawności konfiguracji opisanymi w tym paragrafie sposobami.

Freeradiusa można odpalić w trybie debug przy pomocy polecenia freeradius -X wpisanego w terminalu. Im więcej X sprecyzujemy, tym bardziej szczegółowe będą logi, maksymalnie można ustawić trzy (-XXX). Weryfikacja plików konfiguracyjnych odbywa się przez sprecyzowanie opcji -C , przykładowo:

root@radius:~# freeradius -XXXC
...
Sun Sep 14 13:54:37 2014 : Debug: Configuration appears to be OK.

Jeśli wystąpią jakieś problemy i freeradius nie będzie się chciał nam odpalić, wtedy wystarczy zajrzeć w log i tam z pewnością będzie wypisane co nawaliło i nawet jak to poprawić.

To mniej więcej tyle jeśli chodzi o debug serwera ale możemy także zajrzeć w logi klienta. Narzędzie wpasupplicant wypluwa też pokaźne logi. Poniżej znajdują się dwie linijki, które trzeba wpisać jedna po drugiej w osobnych terminalach, przykładowo:

# /sbin/wpa_supplicant -dd -P /var/run/wpa_supplicant.wlan1.pid -i wlan1 -W -b bond0 -D nl80211,wext -c /etc/wpa_supplicant/wpa_supplicant.conf
...
CTRL_IFACE - wlan1 - wait for monitor to attach

I teraz wpisujemy drugą z linijek:

# /sbin/wpa_cli -P /var/run/wpa_action.wlan0.pid -i wlan0 -p /var/run/wpa_supplicant -a /sbin/wpa_action

Po jej wpisaniu powinno się rozpocząć podłączanie klienta do sieci wifi i powinien zostać wyświetlony obszerny log na pierwszej konsoli, w którym można prześledzić wszystkie etapy połączenia, na wypadek gdyby coś poszło nie tak.

Przy logowaniu się do sieci, również w syslogu są zwracane pewne komunikatu, np. po pomyślnym zalogowaniu można tam ujrzeć:

Sep 14 14:05:38 morfikownia kernel: [ 2303.974300] wlan1: authenticate with e8:94:f6:68:79:f0
Sep 14 14:05:39 morfikownia kernel: [ 2304.364788] wlan1: send auth to e8:94:f6:68:79:f0 (try 1/3)
Sep 14 14:05:39 morfikownia kernel: [ 2304.366995] wlan1: authenticated
Sep 14 14:05:39 morfikownia kernel: [ 2304.369278] wlan1: associate with e8:94:f6:68:79:f0 (try 1/3)
Sep 14 14:05:39 morfikownia kernel: [ 2304.373141] wlan1: RX AssocResp from e8:94:f6:68:79:f0 (capab=0x431 status=0 aid=1)
Sep 14 14:05:39 morfikownia kernel: [ 2304.383611] wlan1: associated
Sep 14 14:05:39 morfikownia kernel: [ 2304.383653] IPv6: ADDRCONF(NETDEV_CHANGE): wlan1: link becomes ready
Sep 14 14:05:39 morfikownia kernel: [ 2304.383716] cfg80211: Calling CRDA for country: PL
Sep 14 14:05:39 morfikownia kernel: [ 2304.419178] cfg80211: Regulatory domain changed to country: PL
Sep 14 14:05:39 morfikownia kernel: [ 2304.419184] cfg80211:  DFS Master region: ETSI
Sep 14 14:05:39 morfikownia kernel: [ 2304.419185] cfg80211:   (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)
Sep 14 14:05:39 morfikownia kernel: [ 2304.419187] cfg80211:   (2402000 KHz - 2482000 KHz @ 40000 KHz), (N/A, 2000 mBm)
Sep 14 14:05:39 morfikownia kernel: [ 2304.419189] cfg80211:   (5170000 KHz - 5250000 KHz @ 80000 KHz), (N/A, 2000 mBm)
Sep 14 14:05:39 morfikownia kernel: [ 2304.419191] cfg80211:   (5250000 KHz - 5330000 KHz @ 80000 KHz), (N/A, 2000 mBm)
Sep 14 14:05:39 morfikownia kernel: [ 2304.419192] cfg80211:   (5490000 KHz - 5710000 KHz @ 80000 KHz), (N/A, 2700 mBm)
Sep 14 14:05:39 morfikownia kernel: [ 2304.419194] cfg80211:   (57240000 KHz - 65880000 KHz @ 2160000 KHz), (N/A, 4000 mBm)
Sep 14 14:05:39 morfikownia wpa_action: WPA_IFACE=wlan1 WPA_ACTION=CONNECTED
Sep 14 14:05:39 morfikownia wpa_action: WPA_ID=2 WPA_ID_STR= WPA_CTRL_DIR=/var/run/wpa_supplicant
Sep 14 14:05:39 morfikownia wpa_action: network settings not defined for default in /etc/network/interfaces
Sep 14 14:05:39 morfikownia wpa_action: ifup wlan1=default
Sep 14 14:05:39 morfikownia wpa_action: creating sendsigs omission pidfile: /run/sendsigs.omit.d/wpasupplicant.wpa_supplicant.wlan1.pid
Sep 14 14:05:39 morfikownia wpa_action: bssid=e8:94:f6:68:79:f0
Sep 14 14:05:39 morfikownia wpa_action: ssid=Winter Is Coming
Sep 14 14:05:39 morfikownia wpa_action: id=2
Sep 14 14:05:39 morfikownia wpa_action: mode=station
Sep 14 14:05:39 morfikownia wpa_action: pairwise_cipher=CCMP
Sep 14 14:05:39 morfikownia wpa_action: group_cipher=CCMP
Sep 14 14:05:39 morfikownia wpa_action: key_mgmt=WPA2/IEEE 802.1X/EAP
Sep 14 14:05:39 morfikownia wpa_action: wpa_state=COMPLETED
Sep 14 14:05:39 morfikownia wpa_action: p2p_device_address=e8:94:f6:1e:15:e9
Sep 14 14:05:39 morfikownia wpa_action: address=e8:94:f6:1e:15:e9
Sep 14 14:05:39 morfikownia wpa_action: Supplicant PAE state=AUTHENTICATED
Sep 14 14:05:39 morfikownia wpa_action: suppPortStatus=Authorized
Sep 14 14:05:39 morfikownia wpa_action: EAP state=SUCCESS
Sep 14 14:05:39 morfikownia wpa_action: selectedMethod=13 (EAP-TLS)
Sep 14 14:05:39 morfikownia wpa_action: EAP TLS cipher=ECDHE-RSA-AES256-SHA

I jak można wyczytać z powyższego logu, zostaliśmy podłączeni do sieci za pomocą protokołu EAP-TLS, czyli przez weryfikację certyfikatów serwera/klienta.

Narzędzia testowe freeradiusa

Freeradius dostarcza kilka narzędzi testowych -- są one zawarte w pakiecie freeradius-utils . Nas głównie interesują dwa z nich radclient oraz radtest , z tym, że radtest jest nakładką na radclient. Oba programy się używa nieco inaczej ale każdy z nich dostarcza tych samych informacji na temat przesyłanych pakietów.

Poniżej jest przedstawiony prosty test logowania się do sieci z wykorzystaniem obu wspomnianych narzędzi:

morfik:~$ radtest morfik tajne-haslo 192.168.1.166 0 test-test
Sending Access-Request of id 248 to 192.168.1.166 port 1812
        User-Name = "morfik"
        User-Password = "tajne-haslo
        NAS-IP-Address = 192.168.1.150
        NAS-Port = 0
        Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host 192.168.1.166 port 1812, id=248, length=20

morfik~$ echo "User-Name = morfik, User-Password = tajne-haslo" | radclient -x 192.168.1.166 auth test-test
Sending Access-Request of id 200 to 192.168.1.166 port 1812
        User-Name = "morfik"
        User-Password = "tajne-haslo"
rad_recv: Access-Accept packet from host 192.168.1.166 port 1812, id=200, length=20

Jak widać wyżej, radtest jest nieco prostszy w użyciu. Zwykle z powyższych narzędzi będzie się korzystać na maszynie, na której jest zainstalowane oprogramowanie freeradius i trzeba się upewnić, że serwer RADIUSa nie ma sprecyzowanego interfejsu i adresu w pliku /etc/freeradius/radiusd.conf oraz, że w pliku /etc/freeradius/clients.conf (lub w bazie danych) widnieje wpis zezwalający przyjmować żądania z adresu 127.0.0.1 , czyli z lokalnej maszyny. Jeśli któryś z powyższych warunków nie zostanie spełniony, testy się nie powiodą.

Test konfiguracji EAP

Oprogramowanie freeradiusa nie jest w stanie przeprowadzić testów EAP -- do tego celu potrzebne nam będzie narzędzie eapol_test , które jest dostarczane z pakietem wpasupplicant. Problem w tym, że domyślnie opcje od eapol_test są zahashowane i by móc używać tego narzędzia, trzeba je sobie samemu skompilować. Inny problem w tym, że mi na debianie nie udało się tego narzędzia zbudować, bo przy kompilacji ciągle mi wyrzucało jakiegoś wrednego błęda. W każdym razie, opis kompilacji jak i samego narzędzia można znaleźć pod tym linkiem.

Freeradius na routerze?

Istnieje też możliwość wyeliminowania dedykowanej maszyny pod serwer RADIUSa i przeprowadzenie wyżej wymienionych kroków na routerze. W OpenWRT oprogramowanie freeradiusa zostało podzielone na szereg mniejszych pakietów, które w sposób modularny rozbudowują funkcjonalność serwera RADIUS. Nazwy pakietów mówią same za siebie:

root@red_viper:~# opkg list | grep freeradius
freeradius2 - 2.2.5-1 - A flexible RADIUS server (version 2)
freeradius2-common - 2.2.5-1 - common files
freeradius2-democerts - 2.2.5-1 - Demo certificates to test the server
freeradius2-mod-always - 2.2.5-1 - Always module
freeradius2-mod-attr-filter - 2.2.5-1 - ATTR filter module
freeradius2-mod-attr-rewrite - 2.2.5-1 - ATTR rewrite module
freeradius2-mod-chap - 2.2.5-1 - CHAP module
freeradius2-mod-detail - 2.2.5-1 - Detailed accounting module
freeradius2-mod-eap - 2.2.5-1 - Base EAP module
freeradius2-mod-eap-gtc - 2.2.5-1 - EAP/GTC module
freeradius2-mod-eap-md5 - 2.2.5-1 - EAP/MD5 module
freeradius2-mod-eap-mschapv2 - 2.2.5-1 - EAP/MS-CHAPv2 module
freeradius2-mod-eap-peap - 2.2.5-1 - EAP/PEAP module
freeradius2-mod-eap-tls - 2.2.5-1 - EAP/TLS module
freeradius2-mod-eap-ttls - 2.2.5-1 - EAP/TTLS module
freeradius2-mod-exec - 2.2.5-1 - EXEC module
freeradius2-mod-expiration - 2.2.5-1 - Expiration module
freeradius2-mod-expr - 2.2.5-1 - EXPR module
freeradius2-mod-files - 2.2.5-1 - Module using local files for authorization
freeradius2-mod-logintime - 2.2.5-1 - Logintime module
freeradius2-mod-mschap - 2.2.5-1 - MS-CHAP and MS-CHAPv2 module
freeradius2-mod-pap - 2.2.5-1 - PAP module
freeradius2-mod-passwd - 2.2.5-1 - Rlm passwd module
freeradius2-mod-preprocess - 2.2.5-1 - Request pre-processing module
freeradius2-mod-radutmp - 2.2.5-1 - Radius UTMP module
freeradius2-mod-realm - 2.2.5-1 - Realms handling module
freeradius2-mod-sql - 2.2.5-1 - Base SQL module
freeradius2-mod-sql-pgsql - 2.2.5-1 - PostgreSQL module
freeradius2-mod-sql-sqlite - 2.2.5-1 - SQLite module
freeradius2-mod-sqlcounter - 2.2.5-1 - Generic SQL Counter module
freeradius2-mod-sqllog - 2.2.5-1 - SQL Logging module
freeradius2-utils - 2.2.5-1 - Misc. client utilities

Niestety, paru pakietów brakuje, np. modułu mysql, niemniej jednak podstawową funkcjonalność można bez problemu zaimplementować. Cała procedura jest przedstawiona w tym linku.

Makulatura

Pisane w oparciu o:

http://www.smallnetbuilder.com/wireless/wireless-howto/30213-how-to-setting-up-freeradius-for-wpa-a-wpa2-enterprise-part-2?showall=&start=1
http://sekurak.pl/bezpieczenstwo-sieci-wi-fi-czesc-9-budowa-sieci-wpawpa-2-enterprise-z-wykorzystaniem-freeradius/
https://www.eduroam.pl/Dokumentacja/freeradius2-1.0.pdf
http://rpc.one.pl/index.php/lista-artykulow/34-openwrt/67-konfiguracja-radius-jako-kontrolera-autoryzacji-wifi-pod-openwrt
http://eko.one.pl/?p=openwrt-freeradius2
http://www.networkworld.com/article/2223672/access-controlhich-eap-types-do-you-need-for-which/access-control/which-eap-types-do-you-need-for-which-identity-projects.html
http://blogs.msdn.com/b/kaushal/archive/2010/11/05/ssl-certificates.aspx
http://www.serveradminblog.com/category/freeradius/
https://extremeshok.com/5486/debian-7-freeradius-server-mysql-authentication/
http://www.gnu.org/software/radius/manual/radius.html

OSnews Wykop Blip Flaker Kciuk Śledzik Facebook Identi.ca Twitter del.icio.us Google Bookmarks