Преамбула… Даная статья была написана ещё летом но, по независящим от автора причинам, немножко подзадержалась...
Однажды, жарким летним вечером, после очередной введённой в консоли браузера команды вида :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 приветствуются!