Обходим блокировки сайтов: или как я развёртывал прямой proxy на базе nginx +5


С чего всё начиналось

Ты как специалист в области IT, после февральских событий скорее всего столкнулся с проблемой недоступности некоторых ресурсов и наверняка подумал о том, как это дело обойти. Но использовать "бесплатный" или сторонний сервис кажется небезопасным или не даёт нужную ширину канала. У тебя есть навыки работы с Linux и свой сервер где нибудь за бугром. Почему бы собственно говоря не сделать свой прямой прокси?

Скорее всего ты посмотришь в сторону SOCKS5 сервера и даже запустишь его в контейнере, всё будет работать, но при этом если ты используешь Windows + любой браузер с расширением для прокси (я использую Switchy Omega), ты столкнёшься с двумя вариантами:

  1. Ты будешь поднимать SOCKS5 без аутентификации. Следовательно наш прокси будет открыт для всего света и нашедшие его люди могут использовать его по своему усмотрению;

  2. Как умный инженер, ты сделаешь это с аутентификацией. Но тут есть проблема - Windows не умеет в SOCKS5 с аутентификацией. Я пытался найти решение этой проблемы, но у меня не получилось найти информацию, как это сделать. Как вариант использовать Tor Browser, но мне лично его использование не кажется удобным.

А почему бы не использовать nginx?

Ни для кого не секрет, что nginx прекрасно из коробки умеет быть обратным прокси, и не без этого получил свою популярность. Но вот быть прямым прокси сервером из коробки он не умеет, для этого придётся его пересобрать! Собственно говоря ниже пойдёт речь о том, как научить его это делать.

Подготовка

Нам потребуется:

  1. Linux сервер за пределами необъятной. Ubuntu, Centos или что вам там нравится. Без разницы;

  2. Доменное имя, которое будет ссылаться на наш сервер;

  3. TLS сертификат (очень желательно, но можно без него). Его получение выходит за рамки данной статьи. Лично я использую acme.sh.

За работу!

Я буду производить настройку на Ubuntu 20.04, вы же вольны использовать всё, что вам душе угодно, но там могут быть свои нюансы. Если вы пойдёте этим путём, то полагаю, вы сами сможете применить эту инструкцию в другом дистрибутиве.

Заблаговременно установим в систему необходимые для сборки пакеты:

apt install -y make build-essential apache2-utils libpcre++-dev libssl-dev zlib1g-dev

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

mkdir build && cd build

Теперь отправная точка для каждого шага именно эта директория.

  1. Скачиваем nginx и разархивируем загруженный архив

    wget https://nginx.org/download/nginx-1.21.6.tar.gz
    tar -xzf nginx-1.21.6.tar.gz

  2. Клонируем следующие репозитории, они нам все будут нужны

    git clone https://github.com/chobits/ngx_http_proxy_connect_module
    git clone https://github.com/openresty/lua-nginx-module
    git clone https://github.com/vision5/ngx_devel_kit

  3. Подготовка для Lua модуля:

    1. Необходимо скачать, разархивировать и установить в систему LuaJIT.

      git clone https://github.com/openresty/luajit2
      cd luajit2
      make && make install

      После этого необходимо создать две необходимые переменные окружения

      export LUAJIT_LIB=/usr/local/lib/
      export LUAJIT_INC=/usr/local/include/luajit-2.1/

    2. Установим в систему lua-resty-core и lua-resty-lrucache. Поочерёдно перейдём в клонированные репозитории и выполним make install

      1. git clone https://github.com/openresty/lua-resty-core
        cd lua-resty-core
        make install

      2. git clone https://github.com/openresty/lua-resty-lrucache
        cd lua-resty-lrucache
        make install

  4. Переходим к настройке модуля ngx_http_proxy_connect_module

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

    cd nginx-1.21.6 && patch -p1 < /etc/nginx/modules/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_102101.patch

    Успешный результат выполнения должен быть примерно таким
    Успешный результат выполнения должен быть примерно таким
  5. Теперь запускаем команду configure. В указанном примере стоит обратить внимание на пути к новым модулям (первые три ключа). Они у вас могут быть другими. Также другие ключи взяты из пакета, который устанавливается из стандартного репозитория Ubuntu. Из этого можно много чего выкинуть, но большинству эту не надо.

    ./configure --add-module=/root/build/ngx_devel_kit \
    	--add-module=/root/build/ngx_http_proxy_connect_module \
    	--add-module=/root/build/lua-nginx-module \
    	--prefix=/etc/nginx \
    	--sbin-path=/usr/sbin/nginx \
    	--modules-path=/usr/lib/nginx/modules \
    	--conf-path=/etc/nginx/nginx.conf\
    	--error-log-path=/var/log/nginx/error.log \
    	--http-log-path=/var/log/nginx/access.log\
    	--pid-path=/var/run/nginx.pid\
    	--lock-path=/var/run/nginx.lock \
    	--http-client-body-temp-path=/var/cache/nginx/client_temp \
    	--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
    	--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
    	--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
    	--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
    	--user=nginx \
    	--group=nginx \
    	--with-compat \
    	--with-file-aio \
    	--with-threads \
    	--with-http_addition_module \
    	--with-http_auth_request_module \
    	--with-http_dav_module \
    	--with-http_flv_module \
    	--with-http_gunzip_module -\
    	--with-http_gzip_static_module \
    	--with-http_mp4_module \
    	--with-http_random_index_module \
    	--with-http_realip_module \
    	--with-http_secure_link_module \
    	--with-http_slice_module \
    	--with-http_ssl_module \
    	--with-http_stub_status_module \
    	--with-http_sub_module \
    	--with-http_v2_module \
    	--with-mail \
    	--with-mail_ssl_module \
    	--with-stream \
    	--with-stream_realip_module \
    	--with-stream_ssl_module \
    	--with-stream_ssl_preread_module
    Что-то подобное вы должны будете увидеть при успешном выполнении команды
    Что-то подобное вы должны будете увидеть при успешном выполнении команды
  6. Осталось запустить

    make && make install

Настройка nginx

Для упрощения инструкции все действия буду производится под пользователем root, в частности запуск демона и создание директорий.

  1. Создадим директорию mkdir -p /var/cache/nginx

  2. Нужно создать файл для запуска nginx, как сервис

    vim /lib/systemd/system/nginx.service

    Со следующим содержимым

    [Unit]
    Description=A high performance web server and a reverse proxy server
    Documentation=man:nginx(8)
    After=network.target
    
    [Service]
    Type=forking
    PIDFile=/run/nginx.pid
    ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
    ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
    ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
    ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
    TimeoutStopSec=5
    KillMode=mixed
    
    [Install]
    WantedBy=multi-user.target

    Теперь обязательно нужно выполнить. Без этого systemd не узнает о нашем сервисе.

    systemctl daemon-reload

  3. Отредактируем файл /etc/nginx/nginx.conf и приведём его примерно к такому содержанию

    # /etc/nginx/nginx.conf
    user root;
    worker_processes  1;
    pid /run/nginx.pid;
    
    events {
        worker_connections  1024;
    }
    
    http {
        lua_package_path "/usr/local/lib/lua/?.lua;;"; #обазательный параметр!
        #lua_load_resty_core off;
    
        include       mime.types;
        default_type  application/octet-stream;
    
        sendfile        on;
    
        keepalive_timeout  65;
    
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
    }
    1. Сгенерируем файл аутентификации. Этот файл будет использоваться сервером для проверки учетных данных. И при настройке прокси в браузере нужно будет указать пользователя proxyuser и пароль, который будет введён при выполнении кода ниже.

      mkdir -p /etc/nginx/auth
      htpasswd -c /etc/nginx/auth/htpasswd proxyuser

    2. Создадим директорию с конфигурациями сайтов

      mkdir -p /etc/nginx/sites-enabled

    3. Осталось создать файл конфигурации в созданной выше директории с таким содержимым. Посмотрите комментарии в коде, если будете делать конфигурацию без сертификата.

      vim /etc/nginx/sites-enabled/fw-proxy

      # /etc/nginx/sites-enabled/fw-proxy
          
      server {
      	# Если без сертифката, то listen 80;
      	listen 443 ssl;
      	server_name proxy.example.com;
      			
      	# Если без сертификата, то убрать или закомментировать два нижних параметра
      	ssl_certificate <path to certificate>/fullchain.pem;
      	ssl_certificate_key <path to ptivate key>/key.pem;
      
      	auth_basic "Server auth";
      	auth_basic_user_file /etc/nginx/auth/htpasswd;
      
      	# transfer Proxy-Authorization header to Authorization header
      	rewrite_by_lua_file /etc/nginx/auth/proxy_auth.lua;
      
      	server_tokens off;
      
      	resolver 8.8.8.8 ipv6=off;
      
      	proxy_connect;
      	proxy_connect_allow all;
      	proxy_connect_connect_timeout 10s;
      	proxy_connect_read_timeout 10s;
      	proxy_connect_send_timeout 10s;
      
      	location / {
      		#proxy_http_version 1.1;
      		proxy_pass http://$host;
      		proxy_set_header Host $host;
      
      		# If backend wont check Auth header, we should not pass the user/password.
      		proxy_hide_header Authorization;
      		proxy_hide_header Proxy-Authorization;
      	}
      }
    4. Проверки работоспособности

      Проверяем конфигурацию nginx командой nginx -t

      Конфигурация успешно проверена
      Конфигурация успешно проверена

      Проверим подключение утилитой curl

Настройка браузера

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

  1. Надо установить расширение Switchy Omega из любого удобного магазина расширений;

  2. Зайти в его настройки и создать новый прокси сервер. Как на скриншоте ниже, не забыв указать данные пользователя в "замочке" справа.

  3. Осталось перейти в профиль switch и сопоставить необходимый сайт с прокси сервером и радоваться жизни. Расширение автоматически будет ходить на указанные сайты через прокси. Естественно в расширении немало возможностей по кастомизации поведения.

Заключение

В заключении хотелось бы написать, что скорее всего есть более элегантные варианты решить эту проблему, но я решил пойти именно этим путём, потому что другого не увидел. В данной инструкции конечно же можно отказаться от использования TLS и базовой аутентификации. Благодаря этому можно будет не заморачиваться с Lua. И собирать nginx только с ngx_http_proxy_connect_module. И даже есть готовые образы для создания контейнера. Но так не секьюрно, а цель была именно в том, чтобы максимально защитить прокси от посягательств извне.




Комментарии (31):

  1. vesper-bot
    /#24599674 / +14

    Проще поднять SSH-тоннель и настроить его в браузере как socks-proxy. А ещё, если твой ssh-тоннель будет принимать подключения только с localhost, его ещё никто не сможет найти.

    • egorsergey
      /#24599716 / +1

      Век живи - век учись, надо попробовать.

  2. FSA
    /#24599814 / +4

    Мне нужен был белый IP адрес для ноутбука. Я просто взял дешёвый VPS и поднял до него Wireguard. Но не стал в него загонять весь свой трафик, а использовал только для связи между ноутбуком и сервером. На сервере просто поднял socks сервер и теперь пользуюсь им, если нужно. Весь трафик идёт внутри туннеля.
    И, да. Ничего не пришлось собирать из исходников. Да и при желании можно весь трафик запихать в туннель, просто создав другую конфигурацию на клиенте.

    А вот тут я описал как настраивал Wireguard: https://tavda.net/wireguard

  3. mikes
    /#24599950 / +1

    а что не так со squid коль есть потребность именно в прокси?

    • vesper-bot
      /#24600014

      Громоздкий, наверно. Я когда его увидел, в осадок выпал от возможностей и объемов необходимой конфигурации.

      • mikes
        /#24600304

        возможностей много да, минимальный конфиг будет строчек 10-15 а то и меньше. ИМХО существенно проще.

        • Hu3yP7
          /#24604232

          Есть ещё такая штука для прокси: https://github.com/qwj/python-proxy

          Тут и протоколов больше (shadowsocks если кому нужно больше скрытности), и настраивать просто.

    • egorsergey
      /#24600520

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

    • FSA
      /#24600672

      Избыточно. Есть простенькие socks прокси с минимумом настроек. Для обхода багов сети самое то.

    • select26
      /#24603502 / +2

      3proxy использую уже не один год. По сравнению со squid есть один недостаток- 3proxy не кэширующий прокси. Но можно ли считать это недостатком в эпоху HTTPS? В общем забыл про squid уже.

      Просто посмотрите.

  4. ABATAPA
    /#24600010 / +1

    навыки работы с Linux и свой сервер где нибудь за бугром. Почему бы собственно говоря не сделать свой прямой прокси?

    Тогда почему ты с такими навыками просто не поднял VPN и не завернул в него нужный трафик?

    • egorsergey
      /#24600508

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

      • select26
        /#24603514

        Я тоже так сделал: поднял пару прокси серверов в РФ и в Европе и настроил proxy.pac скрипт, в котором описана маршрутизация.

        Например, все домены *.ru выпускать через российский прокси, *.de - через европейский и т.д. Естественно, можно указать конкретные сайты.

        Этот скрипт подключил как "автоконфигурация прокси" в браузере.

        Просто. Быстро. Надёжно.

      • 0x131315
        /#24604806

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

  5. init0
    /#24600396 / +2

    А зачем красноглазить и собирать nginx когда можно установить его несколькими командами (не скажу за другие дистрибутивы, но в Debian тот же lua модуль добавляется apt install libnginx-mod-http-lua, думаю и в других это не проблема) или еще проще - запустить его из docker на базе openresty, где уже в наличии тот же lua модуль.

    • egorsergey
      /#24606172

      1. Не уверен насчёт наличия пакета для модуля прокси;

      2. В образе openresty этого модуля по-моему нет.

      П.С. всё это надо уточнять.

  6. mpa4b
    /#24600482 / +1

    Интересно, есть ли какое-либо интегрированное решение (linux), которое бы по маскам для IP и по маскам (регекспам) для dns-имён заворачивало бы трафик в разные места (интерфейсы)? Например что-то прямиком к провайдеру, что-то в vpn-туннель. Эдакий NAT вместе с DNS, только умный.

    • vesper-bot
      /#24600664

      Как я понимаю, колхозить надо. Поставить DNS-прокси (форвардер/кэш) который будет сверять DNS-запросы с регекспами, и обновлять таблицу маршрутизации, чтобы запрос к этому узлу улетал в VPN, если обмен данными с этим узлом настроено отправлять в VPN. При этом браузерный DoH/DoT придется отключить, иначе никакой магии не получится. Ну и вишенка на торте — что делать, если два разных имени ресолвятся в один IPv4, но одно в списке "только VPN" а второе в списке "только без VPN"?


      Правда, сильно не исключаю, что на подобное как раз способен squid, как прокси выше уровнем, чем L3.

    • Manrus
      /#24601044

      Это задача легко решается прямо на роутерах на "продвинутых" прошивках. Либо микротик в стоке.

      • gwathedhel
        /#24603766

        Вот по совпадению - ровно вчера делал такое на микротике. Для заворота в туннель только нужного траффика

        • egorsergey
          /#24606168

          Тоже делал ни МТ такое. Заворачивал траффик на определённые айпишники в туннель. Из минусов:

          1. Не удобно обновлять адрес-листы. Можно конечно написать скрипт на баш/повершелл/питон/(что нравится), который бы это делал, или ходить в сам МТ и делать ручками. В браузере это сделать куда как удобнее и быстрее.

          2. Скорость работы. Вот тут я увидел ОЧЕНЬ медленную загрузку сайтов.

    • select26
      /#24603530

      Чуть выше описал решение с автоконфигурацией прокси браузера.

  7. aleks-th
    /#24601930

    Мне кажется проще Squid заюзать.

    Или какой-нить OpenVPN.

    • egorsergey
      /#24606162

      В Сквид не умею.

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

  8. Dime_n_u
    /#24602550 / +1

    Как опыт наверное пригодится таким заняться, но всё ж раз заморский сервак, то лучше начать с статьи как его оплачивать) пользовался hetzner, но была возможность оплачивать только через swift. Не знаю что это за услуга, но за оплату счёта в 16 евро надо было платить комиссию в 15... Да и раз пошёл такой разговор подскажите как барыги настраивают впн или куда смотреть в плане ограничения ресурсов/трафика? Я по неопытности дал свой впн товарищу, так это чудовище скачало какой-то тупой фильм и мне пришло письмо от MGM от чего я не плохо труханул

    • duckhawk
      /#24604556 / +1

      Есть масса реселлеров, продающих виртуалки вне РФ, которые принимают российские карточки, к примеру inferno.name, vdsina.ru, да десятки их

      • Dime_n_u
        /#24604852

        Спасибо, пошарился на инферно - похоже на барахолку, как буд-то в старом Питере на рынок юнона пришёл где можно найти/купить всё)) На инферно просят за хиленький vps в Германии 75$, когда в хетзнер аналогичный сервак 5-10€ если память не изменяет... Но всё равно спасибо

        • duckhawk
          /#24605226 / +1

          Выглядит да, привет из 90-ых, но вполне работает.

          Не знаю, где Вы смотрите, но у них там как у всех, "одно ядро один гиг" стоит по 5 баксов в месяц. Из плюсов конкретно инферно - условно безлимитный траф (есть некоторые рамки) и абьюзоустойчивость (они очень лояльны к не сильно противозаконным запросам, типа "с вас качали торренты").

          (Подозреваю, что Вы смотрите выделенные сервера, а в данном случае нужно смотреть в виртуальные)

  9. 0x131315
    /#24604776

    Имхо nginx лучше поднимать через docker. Это быстрее, проще, и чище. С докером также в легком доступе множество преднастроенных проксей и VPN на любой вкус. И домен для всего этого не обязателен.

    • egorsergey
      /#24606156

      Да, конечно. Только вот готовый докер образ с такими модулями вы не найдёте. Можете собрать и поделиться с сообществом. Метод в статье не претендует быть идеальным и подходящим для всех.

  10. Johan_Palych
    /#24607512

    Был хороший цикл статей.
    Иногда использую для синхронизации локальных реп Zabbix.
    На Windows и Android не тестировал.
    Работают отлично все три клиента.

    https://habr.com/ru/post/555368/