sobota, 22 lutego 2014

Terminacja SSL/TLS i Apache (przekierowania na katalogi)


Co się stanie gdy wywołamy taki adres?
 http://adres.pl/katalog
Obsługując taki adres serwer Apache spróbuję udostępnić plik o nazwie /katalog (względem DocumentRoot). Gdy się okaże, że pod tą nazwą w systemie plików znajduję się katalog serwer wykona przekierowanie (HTTP 301) na adres:
http://adres.pl/katalog/
W przekierowaniu jest podany adres bezwzględny (pełny URL). Przy tworzeniu pełnego adresu Apache ustawia  rodzaj protokołu (scheme). Domyślna wartość dla scheme to http. Gdy aktywny jest jakiś moduł SSL/TLS (np. mod_ssl) dla vhostów z obsługą SSL-a zwracana jest wartość https.

Na problem możemy natrafić gdy terminacja SSL/TLS obywa się w innym miejscu niż Apache (przykład terminacji na serwerze Nginx). W takiej sytuacji komunikacja serwera proxy z  serwerem Apache odbywa się najczęściej po "czystym" protokole HTTP i  Apache wygeneruję przekierowanie bezwzględne zawierające http://.

Najprostszym rozwiązaniem tego problemu w przypadku Nginx jest wykorzystanie dyrektywy  proxy_redirect. Pozwala ona  na podmianę  w odpowiedzi z backendu np. http:// na https://. Wykorzystując to rozwiązanie  może być trudno wykonać celowe przekierowanie  z poziomu backendu (np. aplikacji php) na adres http:// - może ono zostać zamienione na https://.

Poniższy bardzo prosty moduł dla Apache (dla wersji 2.2.x) rozwiązuję ten problem. Rozwiązanie opiera się na przekazaniu przez serwer proxy nagłówka  (X-Forwarded-Proto) informującego jaki protokół został użyty przy połączeniu. Poniższy moduł po wykryciu takiego nagłówka "informuję" serwer Apache o odpowiednim typie protokołu (zmienia nazwę dla scheme) dla danego requesta.
Moduł jest bardzo prosty. Posiada zaszyte informacje o nazwie nagłówka i dopuszcza tylko zmianę jeśli request został wywołany z adresu 127.0.0.1. Moduł celowo nie obsługuję żadnych parametrów konfiguracyjnych - jeśli moduł zostanie wyłączony konfiguracja serwera Apache będzie dalej prawidłowa (nie będzie nieznanych dyrektyw konfiguracyjnych).

Poniżej kod źródłowy modułu (w języku C) i pakiety RPM.

Pakiet RPM dla CentOS 6.x x86_64
Pakiet SRPMS

/*
 * mod_rpsm.c (Reverse Proxy Scheme Module)
 * Version: 1.0
 * License: Public Domain
 *
 * Author: Robert Socha
 * EMail: socha@socha.it
 *
 * Simple module to set Apache scheme (https or http) when request are coming from 
 * reverse proxy/ssl terminator (eg. Nginx)
 * No configuration directives - pass X-Forwarded-Proto header from proxy 
 * (and set it to https)
 *
 * Compile: apxs -c mod_rpsm.c
 *
 */

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

module AP_MODULE_DECLARE_DATA rpsm_module;

static const char *rpsm_http_scheme(const request_rec *r) {
const char *xfproto;
 if( (strcmp(r->connection->remote_ip,"127.0.0.1")==0) &&
     (xfproto = apr_table_get(r->headers_in, "X-Forwarded-Proto")) &&
     (strcmp(xfproto,"https") == 0)) {
  return "https";
 }
 return NULL;
}

static void rpsm_register_hooks(apr_pool_t *p) {
 ap_hook_http_scheme(rpsm_http_scheme, NULL, NULL, APR_HOOK_MIDDLE);
}
  
module AP_MODULE_DECLARE_DATA rpsm_module = {
 
STANDARD20_MODULE_STUFF,
 NULL,              /* dir config creater */
 NULL,              /* dir merger --- default is to override */
 NULL,              /* server config */
 NULL,              /* merge server configs */
 NULL,              /* command apr_table_t */
 rpsm_register_hooks, /* register hooks */
};

środa, 19 lutego 2014

Nginx TLS Proxy do backendu na HTTPD Apache

Przykładowa konfiguracja terminacji SSL/TLS po stronie Nginx.

Konfiguracja po stronie Nginx

    server {
            listen       443 default_server ssl;
            server_name  servername;
            ssl_certificate      /etc/pki/tls/custom/server.pem;
            ssl_certificate_key  /etc/pki/tls/custom/server.key;
            ssl_session_timeout  5m;
            ssl_prefer_server_ciphers   on;
            ssl_session_cache shared:SSL:10m;
            ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
            ssl_dhparam /etc/pki/tls/dh/dh.pem;
            ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
            ssl_stapling on;
            ssl_stapling_verify on;
            ssl_trusted_certificate /etc/pki/tls/certs/custom/oscp-bundle.pem;
            resolver 8.8.8.8 valid=300s;
            resolver_timeout 10s;
            location / {
                    proxy_pass http://127.0.0.1;
                    proxy_http_version 1.1;
                    proxy_set_header X-Forwarded-For $remote_addr;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_set_header X-SSL-Protocol $ssl_protocol;
                    proxy_set_header X-SSL-Cipher $ssl_cipher;
                    proxy_set_header Host $host;
                    proxy_max_temp_file_size 0;
            }
    }
Konfiguracja po stronie Apache

    LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" vcombined
    LogFormat "%V %{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{X-SSL-Protocol}i %{X-SSL-Cipher}i" vproxy
    SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
    SetEnvIf X-Forwarded-Proto "^https$" HTTPS=on
    CustomLog "access.log" vcombined env=!forwarded
    CustomLog "access.log" vproxy env=forwarded

Wersja BIND


dig @nameserver version.bind txt chaos

nslookup -type=txt -class=chaos version.bind nameserver