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 */
};

Brak komentarzy: