T.me Преамбула… Даная статья была написана ещё летом но, по независящим от автора причинам, немножко подзадержалась…
Однажды, жарким летним вечером, после очередной введённой в консоли браузера команды вида :set content.proxy socks://localhost:9050
, автор сего опуса понял, что дальше так жить нельзя и пора приводить выход во всякие скрытосети, а заодно и обход блокировок имени известной организации к какому-то единому, для любого софта вообще и браузера в частности, «общему знаменателю». А как приводить? Разумеется так, чтобы прокси сервер сам «понимал», через какой вышестоящий прокси отправлять и принимать трафик в зависимости от введённого адреса. Вторая цель, вытекающая из первой, вышестоящие прокси могут работать либо как http, либо как socks и оба протокола должны поддерживаться входным прокси. Ну и сам софт должен быть более менее актуальным, что б в случае ошибок или «хотения фич», не приходилось грустно смотреть на одинокую репу на гитхабе, а то и вообще на каком-нибудь сорсфорже.
Итак цели поставлены!
Муки выбора
На самом деле особых мук не было. Ибо, по большому счёту, из имеющихся известных прокси серверов поставленным требованиям удовлетворяли два. Это privoxy
и tinyproxy
. Но tinyproxy
оказался более живым, более легковесным и более простым, поэтому и был выбран и немедленно установлен (в качестве примера используется текущая версия Manjaro Linux).
sudo pacman -Syu tinyproxy
Само собой, до этого, уже были установлены настроены пакеты tor
и i2pd
. (sudo pacman -Syu tor i2pd
).
Базовая настройка tinyproxy
Итак, настроим базовые перенаправления, чтобы в обычный web ходило напрямую, а в *.i2p
и *.onion
через соответствующие вышестоящие (parent) proxy.
/etc/tinyproxy/tinyproxy.conf:
User tinyproxy
Group tinyproxy
PidFile "/var/run/tinyproxy/tinyproxy.pid"
Port 8888
Listen 127.0.0.1
Timeout 600
DefaultErrorFile "/usr/share/tinyproxy/default.html"
StatFile "/usr/share/tinyproxy/stats.html"
Syslog On
# Set the logging level. Allowed settings are:
# Critical, Error, Warning, Notice, Connect, Info
LogLevel Info
MaxClients 100
MinSpareServers 5
MaxSpareServers 20
StartServers 10
MaxRequestsPerChild 0
Allow 127.0.0.1
ViaProxyName "tinyproxy"
## Parent proxy for TOR hosts
upstream socks5 127.0.0.1:9050 ".onion"
## Parent proxy for I2P hosts
upstream socks5 127.0.0.1:4447 ".i2p"
##### End of static configuration #####
Для начала практически все параметры в конфиге остаются по умолчанию.
- Сохраняем
- Запускаем:
sudo systemctl enable --now tinyproxy
- Проверяем:
journalctl -f -u tinyproxy
, параллельно пытаемся зайти на i2p и onion ресурсы (настроив браузер на использование http proxyhttp://localhost:8888
) и видим в лог файле сообщения о перенаправлениях на parent proxy’s:июл 20 01:36:16 dell-lnx tinyproxy[198356]: Request (file descriptor 6): GET http://flibusta.i2p/ HTTP/1.1 июл 20 01:36:17 dell-lnx tinyproxy[198356]: Found upstream proxy socks5 127.0.0.1:4447 for flibusta.i2p июл 20 01:36:17 dell-lnx tinyproxy[198356]: opensock: opening connection to 127.0.0.1:4447 июл 20 01:36:17 dell-lnx tinyproxy[198356]: opensock: getaddrinfo returned for 127.0.0.1:4447 июл 20 01:36:17 dell-lnx tinyproxy[198356]: Established connection to socks5 proxy "127.0.0.1" using file descriptor 7. июл 20 01:36:40 dell-lnx tinyproxy[198356]: Closed connection between local client (fd:6) and remote client (fd:7) ... июл 20 01:39:36 dell-lnx tinyproxy[214495]: Found upstream proxy socks5 127.0.0.1:9050 for ilitafrzzgxymv6umx2ux7kbz3imyeko6cnqkvy4nisjjj4qpqkrptid.onion июл 20 01:39:36 dell-lnx tinyproxy[214495]: opensock: opening connection to 127.0.0.1:9050 июл 20 01:39:36 dell-lnx tinyproxy[214495]: opensock: getaddrinfo returned for 127.0.0.1:9050 июл 20 01:39:36 dell-lnx tinyproxy[214495]: Established connection to socks5 proxy "127.0.0.1" using file descriptor 7.
Список «zapret.info»
Ну что ж, связка прокси базово работает, теперь начинается самое интересное — обход блокировок роскомнадзора. К сожалению, tinyproxy
не поддерживает внешние файлы со списком parent proxy, но это не должно быть препятствием. Ведь мы можем сгенерировать монолитный конфиг «on the fly».
- Копируем имеющийся конфиг tinyproxy под новым именем:
cp /etc/tinyproxy/tinyproxy.conf /etc/tinyproxy/tinyproxy.conf.static
- Слегка правим новый
/etc/tinyproxy/tinyproxy.conf.static
:LogLevel Info
→LogLevel Error
- Создаём юнит который будет клонировать репозиторий проекта zapret.info —
sudo systemctl edit --full --force z-i-prepare.service
:# /etc/systemd/system/z-i-prepare.service [Unit] Description=Zapret Info repository cloner ConditionPathExists=|!/usr/local/lib/z-i/ ConditionFileNotEmpty=|!/usr/local/lib/z-i/dump.csv Wants=local-fs.target After=local-fs.target Wants=network.target After=network.target # [Service] Type=oneshot User=tinyproxy Group=tinyproxy ExecStartPre=+/usr/bin/mkdir -p /usr/local/lib/z-i ExecStartPre=+/usr/bin/chown tinyproxy:tinyproxy /usr/local/lib/z-i ExecStartPre=+/usr/bin/chmod 0750 /usr/local/lib/z-i ExecStart=git clone --depth 1 https://github.com/zapret-info/z-i.git /usr/local/lib/z-i
- Создаём юнит который будет генерировать конфиг tinyproxy, в рантайме —
sudo systemctl edit --full --force tinyproxy-cfg-generator.service
:# /etc/systemd/system/tinyproxy-cfg-generator.service [Unit] After=z-i-prepare.service Wants=z-i-prepare.service # [Service] Type=oneshot User=tinyproxy Group=tinyproxy Environment="PATH=/usr/local/bin:/usr/sbin:/usr/bin" ExecStart=tinyproxy-cfg-gen.sh StandardOutput=file:/run/tinyproxy/tinyproxy.conf
… и собственно скрипт/usr/local/bin/tinyproxy-cfg-gen.sh
к нему:#!/usr/bin/env sh # tinyproxy-cfg-gen.sh -- tinyproxy dynamic config generator to stdout. awk -F';' '{print "upstream socks5 127.0.0.1:9050 \"" $2"\""}' /usr/local/lib/z-i/dump.csv|tr -d '*'|sort|uniq|grep -v '\s\"\"'>/tmp/tinyproxy.conf.dynamic cat /etc/tinyproxy/tinyproxy.conf.static /tmp/tinyproxy.conf.dynamic
- Таймер и сервис, который раз в сутки будет выкачивать обновления списка и рестартовать основной сервис:
sudo systemctl edit --full --force z-i-update-daily.timer
:# /etc/systemd/system/z-i-update-daily.timer [Unit] Description=Zapret Info daily update After=network.target Wants=network.target # [Timer] OnCalendar=daily AccuracySec=1h Persistent=true # [Install] WantedBy=timers.target
И сервис к немуsudo systemctl edit --full --force z-i-update-daily.service
:# /etc/systemd/system/z-i-update-daily.service [Unit] Description=Zapret Info daily update service After=network.target Wants=network.target # [Service] Type=oneshot User=tinyproxy Group=tinyproxy ExecStartPre=/usr/bin/git -C /usr/local/lib/z-i pull ExecStart=+/usr/bin/systemctl try-restart tinyproxy.service
- Наконец вишенка на торте, редактируем tinyproxy.service под наши нужды —
sudo systemctl edit tinyproxy.service
:# /etc/systemd/system/tinyproxy.service.d/override.conf [Unit] Wants=network.target Wants=z-i-prepare.service After=z-i-prepare.service Wants=tinyproxy-cfg-generator.service After=tinyproxy-cfg-generator.service # [Service] User=tinyproxy Group=tinyproxy ExecStart= ExecStart=/usr/bin/tinyproxy -c /run/tinyproxy/tinyproxy.conf ExecStopPost=+/usr/bin/rm -rf /run/tinyproxy/tinyproxy.conf
- А теперь, со всем этим безобразием мы попытаемся взлететь ©
sudo systemctl enable --now tinyproxy sudo systemctl enable --now z-i-update-daily.timer
How it works?
Вдумчивый читатель безусловно поинтересуется, Для чего такие танцы с бубном? Что ж, в заключении не мешает прояснить некоторые моменты. Пойдём прямо по пунктам предыдущего раздела.
- Тут всё просто. Мы сохраняем в отдельный файл ту часть конфигурации, которая не должна меняться автоматически.
- Очень важный параметр, сокращающий время загрузки основного сервиса с часа(SIC!) до, примерно, полутора минут (нетбучный AMD проц года 2009-го и HDD на 5400 об./мин.). Разумеется это не единственный способ повышения производительности.
- «bootstrap» юнит, который запускается всегда, но отрабатывает только в том случае если отсутствует директория
/usr/local/lib/z-i/
либо пуст файл/usr/local/lib/z-i/dump.csv
(ДирективыCondition*
). Ключик--depth 1
позволяет склонировать только последний коммит, а не все 8 Гб. - Основная генерация конфига и ещё один лайвхак для повышения производительности. Из csv
awk
-ом вырезается поле с доменом, очищается от лишних символов. Удаляются строки с пустым доменом, далееcat
отправляет результат наstdout
. а уже юнит, благодаря директивеStandardOutput=
записывает весь вывод в конфиг файл в/run
наtmpfs
! По зависимостям запускается после того как отработает «bootstrap» юнит из предыдущего пункта. - Раз в сутки, начиная с нуля часов, с джиттером в час, обновляем репозиторий и перегенерим конфиг, с рестартом сервиса. Точнее рестартуем сервис с перегенерацией конфига.
- (и 7.) Ну тут всё понятно, запуск вспомогательных юнитов и основного.
Данная связка работает уже неделю 2,5 месяца. Глюкобагов, пока, вроде бы не замечено. готовые конфиги и скрипты живут на гитхабе, PR приветствуются!