Проксирование c помощью nginx

или как создать зеркало интернет-сайта в сети Yggdrasil

Здесь будет описан способ создания зеркала сайта в сети Yggdrasil с использованием возможности web-сервера nginx работать в качестве прокси-сервера.

Всё описанное доступно для реализации как под управлением ОС Linux, так и Windows.
Мы рассмотрим конфигурирование nginx, установленного в OC linux (на базе Debian).

Для создания корректно функционирующего зеркала сайта необходимы базовые знания языка гипертекстовой разметки (HTML), а так же, Java Script, CSS, и понимание принципов взаимодействия браузера и web-сервера.

Если ранее вы не занимались конфигурированием web-сервера nginx, настоятельно рекомендую ознакомиться со структурой конфигурационного файла, базовыми настройками, встроенными переменными и с примерами простейших конфигураций.
Документация nginx доступна на официальном сайте проекта:
https://nginx.org/ru/docs/
Руководство для начинающих:
https://nginx.org/ru/docs/beginners_guide.html

В процессе исследования проксируемого сайта, скорее всего, вам понадобятся инструменты отладки, встроенные в популярные браузеры. С ними так же желательно предварительно ознакомиться.
Краткое описание инструментов разработчика в браузере Firefox: https://developer.mozilla.org/ru/docs/Tools

Если nginx еще не установлен, необходимо его установить:

sudo apt-get install nginx

Базовой установки (с поставляемыми по-умолчанию модулями) в большинстве случаев будет достаточно.

Конфигурационные файлы nginx находятся в директории /etc/nginx/.
Общие настройки сервера (nginx.conf) мы рассматривать не будем, перейдем непосредственно к конфигурированию проксирования конкретного сайта. Тем, кто впервые установил nginx рекомендую, как минимум, просмотреть файл /etc/nginx/nginx.conf и, желательно, прочитать о директивах, которые там содержатся.

В качестве примера возьмем новостной сайт https://www.interfax.ru/

Небольшое отступление
Конфигурационные файлы конкретных сайтов обычно размещаются в директории /etc/nginx/sites-available/.
А в директории /etc/nginx/sites-enabled/, как правило, помещают символические ссылки на конфигурации из /etc/nginx/sites-available/.
Всё, что находится в /etc/nginx/sites-enabled/ nginx считывает при запуске.
Таким образом можно легко включать и отключать какой-то сайт на сервере.
Например, если в /etc/nginx/sites-available/ есть файл homesite.conf, можно создать на него ссылку в /etc/nginx/sites-enabled/

$ sudo ln -s /etc/nginx/sites-available/homesite.conf /etc/nginx/sites-enabled/homesite.conf

Перезапустив nginx, мы сможем зайти на сайт, настроенный в этом конфигурационном файле.

$ sudo systemctl restart nginx

Если мы захотим сделать сайт недоступным, мы просто удаляем ссылку из /etc/nginx/sites-enabled/ и перезапускаем nginx.

$ sudo rm -f /etc/nginx/sites-enabled/homesite.conf && sudo systemctl restart nginx

Cайт более не работает, а его конфигурация осталась в /etc/nginx/sites-available/.

Для экспериментов временно добавим IP-адрес на интерфейс tun0.

sudo ip address add 300:529f:150c:eafe::7/64 dev tun0:1

Подробнее о том, как добавить несколько адресов на один интерфейс можно прочесть статье Короткий адрес из подсети 300::/64.

Создадим файл /etc/nginx/sites-available/interfax.ru.conf
И внесем в него такое содержимое:

/etc/nginx/sites-available/interfax.ru.conf
server {
    #установка заголовков, которые позволят понять серверу, что запросы пришли через прокси-сервер
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
 
    listen [300:529f:150c:eafe::7]:80;
	location / {
		proxy_pass https://www.interfax.ru/;
	}
}

Таким образом мы сообщаем серверу, на каком интерфейсе (адресе) и порту должен работать сайт, и что при обращении к корневой директории необходимо сделать запрос на https://www.interfax.ru/ и выдать нам результат.

Создаем ссылку в /etc/nginx/sites-enabled/ и перезапускаем nginx.

sudo ln -s /etc/nginx/sites-available/interfax.ru.conf /etc/nginx/sites-enabled/interfax.ru.conf && sudo systemctl restart nginx

Если сейчас мы откроем браузер и перейдем по адресу http://[300:529f:150c:eafe::7]/, мы уже увидим страницу с новостями.
Но это еще не всё. Откроем панель инструментов разработчика, перейдем на закладку Сеть и обновим страничку.
Видно, что часть скриптов запрашивается браузером с других адресов (static.smi2.net др.)…
Пользователи, не имеющие доступа в интернет (подключенные к Yggdrasil, например, посредством радио-линка), не смогут загрузить эти скрипты.
Исправим это, а заодно заблокируем лишнее (баннеры, счетчики и т.п.).

/etc/nginx/sites-available/interfax.ru.conf
server {
    listen [300:529f:150c:eafe::7]:80;
 
    #установка заголовков, которые позволят понять серверу, что запросы пришли через прокси-сервер
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
 
    sub_filter_once off; #нужно заменять все совпадения, не только первое
    sub_filter_last_modified on; #сохранение поля Last-Modified в исходном ответе
 
    location / {
 
        proxy_set_header Accept-Encoding ""; # сообщаем серверу, что данные не нужно сжимать, иначе не будут работать фильтры sub_filter
 
        sub_filter "https://static.smi2.net/" "$scheme://$host/staticsmi2net/";
        sub_filter "//smi2.ru/" "//$host/smi2ru/";
        sub_filter "https://www.interfax.ru/" "$scheme://$host/";
        sub_filter "https://fonts.googleapis.com/" "$scheme://$host/ftsgapis/";
        sub_filter "https://cdn.onthe.io/" "$scheme://$host/cdnontheio/";
 
        #обрезаем лишнее
        sub_filter "https://yastatic.net" "$scheme://$host/_empty";
        sub_filter "//www.googletagmanager.com/" "//$host/_empty";
        sub_filter "/js/prebid.min.js" "/_empty";
        sub_filter "https://static.videonow.ru/" "$scheme://$host/_empty";
        sub_filter "https://stats2.videonow.ru/" "$scheme://$host/_empty";
        sub_filter "https://cdn.videonow.ru/" "$scheme://$host/_empty";
        sub_filter "http://imasdk.googleapis.com/" "$scheme://$host/_empty";
        sub_filter "https://s0.2mdn.net/" "$scheme://$host/_empty";
 
        sub_filter "<!-- tns-counter.ru -->" "<!-- tns-counter.ru";
        sub_filter "<!--/ tns-counter.ru -->" "/ tns-counter.ru -->";
        sub_filter "<!-- Yandex.Metrika counter -->" "<!-- Yandex.Metrika counter";
        sub_filter "<!-- /Yandex.Metrika counter -->" "/Yandex.Metrika counter -->";
        sub_filter "<!-- Global site tag (gtag.js) - Google Analytics -->" "<!-- Global site tag (gtag.js) - Google Analytics";
        sub_filter "<!-- /Global site tag (gtag.js) - Google Analytics -->" " /Global site tag (gtag.js) - Google Analytics -->";
        sub_filter "<!--noindex-->" "<!--noindex";
        sub_filter "<!--/noindex-->" "/noindex-->";
 
        sub_filter "containerId: '" "containerId: '-";
        sub_filter "/css/ads.css" "/_empty";
        sub_filter "cntimg.src = \"/cnt/\" + cntParseReferer()" "cntimg.src = \"/_empty\"";
 
        #скроем ненужные элементы
        sub_filter "class=\"toplinks\"" "style=\"visibility: hidden;\"";
        sub_filter "class=\"header__soc\"" "style=\"visibility: hidden;\"";
        sub_filter "class=\"but__enter\"" "style=\"visibility: hidden;\"";
        sub_filter "<footer " "<footer style=\"visibility: hidden;\"";
        sub_filter "class=\"bss__wrap\"" "style=\"visibility: hidden;\"";
        sub_filter "class=\"faqSend faqSendOpen\"" "style=\"visibility: hidden;\"";
 
        proxy_pass https://www.interfax.ru/; # проксируем
    }
 
    location ~* /cdnontheio {
        rewrite ^/cdnontheio/(.*) /$1 break; # заменяем строку cdnontheio в адресе на параметры в исходном запросе и...
        proxy_pass https://cdn.onthe.io; # делаем запрос к нужному серверу
    }
 
    location ~* /taticsmi2net { # ~* - без учета регистра (~ - с учетом)
        rewrite ^/staticsmi2net/(.*) /$1 break;
        proxy_pass https://static.smi2.net;
    }
 
    location ~* /smi2ru {
        rewrite ^/smi2ru/(.*) /$1 break;
        proxy_pass http://smi2.ru;
    }
 
    location ~* /ftsgapis {
        rewrite ^/ftsgapis/(.*) /$1 break;
        proxy_pass https://fonts.googleapis.com;
    }
 
    location /_empty { # здесь просто выдается пустой ответ
        expires max;
        add_header Content-Type text/plain;
        add_header Content-Length 0;
        return 204;
    }
}

Обновим страницу. Теперь всё выглядит лучше. Запросы напрямую в интернет браузером не отправляются. Перейдем по ссылкам на подробности новостей, убедимся, что там тоже всё хорошо.

Примечание
На популярных и активно поддерживаемых сайтах довольно часто происходят изменения, затрагивающие структуру HTML, CSS, JavaScript и пр. - например, когда администраторы или разработчики решат сменить тему оформления сайта или добавить новые рекламные баннеры. Поэтому на момент прочтения вами этой статьи, если вы будете использовать приведенные конфигурационные файлы, скорее всего, вам потребуется внести какие-то изменения, чтобы достичь желаемого результата. По этой же причине администраторам проксируемых таким образом сайтов (зеркал) периодически придется отслеживать изменения на сайтах и вносить корректировки в конфигурационные файлы.

Конфигурационный файл заметно увеличился. Но если присмотреться, большую часть занимает повторение одной директивы - sub_filter. Эта директива включенного модуля ngx_http_sub_module. Она просто заменяет одну подстроку на другую в получаемом ответе от проксируемого сервера. Таким образом мы подменяем ссылки на наш ресурс, скрываем ненужные элементы, блокируем загрузку ненужных скриптов и т.д.

Например, ссылка //smi2.ru/data/js/93374.js после обработки директивой

sub_filter "//smi2.ru/" "//$host/smi2ru/";

будет выглядеть так: //[300:529f:150c:eafe::7]/smi2ru/data/js/93374.js

Таким образом, запрос будет сделан к нашему ресурсу.
Но как обработать такой запрос?
Вот так обрабатываем эту локацию:

location ~* /smi2ru {
    rewrite ^/smi2ru/(.*) /$1 break; # заменяем /smi2ru/ на исходный запрос (/data/js/93374.js)
    proxy_pass http://smi2.ru; # и отправляем этот запрос на нужный сервер
 
    #если в ответе этого сервера тоже потребуется что-то изменить, добавим сюда proxy_set_header Accept-Encoding "" и sub_filter...
}

Директива rewrite позволяет изменять URI запроса с помощью регулярных выражений.

Совет
Для исследования каких-то конкретных элементов на странице удобно использовать щелчок ПКМ по нему и выбор из контекстного меню пункта «Исследовать элемент» - попадём в ту же панель инструментов разработчика, но на другую закладку, где курсор спозиционируется на код выбранного элемента..

На этом здесь всё. Были рассмотрены основные принципы проксирования сайта, изменения содержимого проксируемых страниц, блокирование ненужных элементов.

Стоит заменить, что это не предел. Этот конфигурационный файл можно оптимизировать, отключив логи там, где это не нужно, добавив кэширование, можно ограничить количество соединений и кое-что другое…

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

Ссылки

Обсуждение

d4708, 2023/11/22 23:38, 2023/11/23 04:45
Решил для себя поднять yggdrasil зеркало opennet, затем решил собрать все конфиги и выложить на гитхаб (ссылку закрепил в материале)

Вся эта затея связана с тем, что у каждого сайта свои заморочки с тонкой настройкой, статья супер но мне кажется будет удобным хранить где то примеры для конкретных сайтов для nginx и apache (кто юзает его) ну и бекап и может кто нибуть пофиксит / доработает / запустит еще одно зеркало из готового набора

Может будет совет или PR как оптимизировать радио-стримы например

У меня сейчас для опеннет не работают табы (тк дропнул яваскрипт по умолчанию) и картинки.
Так что добро пожаловать, само зеркало добавлю в каталог чуть позже

Fyodor Ustinov, 2023/11/23 03:32
Я сейчас буду немного нудеть, но то, что описывается в этой статье это не "зеркало" а "прокси". Т.е. данная статья вводит в некоторое заблуждение прям первой сточкой - "или как создать зеркало интернет-сайта в сети Yggdrasil".
Основное (и по сути - единственное) отличие "Зеркала" от "Прокси" - зеркало хранит данные независимо от основного сайта.

d4708, 2023/11/23 04:29
Спасибо за важное замечание!

Перенес проект (как в прочем, изначально сомневался) в запылившийся репозиторий
https://github.com/YGGverse/YGGbro

newbie, 2023/11/23 09:26
К этому стоит относиться проще. "Зеркало" просто отражает сайт в эту сеть.
Кстати, многие так это называют. Правильно это или нет - насколько я понимаю, устоявшейся терминологии нет.

https://habr.com/ru/articles/158393/
https://qna.habr.com/q/630031
https://ru.wikipedia.org/wiki/Зеркало_веб-сайта#Доменные_зеркала (даже домены, не алиасы, а зеркала)

d4708, 2023/11/23 14:21
Можно конечно спорить о терминологии, для пользователя что зеркало что прокси визуально может не отличаться.

У меня нет технического образования, может терминология описана в общепринятых (промышленных) стандартах, от википедии отталкиваться бы не стал. Но по личному опыту в разработке и некоторого времени обитания в интернетах, да, зеркало подразумевает нечто "резервное", например есть зеркало http://[316:c51a:62a3:8b9::4]/YGGverse и это действительно резервная инстанция, с функцией gittea - mirror

Fyodor Ustinov, 2023/11/23 06:43
Вобще есть три термина, которые народ часто путает. И понятно почему - они очень близки и, даже, краями пересекаются.

Алиас, Прокси и Зеркало.

Алиас - это когда у сайта есть второе-третье-четвёртое имя или ip вдрес. Абсолютно штатная ситуация. У данного сайта, как минимум - http://[222:a8e4:50cd:55c:788e:b0a5:4e2f:a92c], http://howto.ygg и http://howto.ygg.at это алиасы.

Прокси - это когда есть отдельный "программно-аппаратный комплекс", который занимется тем, что пробрасывает трафик через себя так, что-бы основной сервер думал что к нему ходит обычный абонент, а абонент думал, что он ходит на обычный сервер. Он может дополнительно делать еще много разного (например кешировать какие-то данные), но основной признак прокси - он неработоспособен без основного сайта. У данного сайта (как я думаю) это https://ygg.work.gd/, https://howto.yggno.de/ и http://yggdrasil.acetone.i2p/

Зеркало - это тоже отдельный ПАК, но отличается от прокси он тем, что содержит полную (или частичную) копию основного сайта и может работать отдельно. Для данного сайта это http://[320:9c1:e1fa:a105::d]/

Еще из примеров - http://flibusta.ygg это прокси, http://flibusta.ygg.at - это алиас прокси, а http://f.ygg - это частичное зеркало

d4708, 2023/12/11 11:30
Вот я не пойму как настроить nginx, чтобы он не выплевывал редиректом в клирнет, может кто подскажет как заблокировать выходящие переадресации для проксируемого ресурса.

Или оно из кода где-то яваскриптом меня выбрасывает, хотя яваскрипт выключен, остаются мета-теги.
Ну или через заголовок

Ходишь себе спокойно и бац уже в адресной строке давно уже как прямой URL

newbie, 2023/12/11 11:38, 2023/12/11 11:44
Чтобы запретить загрузку браузером ресурсов с внешних (и других) адресов используйте CSP (Content Security Policy) для nginx. Это делается путем установки соответствующего заголовка. Не сложно, погуглите.

Но это, скорее всего, не ваш случай. Вы ходите по сайту, кликаете по ссылкам и не хотите попасть в клирнет. Тут дело не в nginx. Если не хотите, чтобы браузер загружал страницы по клирнетовским ссылкам, когда вы по ним кликаете, тут только отключение для браузера доступа в клирнет поможет. Это можно сделать различными способами: например, настройкой браузера для хождения в сеть через прокси, на котором будут запрещены определенные адреса...

d4708, 2023/12/11 11:48
Спасибо за совет

Про отключение выхода в интернет / утечки трафика в курсе, но хочу нормально настроить сам прокси, т.к. его юзают другие и где-то закралась ошибка, найду скину ссылку (на примере зеркала opennet) - при чем по домену .ygg работает а с IP выкидывает.

newbie, 2023/12/11 11:51
Какие-то ссылки, видимо, не заменяются просто с помощью sub_filter...
d4708, 2023/12/11 12:03
Нет, именно редирект был

кому интересно может покликать, может обнаружит случайно
http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggbro/opennet.ru

newbie, 2023/12/11 12:07
Попутно: по ссылке на MAN'ы (в шапке) выдается ошибка.
d4708, 2024/01/21 03:14
Может кто сталкивался, не первый раз замечаю что при аварийном рестарте системы не стартует nginx, добавлял в кронтаб @reboot service nginx restart - но работает только если руками зайти и ребутнуть, может интерфейс какой не поднялся, или с проксированием связано

d4708, 2024/01/21 06:42
Так, вроде какой сервис не поднялся.
After=network-online.target remote-fs.target nss-lookup.target

Подозреваю что связано с проксированием. Другие сервисы нормально стартуют

Решил пока таймаутом:
/lib/systemd/system/nginx.service
ExecStartPre=/bin/sleep 60

Fyodor Ustinov, 2024/01/21 08:37
Попробуй повесить руками ygg адрес на lo интерфейс (тот, к которому у тебя биндится nginx). Впрочем, да, таймаут тоже в 99.9% решает.

Thunar, 2024/01/21 12:20

Таймаут в юните nginx можно заменить на

Wants=sys-devices-virtual-net-tun0.device
After=sys-devices-virtual-net-tun0.device

(подробнее здесь).



Fyodor Ustinov, 2024/01/21 20:23
Всё таки повесить ip на lo интерфейс проще, чем править файлы всех сервисов, которые зависят от ip yggdrasil.

newbie, 2024/01/21 20:57, 2024/01/21 20:58
Тут есть пример: yggdrasil:subnet_setting
d4708, 2024/01/21 22:45
это интересный момент, спасибо, пригодится - не знал!
у меня там несколько интерфейсов, даже не проверял всегда ли tun0 на Yggdrasil но думаю копнуть и можно юзать

кроме nginx у меня больше ничего не глючит, он часто висит когда падает один из источников proxy_pass - не поднимается пока не закомментить оффлайновый.

Только авторизованные участники могут оставлять комментарии.
yggdrasil/nginx_proxying.txt · Последнее изменение: 2024/03/08 19:10 — Thunar
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki