httpd, проксирование в значение {QUERY_STRING}

Небольшие заметки из личного (и не только) опыта, рекомендации
Ответить
Аватара пользователя
Raven
Бородатый сис
Бородатый сис
Сообщения: 2788
Зарегистрирован: 03 мар 2010, 15:12
ОС: RHEL 7
Откуда: Из серверной

httpd, проксирование в значение {QUERY_STRING}

Сообщение Raven » 10 июл 2013, 16:20

Понадобилось недавно сделать хитрожопый финт ушами - наши чудо-программисты написали супер-пупер крутую прогу, которой в обязалову потребовалось работать через собственный прокс (ибо в обязательном порядке SSL ннада!) на кучу серваков в локалке . Часть отвечающую за передачу динамического контента они написали сами, на PHP с использованием curl (держись прокся - северный пушной зверек крадется медленно, но верно), отдачу статики же было решено поручить веб-серверу. В общем самая хитрость заключалась в следующем - url выглядят следующим образом:

Код: Выделить всё

http://frontend.tld/?url=http://backend.tld/some/path/file.ext
То есть адрес файла вместе с адресом сервера передается в качестве аргумента к url= . Чтож, mod_rewrite в помощь, закручиваем хорошую самокрутку манов и курим-курим-курим! Я в итоге накурил до:

Код: Выделить всё

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} ^url=(.*)$ [NC]
RewriteRule .* %1 [P,L]
добавленное в .htaccess корневой директории домена оно в принципе справлялось достаточно хорошо. Однако вскоре случился затык - кое-где на бэкендах имелись файлы с пробелами в названии (криворукость итернет-пользователей заставляет подозревать что человеческая раса вымрет быстро). И естественно они не загружались. Оказалось что url содержащий пробел кодировался - пробел заменялся на '%20'. Казалось-бы, что в этом такого? Нормальное поведение браузера... Да не тут-то было! Веб-сервер при передаче запроса на проксирование, углядывал в запросе небезопасный по его мнению символ '%' и... кодировал его от греха подальше :-D Тупо заменял на '%25'! В конце-концов бедолага пробел выглядел уже как '%2520' и понять что это за покемон уже никто не был способен. В общем ситуацию удалось разрешить добавлением флага NE (NoEncode) в RewriteRule

Код: Выделить всё

RewriteRule .* %1 [P,L,NE]
Правда радости это много не принесло - аналогичная проблема всплыла и с закодированными слешами и др. спецсимволами. А причина вся вот в чем - правила rewrite составлены таким образом, что если url= имеет аргументы, то присваиваются переменной %1 и содержимое всего запроса меняется на нее и передается в прокси КАК ЕСТЬ! А прокси не разрешает использование закодированных символов в обозначении хоста - то есть можно использовать кодированные символы только в {REQUEST_URI}, {QUERY_STRING} - то есть после http://backend.tld/. У меня же кодировалась вся строка запроса http%3A%2F%2Fbackend.tld%2F.
Перекурив маны еще раз вдоль и поперек нашел весьма интересную директиву - RewriteMap, позволяющую запускать для рерайта внешние обработчики. Быстренько сообразив что к чему накатал простенький скрипт на perl

Код: Выделить всё

#!/usr/bin/perl

use URI::Escape;

$| = 1;
while (<STDIN>) {

    print uri_unescape($_);
}
Скрипт был назван decode.pl и помещен в папку с конфигами апача (заострю внимание на том, что скрипту требуется модуль URI::Escape, поставить можно через cpan).

далее в конфиге виртуалхоста добавляем строку

Код: Выделить всё

RewriteMap  decode prg:/usr/local/etc/apache22/decode.pl
а в .htaccess приводим правила рерайта к следующему виду:

Код: Выделить всё

RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} ^url=(.*)$ [NC]
RewriteRule .* ${decode:%1} [P,L,NE]
то есть, прежде чем передать строку в прокси-сервер, рерайт прогоняет ее через перловый скрипт слушающий stdin и заменяющий все кодированные символы на обычные. Profit!

UPD: Не, не profit! На боевом сервере оказался ужасно старючий перл, который при попытке собрать модуль URI::Escape возжелал обновиться. Ждать пока сие чудо скомпилится на серваке эквивалентном PIII желания особого не было, но к счастью там обнаружился python, нормальной версии 2.6!!! Спасибо gen1us2k за за помощь в конвертировании скрипта в python - все воркает замечательно!

Код: Выделить всё

#!/usr/bin/env python
from urllib2 import unquote
import sys

while 1:
    request = sys.stdin.readline().strip()
    sys.stdout.write('%s\n' % unquote(request))
    sys.stdout.flush()
Я не злопамятный, я просто часто ковыряю логи
Изображение
Аватара пользователя
Raven
Бородатый сис
Бородатый сис
Сообщения: 2788
Зарегистрирован: 03 мар 2010, 15:12
ОС: RHEL 7
Откуда: Из серверной

Re: httpd, проксирование в значение {QUERY_STRING}

Сообщение Raven » 12 июл 2013, 10:18

Да, кстати. Вот теперь вопрос - мож кто знает как отсечь передачу query_string в запросе к бэкенду? Просто проанализировав логи на серверах назначения обнаружил интересную картинку - запросы приходят в таком виде:

Код: Выделить всё

/some/path/file.ext?url=http://backend.tld/some/path/file.ext
Для серверов отдающих статику это конечно не очень критично, но тут уже чисто мои эстетические принципы - все должно быть чисто!
Я не злопамятный, я просто часто ковыряю логи
Изображение
Аватара пользователя
Gen1us2k
Модератор
Модератор
Сообщения: 771
Зарегистрирован: 02 мар 2010, 16:13

Re: httpd, проксирование в значение {QUERY_STRING}

Сообщение Gen1us2k » 13 авг 2013, 20:51

Raven
try:
sys.stdout.write('%s\n' % unquote(request.split('?')[1]))
except Exception:
sys.stdout.write('%s\n' % unquote(request))
Изображение
Home: Windows Heaven
Home: Debian 6
For Servers: Debian || RHEL Based || Gentoo || FreeBSD
Ответить

Вернуться в «Полезные советы»