Прячем сайт за реверс-прокси Nginx

Все настройки выполнены в VPS от hostsailor.com

Сайт для статьи размещён на fornex.com

Nginx это не только web-сервер, но и кое что получше. Его можно использовать в качестве обратного прокси или реверс-прокси. Что это значит? Реверс-прокси это посредник между пользователем и реальным web-сервером. Это значит, мы можем скрыть от пользователя (или от того, сто фильтрует наш трафик ) настоящий адрес web-сервера, обеспечить доступ по https к серверу, который умеет только http, можем подменить домен web-сервера, можем фильтровать содержимое трафика как с целью подмены содержимого сайта, так и для защиты web-сервера от атак злодеев, можем кэшировать сайт для ускорения, можем балансировать нагрузку, можем… Да очень много можем! Например, сделать своё собственное зеркало RuTracker или любого другого сайта, к которому нет доступа обычным способом.

Если интересно, читаем дальше.

Всё описанное в этой статье приведено только в обучающих целях и не должно быть использовано для противоправных действий. Админитсрация сайта не несёт ответсвенность за действия третьих лиц, использующих инструкции из данной статьи. Все скриншоты и адреса сайтов в этой статье являются художественным вымыслом, совпадения с реальными адресами и сайтами случано.

Лучше всего показывать на реальных примерах. Нам понадобится VPS с минимальными характеристиками. Nginx не требователен к ресурсам. Главное, чтобы хватило лимитов трафика под Вашу задачу. В данной статье для скриншотов будет использоваться VPS с операционной системой CentOS 7. Для других операционных систем конфиги абсолютно аналогичны. Старайтесь использовать минимальную конфигурацию операционной системы без дополнительных пакетов.

Также нам понадобится домен. Можете использовать домен третьего уровня у какого-то своего домена, но лучше для таких дел арендовать отдельный новый платный домен, или арендовать бесплатный домен где-нибудь на freenom.com. Вы должны быть готовы к тому, что ваш домен добавят в реестр запрещенных сайтов. Это не должно затронуть какие-либо другие Ваши сервисы, кроме реверс-прокси. Для этого домена нужно добавить A запись с IP адресом нашего VPS. Как арендовать домен и настраивать DNS записи можно почитать здесь. Для примера в статье будет использоваться домен mydomain.com

Как арендовать и первично настроить VPS описано здесь, здесь и здесь. Должен быть разрешён доступ к серверу по портам tcp/22, tcp/80 и tcp/443. Если вы используете эту статью для настройки файрволла, то конфиг ipt-set будет выглядеть так:

#!/bin/sh
IF_EXT="venet0"
IPT="/sbin/iptables"
IPT6="/sbin/ip6tables"

# flush
$IPT --flush
$IPT -t nat --flush
$IPT -t mangle --flush
$IPT -X
$IPT6 --flush

# loopback
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

# default
$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP
$IPT6 -P INPUT DROP
$IPT6 -P OUTPUT DROP
$IPT6 -P FORWARD DROP

# allow forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# NAT
# #########################################
# SNAT - local users to out internet
$IPT -t nat -A POSTROUTING -o $IF_EXT -j MASQUERADE

# INPUT chain
# #########################################
$IPT -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
$IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# ssh
$IPT -A INPUT -i $IF_EXT -p tcp --dport 22 -j ACCEPT
# WEB
$IPT -A INPUT -i $IF_EXT -p tcp --dport 80 -j ACCEPT
$IPT -A INPUT -i $IF_EXT -p tcp --dport 443 -j ACCEPT

# OUTPUT chain
# #########################################
$IPT -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Обновим сервер:

yum -y update
yum -y install epel-release

Ставим собственно Nginx

yum -y install nginx

Пробуем запустить

systemctl start nginx

Теперь, если мы попробуем в браузере перейти по адресу нашего домена http://mydomain.com (вы, соответсвенно вводите своё имя домена), то должны увидеть страницу-заглушку. Типа такой:

Также можно проверить работоспособность службы командой:

systemctl status -l nginx

Вывод команды должен выглядеть так:

Если возникли проблемы и служба не запускается, то скорее всего вы используете не минимальный дистрибутив операционной системы и в системе уже установлен какой-то web-сервер, который занимает порт. Чтобы проверить это, установим сетевые утилиты

yum install net-tools -y

И проверим, кто занимает порт

netstat -tulnp | grep 80

В выводе команды вы можете увидеть, например:

tcp 	0 	0 :::80 	:::* 		LISTEN 	104/httpd

Это значит, что установлен Apache и запущен его демон httpd. Убираем это командами:

systemctl stop httpd
systemctl disable httpd

А потом пробуем запустить службу Nginx как описано выше. Если всё хорошо, добавляем её в автозагрузку:

systemctl enable nginx

Переходим к конфигу Nginx. Откройте файл /etc/nginx/nginx.conf

Напомню, все манипуляции с фалами я делаю с помощью программ WinSCP, как это описано в этой статье. В качестве редактора рекомендую использовать программу Notepad++. Установите её на свой компьютер и укажите в настройках WinSCP путь к редактору в меню Options->Preferences->Editors. Он должен быть первым в списке для использования по умолчанию.

В конфиге уже есть секция

server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

Именно эта секция выводит страницу-заглушку. А вот ниже есть закоментированный пример секции https (порт 443) страницы-заглушки. Крайне желательно эту секцию сделать работающей. Нам понадобятся сертификаты. Установим certbot и получим сертификат командами:

yum -y install certbot python2-certbot-nginx
certbot certonly

На запрос:

Вводим 1

На запрос:

Вводим свой действующий e-mail. На него будут приходить в уведомления. Например, если срок сертификата истекает и его необходимо продлить.

Напомню:  Let’s Encrypt выдаёт сертифакты со сроком действия три месяца.

Далее принимаем соглашение и отказываемся от рассылок, вводим, соответственно, A и N

Дальше нужно ввести имя Вашего домена. В моём случае это mydomain.com. И нажать Enter. Если у Вас настроена A запись, как указано в начале статьи, и прошло достаточно времени (запись должна прореплицироваться на все DNS-сервера, это можета занимать до 24-х часов, дожен пинговаться адрес домена), то будет получен сертификат и мы получим вывод в консоли:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/mydomain.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/mydomain.com/privkey.pem
   Your cert will expire on 2020-02-26. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

Заметьте, тут сразу есть пути к файлам сертификата и их можно очень удобно скопировать в конфиг. Возвращаемся к файлу /etc/nginx/nginx.conf, раскоментируем секцию для https и исправим пути на сертификат. Получится так:

# Settings for a TLS enabled server.

    server {
        listen       443 ssl http2 default_server;
        listen       [::]:443 ssl http2 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        ssl_certificate "/etc/letsencrypt/live/mydomain.com/fullchain.pem";
        ssl_certificate_key "/etc/letsencrypt/live/mydomain.com/privkey.pem";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

Сохраним файл и перезапустим службу Nginx. Перезапускать службу нужно каждый раз, когда вы меняете конфиги Nginx.

systemctl restart nginx

И проверяем, что старница-заглушка доступна в браузере по адресу https://mydomain.com (вы вводите своё имя домена).

В конфиге /etc/nginx/nginx.conf обратите внимание на строчку

include /etc/nginx/conf.d/*.conf;

Это значит, что если мы в папке /etc/nginx/conf.d/ разместим файл с расширением .conf, то он будет включен в основной конфиг и выполнен вместе с ним. Так и будем делать. Файл /etc/nginx/nginx.conf закрываем и больше не трогаем.

Теперь определим, к чему мы будем проксировать трафик. Например, я хочу чтоб при переходе по адресу test.mydomain.com, во-первых, происходила переадресация на адрес https://test.mydomain.com (мы за безопасность и шифрование), а во-вторых, открывался сайт eng.rkn.gov.ru.

Сначала создадим домен третьего уровня вида test.mydomain.com

Категорически рекомендую использовать для проксирования только домены третьего уровня. Если какому-то регулятору не понравится то, что у вас по этому адресу расположено, он забанит именно домен третьего уровня и не забанит домен второго уровня. По крайней мере, скорее всего не забанит :).

Т.е. создадим на DNS-сервере А запись test (вы подставляете своё имя) с тем же IP-адресом, что и основной домен mydomain.com (вы подставляете своё имя). Дождитесь, чтобы запись прореплицировалась по DNS-серверам. Можно проверить, что домен пингуется командой на VPS:

ping test.mydomain.com -c 3

Должны корректно пройти три пинга

PING test.mydomain.com (185.nnn.nnn.nnn) 56(84) bytes of data.
64 bytes from vpsNNNN.hostsailor.com (185.nnn.nnn.nnn): icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from vpsNNNN.hostsailor.com (185.nnn.nnn.nnn): icmp_seq=2 ttl=64 time=0.019 ms
64 bytes from vpsNNNN.hostsailor.com (185.nnn.nnn.nnn): icmp_seq=3 ttl=64 time=0.019 ms

--- test.secfall.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.019/0.023/0.031/0.005 ms

В папке /etc/nginx/conf.d/ создаём файл с именем вида test.mydomain.com.conf следующего содержания:

server {
    listen 80;
	server_name test.mydomain.com;
	location /.well-known/acme-challenge {
            alias /usr/share/nginx/html/.well-known/acme-challenge;
    }
	location / {
		return 301 https://$server_name$request_uri;
	}
}

Перезапускаем службу Nginx и, с помощью команды certbot certonly, делаем сертификат для нашего домена третьего уровня test.mydomain.com (подставляете свои имена).

Если сертификат получен, дописываем (т.е. добавляем с новой строчки в конце) в файл /etc/nginx/conf.d/test.mydomain.com.conf секцию для https:

server {
	listen 443 ssl;
	server_name test.mydomain.com;

	access_log /var/log/nginx/test.mydomain.com.access.log;
	error_log /var/log/nginx/test.mydomain.com.error.log;

	client_max_body_size 0;
	underscores_in_headers on;

	ssl_certificate /etc/letsencrypt/live/test.mydomain.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/test.mydomain.com/privkey.pem;

	ssl_stapling on;
	ssl_stapling_verify on;
	
	location /.well-known/acme-challenge {
            alias /usr/share/nginx/html/.well-known/acme-challenge;
    }

	location / {     
		proxy_set_header Host eng.rkn.gov.ru;
		add_header Front-End-Https on;
		add_header Referer-Policy no-referrer;
		proxy_pass http://eng.rkn.gov.ru;
	}
}

Рассмотрите внимательно конфиг. В нём test.mydomain.com вы заменяете на своё имя домена, а eng.rkn.gov.ru на имя того сервера, который будете проксировать. И обратите внимание, что в данном конфиге к eng.rkn.gov.ru сервер обращается по http, а нам отдаёт по https. Такое полезно, когда web-сервер расположен где-то во внутренней инфраструктуре и нет необходимости или возможности прикрутить к нему https.

Например, есть некий сервис который вообще не умеет в https. Мы устанавливаем его на сервер, привязываем к адресу localhost и к порту, например, 8080. Теперь к сервису нельзя обратится извне, а только «изнутри» сервера по адресу http://localhost:8080. На этот же сервер ставим реверс-прокси, как написано выше, и указываем 

proxy_pass http://localhost:8080;

Т.е. Nginx будет обращаться к этому же серверу на localhost к нашему сервису по порту 8080. А нам будет отдавать нормальный https. Да ещё всякой фильтрации для безопасности можно навесить, чтобы помешать злодеям ломать наш сервис. Но об этом ниже.

Сохраняем файл, перезапускаем службу Nginx. Возможно вы где-то накосячили и ошиблись. Тогда командой

systemctl status -l nginx

смотрим, в чём ошибка. Обычно Nginx достаточно внятно пишет, что ему не нравиться, и даже укавает номер строки конфига, где есть ошибка.

Если служба запустилась без ошибок, то в браузере открываем адрес test.mydomain.com Получится как-то так:

 

Обратите внимание, что хотя мы не писали в адресе https, сервер сам перевёл нас на зашифрованный трафик. Всё, мы сделали зеркало сайта! Дальше можно дорабатывать конфиг. Например добавить строчку

proxy_cookie_domain rkn.gov.ru $host;

proxy_pass_header Set-Cookie;

Это позволит обеспечить корректную работу куков и нормальную авторизацию на проксируемом сайте. Получится так (показан кусок конфига, только с параметрами проксирования):

	location / {     
		
                proxy_cookie_domain eng.rkn.gov.ru $host;
                proxy_pass_header Set-Cookie;

                proxy_set_header Host eng.rkn.gov.ru;
                add_header Front-End-Https on;
                add_header Referer-Policy no-referrer;
                proxy_pass http://eng.rkn.gov.ru;
	}

Можно включить фильтрацию текста. Например заменять слово «Roskomnadzor» на «Roskompozor». Нужно дописать пару строчек (показан кусок конфига, только с параметрами проксирования):

	location / {     
                proxy_cookie_domain rkn.gov.ru $host;
                proxy_pass_header Set-Cookie;
                proxy_set_header Host eng.rkn.gov.ru;
                add_header Front-End-Https on;
                add_header Referer-Policy no-referrer;
                proxy_pass http://eng.rkn.gov.ru;
		
                sub_filter "Roskomnadzor" "Roskompozor";
                sub_filter_once off;
	}

Директива

sub_filter «Roskomnadzor» «Roskompozor»;

показывает что на что менять.

Директива

sub_filter_once off;

говорит серверу, что заменить нужно все найденные вхождения. Если не указать, заменит только первое совпадение.

Сохряняем конфиг, перезапускаем службу Nginx и с помощью комбинации Ctrl+F5 (чтобы игнорировать кэш браузера) обновляем страницу в браузере. Получится как-то так 🙂

Теперь про фильтрацию заголовков. Это нужно делать, если проксируете трафик к какому-то своему сервису и нужно его дополнительно защитить. Я покажу часть конфига с проксированием со всеми основными примочками безопасности (кусок с ними выделен линиями). Я не буду расписывать — для чего они. Это тема отдельной статьи. Если интересно — легко сможете нагуглить.

location / {     
		proxy_cookie_domain rkn.gov.ru $host;
		proxy_pass_header Set-Cookie;
		proxy_set_header Host eng.rkn.gov.ru;
		#------------------------------------------------------------------------------------------------------------------------------------------
		proxy_headers_hash_max_size 512;
		proxy_headers_hash_bucket_size 64;
		proxy_set_header Accept-Encoding "";
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
		add_header X-Content-Type-Options nosniff;
		add_header X-XSS-Protection "1; mode=block";
		add_header Content-Security-Policy "default-src 	'self'; 
											frame-ancestors							https://framer.mozilla.org; 
											form-action 	'self'; 
											base-uri 		'self'; 
											script-src 		'self' 					https://mc.yandex.ru https://ssl.google-analytics.com; 
											img-src 		'self'					https://mc.yandex.ru https://ssl.google-analytics.com; 
											style-src 		'self'	'unsafe-inline'	https://mc.yandex.ru https://fonts.googleapis.com;
											font-src 		'self'					https://mc.yandex.ru https://themes.googleusercontent.com; 
											frame-src 								https://mc.yandex.ru; 
											object-src 'none'
											";
		add_header X-Frame-Options "DENY";
		proxy_cookie_path / "/; HTTPOnly; Secure";
		#------------------------------------------------------------------------------------------------------------------------------------------
				add_header Front-End-Https on;
		add_header Referer-Policy no-referrer;
		proxy_pass http://eng.rkn.gov.ru;
	}

Если Вам нужно добавить ещё одно зеркало, то порядок действий такой:

  1. Добавляет новую A запись с доменом третьего уровня на DNS-сервере. Ждем, пока не начнёт пинговаться с нашего VPS.
  2. Создаем ещё один файл конфига в папке /etc/nginx/conf.d/ с секцией для 80 порта (для http) и перезапускаем Nginx.
  3. Делаем сертификаты с помощью certbot.
  4. Дописываем секцию для https в конфиг. Указывем пути на сертификаты. Перезапускаем Nginx.

Еще можно добавить ключ для алгоритма Диффи-Хелмана. Настроить автообновлене сертификатов. Как сделать, описано в этой статье.

 

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>