 Instrukcja konfiguracji serwera Apache
z użyciem certyfikatów PKI UMK
Adam Laskowski
22-04-2001
0. Wstęp
Dokument ten opisuje proces konfiguracji uprawnień dostępu do serwera
Apache w oparciu o certyfikaty SSL. Nie zawiera on instrukcji instalacji
Apache z mod_ssl - zawarta jest ona w
odrębnym dokumencie.
W treści dokumentu wszystkie ścieżki podane są (o ile nie jest to
wyraźnie określone) relatywnie względem
katalogu głównego Apache'a - typowo /usr/local/apache .
1. Instalacja certyfikatów serwera
Zakładamy, że przy instalacji Apache nie zostały wygenerowane certyfikaty
serwera. Jeżeli tak się stało to należy te certyfikaty skasować, aby nie
wprowadzać zbędnego zamieszania w katalogach konfiguracyjnych Apache.
1.1. Lokalizacja certyfikatów
Wszystkie pliki certyfikatów, kluczy prywatnych, CRL itp. są przechowywane
w podkatalogach katalogu conf :
ssl.crt/ - certyfikat serwera, certyfikaty CA
ssl.key/ - klucze prywatne certyfikatów
ssl.csr/ - zlecenia certyfikacji (Certificate Signing Request)
ssl.crl/ - listy certyfikatów odwołanych (Certificate Revocation List)
ssl.prm/ - parametry kluczy DSA (DSA Parameter Files)
Trzy ostatnie rodzaje plików nie są używane w ramach naszej PKI - będziemy
się zajmować tylko plikami .crt oraz .key .
1.2. Rozpakowanie certyfikatów
Urząd Certyfikacyjny dostarcza nam certyfikat w formie zaszyfrowanego i spakowanego pliku
.p12 . Należy więc taki plik rozpakować w bezpiecznym miejscu używając
polecenia openssl pkcs12 :
# openssl pkcs12 -nocerts \
-in server.p12 -out server.key
Enter Import Password: *******
MAC verified OK
Enter PEM pass phrase: *******
Veifying password - Enter PEM pass phrase: *******
# openssl pkcs12 -clcerts -nokeys \
-in server.p12 -out server.crt
Enter Import Password: *******
MAC verified OK
# openssl pkcs12 -cacerts -nokeys \
-in server.p12 -out chain.crt
Enter Import Password: *******
MAC verified OK
#
Hasła, które podajemy przy rozpakowywaniu to odpowiednio:
- Import password - hasło zabezpieczające plik
.p12
- PEM pass phrase - hasło zabezpieczające klucz prywatny
Hasło klucza prywatnego
Musimy w tym miejscu podjąć ważną decyzję: czy klucz prywatny naszego serwera
będzie zabezpieczony hasłem. W zależności od okoliczności możemy:
- Zabezpieczyć klucz: zwiększamy wtedy bezpieczeństwo serwera, jednak przy
każdym jego restarcie musimy ręcznie wpisać hasło klucza prywatnego.
Utrudnia to automatyzację serwera i zmniejsza odporność na awarie.
- Nie zabezpieczać klucza: serwer wtedy potrafi podnieść się automatycznie
np. po restarcie, jednak narażamy się na ryzyko dostania się klucza w
niepowołane ręce.
Jeżeli zdecydujemy się używać klucza prywatnego serwera w postaci nie
zabezpieczonej hasłem, to w pierwszym poleceniu (eksportującym klucz
prywatny) musimy dodać parametr -nodes . Program
openssl nie będzie wtedy pytał o PEM pass phrase:
# openssl pkcs12 -nocerts -nodes \
-in server.p12 -out server.key
Enter Import Password: *******
MAC verified OK
#
Otrzymaliśmy zatem trzy pliki:
server.crt - certyfikat serwera
server.key - klucz prywatny serwera
chain.crt - łańcuch certyfikatów potwierdzających
certyfikat serwera
Certyfikaty (*.crt ) kopiujemy do katalogu conf/ssl.crt ,
a klucz publiczny (server.key ) do katalogu conf/ssl.key .
1.3. Konfiguracja serwera
Zanim skonfigurujemy pliki certyfikatów należy poprawić konfigurację portów
Apache. Domyślna konfiguracja bowiem zakłada, że serwer HTTP działa na porcie
8080, a HTTPS - na porcie 8433. Należy to poprawić, ustawiając odpowiednio
port 80 i 443 w następujących miejscach:
Domyślny serwer (HTTP) używa dyrektywy Port w głównym wątku
pliku konfiguracyjnego. Port SSL natomiast ustawiamy w sekcji otoczonej
nagłówkiem <IfDefine SSL> . Należy jeszcze poprawić
nagłówek domyślnego VirtualHost 'a- znajdujemy linię:
<VirtualHost _default_:8443>
i zmieniamy numer portu na 443.
Aby skonfigurować Apache tak, aby używał właśnie skopiowanych certyfikatów
do autoryzacji serwera i bezpiecznych połączeń należy poinformować go
o lokalizacji i nazwach plików. W tym celu wyszukujemy i poprawiamy
następujące pola w pliku conf/httpd.conf :
SSLCertificateFile
wpisujemy tu pełną ścieżkę do pliku server.crt
SSLCertificateKeyFile
wpisujemy tu pełną ścieżkę do pliku server.key
SSLCertificateChainFile
wpisujemy tu pełną ścieżkę do pliku chain.crt
SSLCACertificateFile
wpisujemy tu pełną ścieżkę do pliku chain.crt
Jeżeli chcemy autoryzować dostęp klientów włączonych w struktury certyfikacji
inne niż ta, do której należy serwer WWW, to możemy, korzystając z
dyrektywy SSLCACertificateFile podać lokalizację pliku z
certyfikatami innych urzędów certyfikacyjnych. Można tam wskazać np.
plik conf/ssl.crt/ca-bundle.crt - przy instalacji Apache
ładowany tam jest zbiór certyfikatów największych światowych urzędów
certyfikacyjnych.
Podane opcje konfiguracyjne możemy definiować osobno w obrębie każdego
wirtualnego hosta udostępnianego przez dany serwer.
1.4. Uruchamianie serwera
Apache z obsługą mod_ssl uruchamiamy w standardowy sposób - przy użyciu
polecenia bin/apachectl . Mamy następujące możliwości:
bin/apachectl start
Uruchomienie wyłącznie standardowego serwera HTTP
bin/apachectl startssl
Uruchomienie serwera HTTP i HTTPS
bin/apachectl stop
Zatrzymanie serwera
bin/apachectl restart
Przeładowanie konfiguracji serwera (lub uruchomienie, jeśli nie działa)
Zatem uruchamiamy serwer HTTPS:
# bin/apachectl startssl
Apache/1.3.14 mod_ssl/2.7.1 (Pass Phrase Dialog)
Some of your private key files are encrypted
To read them you have to provide us with the pass phrases.
Server www.ds3.uni.torun.pl:443 (RSA)
Enter pass phrase:
Ok: Pass Phrase Dialog successful.
bin/apachectl startssl: httpd started
#
Jak widać, klucz prywatny jest zaszyfrowany i przy starcie serwera
należało podać jego hasło.
2. Konfigurowanie uprawnień do zasobów serwera
Konfiguracja dostępu do zasobów bazuje całkowicie na jednoznacznej
nazwie obiektu (Distinguished Name, DN), zawartej w
certyfikacie. Certyfikat kliencki osoby próbującej uzyskać dostęp
powinien (choć nie jest to regułą) być umieszczony w tej samej strukturze
PKI, co serwer WWW. Jak już wiemy, serwer przechowuje certyfikaty
wszystkich urzędów certyfikacyjnych aż do root CA zatem może
zawsze sprawdzić autentyczność certyfikatu klienta.
Zakładamy, że Czytelnik jest zaznajomiony z ogólnymi regułami konfigurowania
dostępu do stron w Apache (bez użycia SSL). Jeżeli pojęcia zasięgu
(scope), lokacji (location), czy wirtualnego serwera
(virtual host) nie są Czytelnikowi znane, to odsyłamy do dokumentacji
Apache.
Przypomnijmy, że dostęp do zasobów można konfigurować globalnie
(w pliku httpd.conf ) lub lokalnie (z użyciem
plików .htaccess ). Pliki .htaccess działają
podobnie jak odpowiadające im sekcje <Directory> w
pliku konfiguracyjnym, ale umożliwiają np. definiowanie uprawnień przez
właściciela strony.
W dalszym ciągu opiszemy opcje mod_ssl umożliwiające
dokładne konfigurowanie uprawnień dostępu bez rozdrabniania się na
scope w jakim wystepują - znaczenie jest w większości przypadków
identyczne.
2.1. SSLRequireSSL
Włączenie tej opcji powoduje, że fragment danych, do którego uprawnienia
właśnie definiujemy będzie dostępny jedynie przy użyciu protokołu https.
Jeżeli klient będzie próbował dostać się do niego przy użyciu połączenia
nieszyfrowanego, to otrzyma błąd Permission Denied.
2.2. SSLVerifyClient poziom
Opcja ta ustala dopuszczalny sposób weryfikacji klienta chcącego uzyskać
dostęp do danego zasobu. Możliwe wartości parametru poziom to:
none - certyfikat nie jest wymagany
optional - certyfikat nie jest wymagany, lecz może
zostać przedstawiony jeżeli przeglądarka wyrazi taką chęć
optional_no_ca - certyfikat jest wymagany, jednak nie
musi on koniecznie być potwierdzony przez zaufane CA
require - certyfikat jest wymagany i musi on mieć
potwierdzenie możliwe do sprawdzenia przy użyciu certyfikatów CA
istniejących w bazie serwera.
W praktyce używamy jedynie opcji none lub require .
2.3. SSLVerifyDepth poziom
Ta opcja umożliwia ustalenie ilości poziomów weryfikacji CA dla certyfikatu
klienta. Inaczej: ilośći certyfikatów nadrzędnych CA uwzględnionych przy
sprawdzaniu autentyczności certyfikatu klienta dla włączonej opcji
SSLVerifyClient require .
W praktyce opcja ta ma niewielkie znaczenie, warto ją ustawić na względnie
dużą liczbę, w okolicach 10.
2.4. SSLRequire wyrażenie_logiczne
Jest to najbardziej rozbudowana opcja mod_ssl. W skrócie działa
ona tak, że jeżeli wyrażenie logiczne ma wartość true,
to klient uzyska dostęp do zasobów, jeżeli zaś false, to otrzyma
komunikat o braku dostępu.
Warunki mogą być bardzo rozbudowane - możemy używać w nich standardowej
składni shella bash , z pewnymi ograniczeniami opisanymi
szczegółowo w dokumentacji mod_ssl.
Oto opis składni warunku logicznego mod_ssl:
wyrażenie ::= "true" | "false"
| "!" wyrażenie
| wyrażenie "&&" wyrażenie
| wyrażenie "||" wyrażenie
| "(" wyrażenie ")"
| składnik
składnik ::= słowo "==" słowo | słowo "eq" słowo
| słowo "!=" słowo | słowo "ne" słowo
| słowo "<" słowo | słowo "lt" słowo
| słowo "<=" słowo | słowo "le" słowo
| słowo ">" słowo | słowo "gt" slowo
| słowo ">=" słowo | słowo "ge" słowo
| słowo "in" "{" lista "}"
| słowo "=~" wyrażenie_regularne
| słowo "!~" wyrażenie_regularne
lista ::= słowo
| lista "," słowo
słowo ::= cyfra
| łańcuch
| zmienna
| funkcja
cyfra ::= [0-9]+
łańcuch ::= "..."
zmienna ::= "%{"nazwa_zmiennej"}"
funkcja ::= nazwa_funkcji "(" argumenty ")"
Jak na razie jedyna dostępna funkcja to file(nazwa_pliku) ,
której wynikiem jest zawartość podanego pliku. Można ją np. porównać z wynikiem
ewaluacji wyrażenia regularnego. Dostępnych zmiennych natomiast jest
mnóstwo - większość z nich dotyczy konkretnych pól z certyfikatu
klienta lub serwera:
Standardowe zmienne Apache i CGI:
HTTP_USER_AGENT PATH_INFO AUTH_TYPE
HTTP_REFERER QUERY_STRING SERVER_SOFTWARE
HTTP_COOKIE REMOTE_HOST API_VERSION
HTTP_FORWARDED REMOTE_IDENT TIME_YEAR
HTTP_HOST IS_SUBREQ TIME_MON
HTTP_PROXY_CONNECTION DOCUMENT_ROOT TIME_DAY
HTTP_ACCEPT SERVER_ADMIN TIME_HOUR
HTTP:nagłówek SERVER_NAME TIME_MIN
THE_REQUEST SERVER_PORT TIME_SEC
REQUEST_METHOD SERVER_PROTOCOL TIME_WDAY
REQUEST_SCHEME REMOTE_ADDR TIME
REQUEST_URI REMOTE_USER ENV:zmienna
REQUEST_FILENAME
Dodatkowe zmienne związane z SSL:
HTTPS SSL_CLIENT_M_VERSION SSL_SERVER_M_VERSION
SSL_CLIENT_M_SERIAL SSL_SERVER_M_SERIAL
SSL_PROTOCOL SSL_CLIENT_V_START SSL_SERVER_V_START
SSL_SESSION_ID SSL_CLIENT_V_END SSL_SERVER_V_END
SSL_CIPHER SSL_CLIENT_S_DN SSL_SERVER_S_DN
SSL_CIPHER_EXPORT SSL_CLIENT_S_DN_C SSL_SERVER_S_DN_C
SSL_CIPHER_ALGKEYSIZE SSL_CLIENT_S_DN_ST SSL_SERVER_S_DN_ST
SSL_CIPHER_USEKEYSIZE SSL_CLIENT_S_DN_L SSL_SERVER_S_DN_L
SSL_VERSION_LIBRARY SSL_CLIENT_S_DN_O SSL_SERVER_S_DN_O
SSL_VERSION_INTERFACE SSL_CLIENT_S_DN_OU SSL_SERVER_S_DN_OU
SSL_CLIENT_S_DN_CN SSL_SERVER_S_DN_CN
SSL_CLIENT_S_DN_T SSL_SERVER_S_DN_T
SSL_CLIENT_S_DN_I SSL_SERVER_S_DN_I
SSL_CLIENT_S_DN_G SSL_SERVER_S_DN_G
SSL_CLIENT_S_DN_S SSL_SERVER_S_DN_S
SSL_CLIENT_S_DN_D SSL_SERVER_S_DN_D
SSL_CLIENT_S_DN_UID SSL_SERVER_S_DN_UID
SSL_CLIENT_S_DN_Email SSL_SERVER_S_DN_Email
SSL_CLIENT_I_DN SSL_SERVER_I_DN
SSL_CLIENT_I_DN_C SSL_SERVER_I_DN_C
SSL_CLIENT_I_DN_ST SSL_SERVER_I_DN_ST
SSL_CLIENT_I_DN_L SSL_SERVER_I_DN_L
SSL_CLIENT_I_DN_O SSL_SERVER_I_DN_O
SSL_CLIENT_I_DN_OU SSL_SERVER_I_DN_OU
SSL_CLIENT_I_DN_CN SSL_SERVER_I_DN_CN
SSL_CLIENT_I_DN_T SSL_SERVER_I_DN_T
SSL_CLIENT_I_DN_I SSL_SERVER_I_DN_I
SSL_CLIENT_I_DN_G SSL_SERVER_I_DN_G
SSL_CLIENT_I_DN_S SSL_SERVER_I_DN_S
SSL_CLIENT_I_DN_D SSL_SERVER_I_DN_D
SSL_CLIENT_I_DN_UID SSL_SERVER_I_DN_UID
SSL_CLIENT_I_DN_Email SSL_SERVER_I_DN_Email
SSL_CLIENT_A_SIG SSL_SERVER_A_SIG
SSL_CLIENT_A_KEY SSL_SERVER_A_KEY
SSL_CLIENT_CERT SSL_SERVER_CERT
SSL_CLIENT_CERT_CHAINn
SSL_CLIENT_VERIFY
Nie opisujemy tutaj znaczenia tych zmiennych, gdyż w większości
przypadków jest ono albo oczywiste, albo pokrywa się ze
znaczeniem analogicznej zmiennej środowiskowej, lub nagłówka HTTP.
3. Przykłady
Dostęp tylko dla ściśle określonych użytkowników wyłącznie przez SSL:
SSLRequireSSL
SSLVerifyClient require
SSLVerifyDepth 5
SSLRequire \
(%{SSL_CLIENT_S_DN_CN} in { "muflon" "perkins" }) \
and (%{SSL_CLIENT_S_DN_OU} == "WMiI") \
and (%{SSL_CLIENT_S_DN_OU} == "UMK") \
and (%{SSL_CLIENT_S_DN_O} == "MEN")
Dostęp tylko dla pracowników WMiI:
SSLRequireSSL
SSLVerifyClient require
SSLVerifyDepth 5
SSLRequire \
(%{SSL_CLIENT_S_DN_OU} == "staff") \
and (%{SSL_CLIENT_S_DN_OU} == "WMiI") \
and (%{SSL_CLIENT_S_DN_OU} == "UMK") \
and (%{SSL_CLIENT_S_DN_O} == "MEN")
Bardziej skomplikwoany przykład: dostęp tylko dla studentów w dniach
roboczych od godziny 8 do 20, z użyciem dwóch wyróżnionych protokołów
szyfrowania. Dodatkowo zdefiniowano "furtkę" pozwalającą na dostęp
z podsieci 158.75.2.* bez powyższych ograniczeń (ale wyłącznie dla
osób legitymujących się ważnym certyfikatem):
SSLRequireSSL
SSLVerifyClient require
SSLVerifyDepth 5
SSLRequire \
((%{SSL_CIPHER} !~ m/^(EXP|NULL)-/) \
and (%{SSL_CLIENT_S_DN_OU} == "WMiI") \
and (%{SSL_CLIENT_S_DN_OU} == "UMK") \
and (%{SSL_CLIENT_S_DN_O} eq "MEN") \
and (%{SSL_CLIENT_S_DN_OU} in {"studinfo" "studmat"}) \
and (%{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5) \
and (%{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20) \
) or (%{REMOTE_ADDR} =~ m/^158\.75\.2\.[0-9]+$/)
|