Section author: Vedran Miletić

Konfiguracija HTTPS-a u web poslužitelju Apache HTTP Server

Modul mod_ssl implementira podršku za SSLv3 i TLSv1.x u Apacheju (SSLv2 više nije podržan jer se smatra nesigurnim) koji se koriste kod ostvarivanja HTTPS veza. Za izvođenje kriptografskih algoritama koristi se OpenSSL.

Hint

Više detalja o SSL-u i TLS-u u Apacheju moguće je pronaći u službenoj dokumentaciji u dijelu Apache SSL/TLS Encryption

Stvaranje i postavljanje certifikata i privatnog ključa

Stvorimo samopotpisani certifikat server.crt koji traje 30 dana i njegov pripadni tajni ključ server.key (takva imena datoteka će nam trebati kasnije):

$ openssl req -x509 -nodes -days 30 -newkey rsa:4096 -keyout server.key -out server.crt
Generating a RSA private key
................................................................................................................................................................++++
......................................................................................................................................................................................................++++
writing new private key to 'server.key'
-----
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) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:

Dodajmo dvije nove naredbe COPY u Dockerfile kojima ćemo kopirati certifikat i tajni ključ u direktorij /usr/local/apache2/conf unutar slike:

FROM httpd:2.4
COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf
COPY ./www /var/www
COPY server.crt /usr/local/apache2/conf
COPY server.key /usr/local/apache2/conf

U datoteci my-httpd.conf odkomentirajmo linije:

(...)
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
(...)
#LoadModule ssl_module modules/mod_ssl.so
(...)
#Include conf/extra/httpd-ssl.conf
(...)

Modul mod_ssl za svoj rad zahtijeva modul mod_socache_shmcb pa prva od tri linije učitava taj modul. Druga linija učitava mod_ssl, a treća njegovu konfiguraciju u kojoj se nalaze naredbe kao što je Listen 443 koja uključuje HTTPS na vratima 443. Konfiguraciju ćemo nešto kasnije uređivati.

Izgradimo sliku i pokrenimo kontejner:

$ sudo docker build -t "my-httpd:2.4-3" .
Sending build context to Docker daemon  32.26kB
Step 1/5 : FROM httpd:2.4
---> b2c2ab6dcf2e
Step 2/5 : COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf
---> 765440898954
Step 3/5 : COPY ./www /var/www
---> a0c0158083fb
Step 4/5 : COPY server.key /usr/local/apache2/conf
---> 6ffd3334243b
Step 5/5 : COPY server.crt /usr/local/apache2/conf
---> d5b1f004015f
Successfully built d5b1f004015f
Successfully tagged my-httpd:2.4-3
$ sudo docker run my-httpd:2.4-3
[Sun May 10 17:02:53.475776 2020] [ssl:warn] [pid 1:tid 140698297431168] AH01906: www.example.com:443:0 server certificate is a CA certificate (BasicConstraints: CA == TRUE !?)
[Sun May 10 17:02:53.476093 2020] [ssl:warn] [pid 1:tid 140698297431168] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
[Sun May 10 17:02:53.478932 2020] [ssl:warn] [pid 1:tid 140698297431168] AH01906: www.example.com:443:0 server certificate is a CA certificate (BasicConstraints: CA == TRUE !?)
[Sun May 10 17:02:53.478939 2020] [ssl:warn] [pid 1:tid 140698297431168] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
[Sun May 10 17:02:53.480043 2020] [mpm_event:notice] [pid 1:tid 140698297431168] AH00489: Apache/2.4.43 (Unix) OpenSSL/1.1.1d configured -- resuming normal operations
[Sun May 10 17:02:53.480070 2020] [core:notice] [pid 1:tid 140698297431168] AH00094: Command line: 'httpd -D FOREGROUND'

Ukoliko se kod izdavanja certifikata pod Common Name unese apache-primjer.rm.miletic.net, upozorenja server certificate does NOT include an ID which matches the server name ne bi trebalo biti. Ovo drugo upozorenje, server certificate is a CA certificate (BasicConstraints: CA == TRUE !?), Apache daje zato što je certifikat samopotpisan. Zbog toga kod testiranja naredba curl treba parametar -k i imamo:

$ curl -k https://172.17.0.2/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
</body></html>

Konfiguracijske naredbe HTTPS poslužitelja

Vidimo da HTTPS poslužitelj odgovara na upit, ali ne sadržajem datoteke index.html koji smo se nadali dobiti. Na strani poslužitelja ispisuju se poruke o pogreškama:

[Sun May 10 17:02:56.688057 2020] [authz_core:error] [pid 9:tid 140698145773312] [client 172.17.0.1:54160] AH01630: client denied by server configuration: /usr/local/apache2/htdocs/
172.17.0.1 - - [10/May/2020:17:02:56 +0000] "GET / HTTP/1.1" 403 199

Morat ćemo konfigurirati DocumentRoot i za HTTPS poslužitelj. Dohvatimo konfiguracijsku datoteku:

$ sudo docker run --rm httpd:2.4 cat /usr/local/apache2/conf/extra/httpd-ssl.conf > my-httpd-ssl.conf

Uredimo datoteku; uočimo da u njoj postoji niz konfiguracijskih naredbi koje počinju nizom slova SSL i određuju način rada SSL-a. Naredbom SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES (dokumentacija) navode se dozvoljeni šifrarnici. Podsjetimo se da se kod SSL-a koriste četiri vrste šifrarnika:

  • algoritam za razmjenu ključeva: RSA, Diffie-Hellman, Elliptic Curve Diffie-Hellman, Secure Remote Password

  • algoritam za autentifikaciju: RSA, Diffie-Hellman, DSS, ECDSA, a specijalno može i nijedan

  • algoritam za šifrirane poruka: AES, DES, Triple-DES, RC4, RC2, IDEA, itd.

  • algoritam za računanje sažetka koda autentifikacije poruke: MD5, SHA ili SHA1, SHA256, SHA384

Prve dvije vrste algoritma se u nekim podjelama šifrirarnika navode zajedno pa onda kažemo da su tri različite vrste šifrarnika. U našem slučaju vrijednost HIGH:MEDIUM:!MD5:!RC4:!3DES ima značenje:

  • HIGH – svi šifrarnici koji koriste trostruki DES ili bolji algoritam (npr. AES)

  • MEDIUM – svi šifrarnici koji koriste 128-bitno ili bolje šifriranje

  • !MD5 – ne MD5

  • !RC4 – ne RC4

  • !3DES – ne trostruki DES (bolji algoritmi od trostrukog DES-a su dozvoljeni)

Note

Za današnje standarde korištenje ovoliko širokog skupa šifrarnika (koji onda uključuje i neke slabe šifrarnike) ne bi bilo preporučeno. Popise preporučenih šifrarnika možete pročitati na Remy van Elstovom blogu u članku Strong SSL Security on Apache2 i na Mozillinom wikiju u članku Security/Server Side TLS. Pored toga, Mozilla je razvila generator konfiguracija SSL-a koji je vrlo jednostavan za korištenje, a podržava i Apache.

Naredbom openssl ciphers možemo provjeriti o kojim se točno šifrarnicima radi:

$ openssl ciphers -v 'HIGH:MEDIUM:!MD5:!RC4:!3DES'
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
DHE-DSS-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=DSS  Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
(...)
PSK-CAMELLIA128-SHA256  TLSv1 Kx=PSK      Au=PSK  Enc=Camellia(128) Mac=SHA256
DHE-RSA-SEED-SHA        SSLv3 Kx=DH       Au=RSA  Enc=SEED(128) Mac=SHA1
DHE-DSS-SEED-SHA        SSLv3 Kx=DH       Au=DSS  Enc=SEED(128) Mac=SHA1
ADH-SEED-SHA            SSLv3 Kx=DH       Au=None Enc=SEED(128) Mac=SHA1
SEED-SHA                SSLv3 Kx=RSA      Au=RSA  Enc=SEED(128) Mac=SHA1

Note

Skup algoritama za šifriranje čije korištenje se preporuča kod rada sa TLS/SSL certifikatima mijenja se iz godine u godinu kako se pronalaze sigurnosni propusti u njima i kako procesna moć računala raste pa je dobro kod postavljanja TLS-a/SSL-a provjeriti aktualne najbolje prakse, primjerice one koje navodi Qualys SLL Labs, autor SSL Server Testa i SSL Client Testa.

Naredba SSLProtocol all -SSLv3 (dokumentacija) definira dozvoljene verzije TLS-a i SSL-a. Vrijednost all -SSLv3 uključuje TLSv1, TLSv1.1, TLSv1.2, TLSv1.3, a isključuje SSLv3.

Naredbe SSLCertificateFile "/usr/local/apache2/conf/server.crt" i SSLCertificateKeyFile "/usr/local/apache2/conf/server.key" navode certifikat i pripadni tajni ključ koji se koriste (respektivno).

Osim konfiguracijskih naredbi koje počinju nizom slova SSL, unutar bloka <VirtualHost></VirtualHost> vidimo i već poznate naredbe DocumentRoot, ServerName i ServerAdmin:

<VirtualHost _default_:443>

#   General setup for the virtual host
DocumentRoot "/usr/local/apache2/htdocs"
ServerName www.example.com:443
ServerAdmin you@example.com
(...)
</VirtualHost>

Detaljnije se načinom rada virtualnih domaćina (naredba <VirtualHost>, dokumentacija <https://httpd.apache.org/docs/2.4/mod/core.html#virtualhost>) bavimo drugdje. Postavimo DocumentRoot "/var/www/html" i ServerName apache-primjer.rm.miletic.net:443, a ServerAdmin na vlastitu e-mail adresu. Dodajmo u Dockerfile kopiranje izmijenjene datoteke na odgovarajuće mjesto:

FROM httpd:2.4
COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf
COPY ./www /var/www
COPY server.crt /usr/local/apache2/conf
COPY server.key /usr/local/apache2/conf
COPY ./my-httpd-ssl.conf /usr/local/apache2/conf/extra/httpd-ssl.conf

Izgradimo sliku i pokrenimo Docker kontejner:

$ sudo docker build -t "my-httpd:2.4-4" .
Sending build context to Docker daemon  72.19kB
Step 1/6 : FROM httpd:2.4
---> b2c2ab6dcf2e
Step 2/6 : COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf
---> Using cache
---> 765440898954
Step 3/6 : COPY ./www /var/www
---> Using cache
---> a0c0158083fb
Step 4/6 : COPY server.crt /usr/local/apache2/conf
---> baafa15fa976
Step 5/6 : COPY server.key /usr/local/apache2/conf
---> eebe8db06676
Step 6/6 : COPY ./my-httpd-ssl.conf /usr/local/apache2/conf/extra/httpd-ssl.conf
---> 0514261bb6fe
Successfully built 0514261bb6fe
Successfully tagged my-httpd:2.4-4
$ sudo docker run my-httpd:2.4-4
[Sun May 10 18:57:41.346009 2020] [ssl:warn] [pid 1:tid 139678405715072] AH01906: www.example.com:443:0 server certificate is a CA certificate (BasicConstraints: CA == TRUE !?)
[Sun May 10 18:57:41.346345 2020] [ssl:warn] [pid 1:tid 139678405715072] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
[Sun May 10 18:57:41.349298 2020] [ssl:warn] [pid 1:tid 139678405715072] AH01906: www.example.com:443:0 server certificate is a CA certificate (BasicConstraints: CA == TRUE !?)
[Sun May 10 18:57:41.349305 2020] [ssl:warn] [pid 1:tid 139678405715072] AH01909: www.example.com:443:0 server certificate does NOT include an ID which matches the server name
[Sun May 10 18:57:41.350403 2020] [mpm_event:notice] [pid 1:tid 139678405715072] AH00489: Apache/2.4.43 (Unix) OpenSSL/1.1.1d configured -- resuming normal operations
[Sun May 10 18:57:41.350429 2020] [core:notice] [pid 1:tid 139678405715072] AH00094: Command line: 'httpd -D FOREGROUND'

Uvjerimo se da sada radi ispravno:

$ curl -k https://172.17.0.2/
<html><body><h1>Radi!</h1></body></html>

Hint

U praksi je ponekad moguće automatizirati čitav ovdje opisani postupak. Naime, Apache od verzije 2.4.30 nadalje sadrži mod_md koji dohvaća certifikate s besplatnog i otvorenog autoriteta certifikata Let’s Encrypt za domene navedene pod konfiguracijskom naredbom MDomain. Nažalost, taj postupak ovdje ne možemo koristiti jer Let’s Encrypt zahtijeva da imamo registrirane domene na internetu kako bi mogao izdati certifikat za njih. Pored toga, čak i kad možemo automatizirati postavljanje certifikata, dobro je znati kako čitav postupak izgleda kako bismo se mogli snaći u situacijama kad neki dio tog automatiziranog postupka zakaže.