habrahabr

Обойдемся без VPN: связка из 2 прокси для хождения в Интернет

  • воскресенье, 22 декабря 2024 г. в 00:00:12
https://habr.com/ru/articles/866746/

На Хабре есть уже десятки статей о том, как поднять свой VPN. Но, кроме VPN, существует еще и прокси. Для браузера его более, чем достаточно.

Практика показывает, что РКН на данный момент не ломает даже прозрачные HTTP прокси (МГТС, Москва). Надеяться на это, впрочем, не приходится, поэтому мы поднимем еще и HTTPS прокси с помощью Squid. Данный прокси работает тупо по адресу и паре логин:пароль безо всяких PAC файлов и прочих костылей на стороне клиента (костылей на стороне сервера будет предостаточно). Позволяет гонять через себя весь TCP трафик не интересуясь, что там уже заблокировано, а что еще нет. Кроме того, его хорошо понимают скрипты и программы Linux, потому что он совместим с переменными HTTP_PROXY и HTTPS_PROXY. Например, в докере:

    environment:
      - HTTPS_PROXY=https://user:password@domain1.com:12345

В минимальной проверенной конфигурации, нам понадобится два сервера: один в РФ, второй "за кордоном". Теоретически, сервер в РФ можно заменить локальным Squid. Для пользователей Linux это не сложно: squid-openssl ставится одной командой в терминале. Но для Windows ничего подходящего (и стабильно работающего) я так и не нашел.

Кроме того, полноценный сервер в РФ позволяет предоставить доступ неограниченному кругу лиц и устройств. И, например, размазать финансовые затраты на несколько человек.

Сервер в РФ снижает пинг до доступных ему ресурсов (по сравнению с зарубежным) и снимает бОльшую часть вопросов, которые могут появиться у государственных\российских сайтов
Сервер в РФ снижает пинг до доступных ему ресурсов (по сравнению с зарубежным) и снимает бОльшую часть вопросов, которые могут появиться у государственных\российских сайтов

Идеальная, максимальная конфигурация: 1 сервер в РФ, любое количество серверов не в РФ, свой домен.

Весь гайд и конфигурация рассчитаны на последнюю Ubuntu 24, хотя бы 1 гигабайт ОЗУ и 10ГБ места на диске. Минимальный shared конфиг аезы подойдет.

После прохождения всех шагов гайда вы получаете HTTPS и HTTP прокси с авторизацией по паре логин:пароль. Учтите, что такая авторизация - непопулярный в 2024 году способ использовать прокси, поэтому почти для всех браузеров вам понадобится расширение, которое имеет поля для их ввода. Для Firefox я использую FoxyProxy.

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

Я не ставил цели сэкономить. Поэтому у меня есть два сервера (за которые я плачу в сумме 14000 рублей в год) и домен, за который я отдаю еще 5 тысяч рублей в год. Пространство для экономии есть: существуют копеечные домены, есть очень дешевые VPS, можно попробовать где-то прикрутить самоподписанный сертификат и обойтись без домена вовсе, а некоторые серверы могут быть ipv6-only, еще дешевле. Всего этого в гайде не будет - тут вам придется изучать вопрос самостоятельно. Меня удовлетворил вариант с делением ценника на несколько человек - я лично отдаю 4000 рублей в год.

Далее по гайду, первый сервер - это сервер в РФ, который и будет прокси-сервером, к которому будут подключаться клиенты (браузеры). Второй сервер - это сервер в другой стране, к которому сервер 1 будет обращаться по необходимости. Клиенты напрямую к серверу 2 не подключаются.

1. Безопасность

На всех серверах меняем socket ssh на сервис, чтобы он читал настройки из /etc/ssh/sshd_config:

systemctl disable --now ssh.socket
systemctl enable --now ssh.service

Генерируем новый ключ на сервере:

ssh-keygen -t rsa -b 4096 -C "root@server"
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

Читаем ключ с сервера: cat ~/.ssh/id_rsaи копируем его текстом себе на ПК. Файл с сервера удаляем rm ~/.ssh/id_rsa

Если ваш ssh клиент требует файл ppk, то этот текст можно импортировать в puttygen (conversions -> import key) и сохранить как ppk (file -> save private key).

Проверяем, что авторизация по ключу и без пароля работает, после чего отключаем вход по паролю, добавляя эти строчки в nano /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
PubkeyAuthentication yes

Раз уж мы здесь, переносим ssh на другой порт:

Port 45678

Убедитесь, что в файле нет конфликтующих строчек, разрешающих авторизацию по паролю, вроде PasswordAuthentication yes

Далее, разрешаем новый порт ufw allow 45678 и перезагружаем ssh systemctl restart ssh

Пока мы занимаемся портами, разрешим себе на будущее сразу все:

ufw allow 80
ufw allow 443
ufw allow 12345
ufw allow 12346

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

Кроме того, если у вас статический IP, можно разрешить с него (и к нему) весь трафик. Аналогично, если вы используете конфигурацию с 2 серверами, есть смысл разрешить им весь трафик друг к другу:

ufw allow from 111.222.333.444
ufw allow to 111.222.333.444

Вызов ufw status после этого должен возвращать:

Status: active

To                         Action      From
--                         ------      ----
443                        ALLOW       Anywhere
80                         ALLOW       Anywhere
... и так далее ...

Если status: inactive, то включаем: ufw enable

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

2. Подготовка сторонних программ

Эти шаги тоже делаем на всех серверах.

Устанавливаем nginx, он еще может пригодиться в хозяйстве (например, проксировать vless grpc или websocket, если вы решите совместить приятное с полезным и развернуть на том же сервере vpn):

apt update
apt install -y nginx-full

Я настоятельно рекомендую вам использовать HTTPS, для чего будет очень удобно заиметь собственный домен. Устанавливаем certbot для получения сертификатов:

apt install -y snapd
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot

На этом этапе вы должны направить свой (под)домен на IP сервера. Если есть IPV6, сделайте и A и AAAA записи. После чего, немного обождав, получаем сертификат на этот домен:

certbot --nginx

В nginx сертификат встанет сам, и путь до него можно будет посмотреть в конфигурации оного. Но в любом случае, пути сохраняем, потому что они нам понадобятся в шаге 4.

3. Подготовка Squid

Добавляем репу со squid, поддерживающим ssl и устанавливаем его на все серверы (не обязательно для Ubuntu 24):

wget -qO - https://packages.diladele.com/diladele_pub.asc | sudo apt-key add -

echo "deb https://squid57.diladele.com/ubuntu/ focal main" > /etc/apt/sources.list.d/squid57.diladele.com.list

Устанавливаем:

apt-get update && apt-get install -y squid-common squid-openssl apache2-utils wget squidclient libecap3 libecap3-dev

На первом сервере, который будет раскидывать клиентов по остальным, создаем файл с паролем:

htpasswd -c /etc/squid/htpasswd proxy

Обратите внимание, что дополнительные пользователи добавляются уже без -c:

htpasswd /etc/squid/htpasswd proxy2

На остальных серверах пароль нам не нужен - прокси будут разрешены по IP.

4. Конфигурация squid:

Во всех случаях редактируем файл /etc/squid/squid.conf

Сервер 1 в РФ (к которому ходят все клиенты):

https_port 111.222.333.444:12345 cert=/etc/letsencrypt/live/domain1.com/fullchain.pem key=/etc/letsencrypt/live/domain1.com/privkey.pem
https_port [0000:0000:0000::2]:12345 cert=/etc/letsencrypt/live/domain1.com/fullchain.pem key=/etc/letsencrypt/live/domain1.com/privkey.pem

http_port 111.222.333.444:12346
http_port [0000:0000:0000::2]:12346

dns_nameservers 8.8.8.8 1.1.1.1

# Authentication settings
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/htpasswd
auth_param basic realm proxy

# Access control lists (ACLs)
acl authenticated proxy_auth REQUIRED
acl primary dstdomain "/etc/squid/squid-whitelist-zones.conf"
acl hasRequest has request
acl allowed_ips src 000.000.000.000

access_log daemon:/var/log/squid/access.log hasRequest

# Access rules
http_access allow allowed_ips        # Allow these IPs without authentication
http_access allow authenticated       # Allow authenticated users
http_access deny all                  # Deny all others

# Cache settings
cache_peer domain2.com parent 14881 0 no-query no-digest ssl sslflags=DONT_VERIFY_PEER sourcehash
cache_peer_access domain2.com allow primary
cache_peer_access domain2.com deny all

cache_peer 555.555.555.555 parent 14881 0 no-query no-digest ssl sslflags=DONT_VERIFY_PEER sourcehash
cache_peer_access 555.555.555.555 allow primary
cache_peer_access 555.555.555.555 deny all

prefer_direct on
never_direct allow primary
never_direct deny all

cache allow all
cache_dir aufs /tmp/squid 1000 16 256
cache_mem 256 MB
memory_cache_mode always
maximum_object_size 4096 KB
maximum_object_size_in_memory 256 KB

pipeline_prefetch 10
collapsed_forwarding on

max_filedescriptors 65535
pinger_enable on

Пояснения по релевантным пунктам:

1 и 2 нужно удалить, если вы не планируете HTTPS. В противном случае, вам нужно поменять путь до сертификата, который вам дал letsencrypt.

2 и 5 нужно удалить, если у вас нет IPV6 адреса.

4 и 5 можно удалить, если вам не нужен HTTP прокси.

В строчке 17 можно указать собственный IP (если он у вас статический) для доступа без пароля. В противном случае, строчки 17 и 22 удаляем.

В строчках 27-29 и 31-33 настраивается маршрутизация до двух серверов (связка из 3 серверов и, потенциально, больше). Первый пример с хождением по домену, второй - по IP. Меняем на свои значения. Учтите, что настраивать один прокси и как домен, и как IP, не нужно - squid их сам под капотом интерпретирует в IP. Поэтому, если у вас только один прокси в другой стране - оставляйте один блок, не два.

По параметрам cache_peer: sourcehash нужен для того, чтобы зарубежный сервер для маршрутизации выбирался исходя из IP клиента (раскидывать клиентов более или менее равномерно). Если сервер один, sourcehash нужно убрать.

ssl можно убрать, но тогда шифрования между серверами не будет, со всеми вытекающими. Это если у вас нет домена и HTTPS.

sslflags=DONT_VERIFY_PEER не обязателен, но иногда squid перекрывает, и он начинает терять подключение между серверами. Этот флаг помогает. По принципу Неуловимого Джо, MITM между проксями мы не боимся. Если вы убрали ssl, то и sslflags убираем тоже.

Кеш, откровенно говоря, можно отключить, удалив строчки с 39 по 44. Он все равно ничего не делает для HTTPS трафика. Сэкономим место на диске и в памяти. Решение ваше.

Всю остальную магию оставляем без изменений.

Сервер 2 (и последующие серверы, к которым ходит первый):

https_port 444.333.222.111:12345 cert=/etc/letsencrypt/live/domain2.com/fullchain.pem key=/etc/letsencrypt/live/domain2.com/privkey.pem
https_port [9999:9999:9999::2]:12345 cert=/etc/letsencrypt/live/domain2.com/fullchain.pem key=/etc/letsencrypt/live/domain2.com/privkey.pem

http_port 444.333.222.111:12346
http_port [9999:9999:9999::2]:12346

# Access control lists (ACLs)
acl A_proxy src domain1.com
acl B_proxy src 111.222.333.444

# Access rules
http_access allow A_proxy
http_access allow B_proxy
http_access deny all

cache allow all
cache_dir aufs /tmp/squid 1000 16 256
cache_mem 256 MB
memory_cache_mode always
maximum_object_size 4096 KB
maximum_object_size_in_memory 256 KB

# Additional settings
max_filedescriptors 65535
pinger_enable on

Здесь логика первых 5 строчек та же, что и выше: меняем IP, опционально убираем HTTP или HTTPS, опционально убираем IPV6.

Важно, что здесь в строчках 8 и 9 мы разрешаем доступ по IP с первого сервера в РФ. Можно разрешить с домена, но я предпочитаю перебдеть и разрешить оба варианта.

Кеш, аналогично, можно отключить (16-21).

Конфигурация сервера 2 на этом закончена, но нам все еще нужен список заблокированных доменов для сервера 1.

5. Добываем список доменов для маршрутизации

Для самых ленивых я выкладываю сегодняшний squid-whitelist-zones.conf

Этот файл нам нужен на первом сервере в РФ. Кладем по пути из конфигурации в строчке 15: acl primary dstdomain "/etc/squid/squid-whitelist-zones.conf"

Обновлять я его не буду, так что собирайте лучше свой (или ищите готовый для squid). Делать это будем на забугорном сервере, чтобы избежать вмешательства РКН.

Для сборки, воспользуемся antizapret-pac-generator-light:

mkdir /opt/antizapret-pac-generator-light
cd /opt/antizapret-pac-generator-light
git clone https://bitbucket.org/anticensority/antizapret-pac-generator-light/src/master/ .
mkdir /etc/nginx/proxy/

Отредактируем файл doall.sh, чтобы он копировал готовый .conf файл для раздачи через nginx:

nano doall.sh

#!/bin/bash

set -e

HERE="$(dirname "$(readlink -f "${0}")")"
cd "$HERE"

./update.sh
./parse.sh
./process.sh

cp /opt/antizapret-pac-generator-light/result/squid-whitelist-zones.conf /etc/nginx/proxy/squid-whitelist-zones.conf
chown www-data:root /etc/nginx/proxy/squid-whitelist-zones.conf

Добавляем в cron crontab -e:

0 0 * * * /opt/antizapret-pac-generator-light/doall.sh

Разрешаем выполнение:

chmod -R +x /opt/antizapret-pac-generator-light

Если каких-то доменов в стандартной конфигурации будет не хватать (или вы, например, хотите добавить домены, которые блокируют IP РФ), их можно добавить в файл /opt/antizapret-pac-generator-light/config/include-hosts-custom.txt, например:

twitter.com
twimg.com
x.com
chatgpt.com

Теперь, файл нужно донести до первого сервера. Тут нам и пригодится nginx. Редактируем /etc/nginx/sites-enabled/default

Нам нужно добавить папку с файлом. В https блок добавляем:

location /cheburnet/ {
    alias /etc/nginx/proxy/;
    allow all;
}

После чего nginx перезапускам service nginx restart

Теперь на первом сервере, раскидывающем клиентов, ежедневно забираем новый файл. Я предпочитаю для этого использовать свой скрипт. Создаем его /etc/squid/update.sh:

#!/bin/bash

set -e

# Download the latest whitelist
wget -O /etc/squid/squid-whitelist-zones.conf https://domain2.com/cheburnet/squid-whitelist-zones.conf

# Remove all domains ending with .ua
sed -i '/\.ua$/d' /etc/squid/squid-whitelist-zones.conf

# Manually remove problematic domains that are subdomains
sed -i '/\.sex2\.kirov\.life$/d' /etc/squid/squid-whitelist-zones.conf
sed -i '/\.sex\.kirov\.life$/d' /etc/squid/squid-whitelist-zones.conf

# Reload Squid configuration with low priority
nice -n 19 squid -k reconfigure

Скрипт подчищает несколько доменов, которые засоряют выдачу service squid status

Не забываем указать свой реальный домен в строчке 6. Путь должен открываться из браузера. Если вам нужны домены в зоне UA, удаляем строчку 9. Разрешаем выполнение chmod +x /etc/squid/update.shи закидываем в cron crontab -e:

0 1 * * * /etc/squid/update.sh

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

Для первого запуска и тестов, оба скрипта запускаем руками, не дожидаясь часа X.

На втором:

./opt/antizapret-pac-generator-light/doall.sh

На первом:

./etc/squid/update.sh

На первом сервере должен появиться файл /etc/squid/squid-whitelist-zones.conf на 3+ мегабайт.

Второй скрипт применяет все настройки, что мы прописывали в squid.conf. На будущее, это делается либо запросом к самому squid перечитать конфигурацию squid -k reconfigure, либо перезапуском сервиса: service squid restart

Кроме того, для экспериментов у squid есть еще squid -k check, который проверит ваш конфиг не роняя прокси.

Проверяем service squid status

В идеале, мы хотим видеть относительно чистый лог:

squid.service - Squid Web Proxy Server
     Loaded: loaded (/usr/lib/systemd/system/squid.service; enabled; preset: enabled)
     Active: active (running) since Sun 2024-12-15 17:02:24 MSK; 47min ago

Dec 15 17:02:24 server1.com squid[1989315]: Accepting HTTP Socket connections at conn9 local=[censored]:censored remote=[::] FD 18 flags=9
                                                       listening port: [censored]:censored
Dec 15 17:02:24 server1.com squid[1989315]: Done reading /tmp/squid swaplog (1468 entries)
Dec 15 17:02:24 server1.com squid[1989315]: Finished rebuilding storage from disk.
                                                          1468 Entries scanned
                                                             0 Invalid entries
                                                             0 With invalid flags
                                                          1468 Objects loaded
                                                             0 Objects expired
                                                             0 Objects canceled
                                                             0 Duplicate URLs purged
                                                             0 Swapfile clashes avoided
                                                       Took 0.02 seconds (73499.22 objects/sec).
Dec 15 17:02:24 server1.com squid[1989315]: Beginning Validation Procedure
Dec 15 17:02:24 server1.com squid[1989315]: Completed Validation Procedure
                                                       Validated 1468 Entries
                                                       store_swap_size = 44888.00 KB
Dec 15 17:02:24 server1.com squid[1989315]: Configuring Parent server2.com
Dec 15 17:02:24 server1.com squid[1989315]: Configuring Parent server3.com
Dec 15 17:02:24 server1.com squid[1989315]: Starting new basicauthenticator helpers...
                                                       current master transaction: master54
Dec 15 17:02:24 server1.com squid[1989315]: helperOpenServers: Starting 1/20 'basic_ncsa_auth' processes
                                                       current master transaction: master54
Dec 15 17:02:25 server1.com squid[1989315]: storeLateRelease: released 0 objects

Все серверы должны быть active (running). Также сервер 1 в РФ должен видеть "родителей": Configuring Parent <...>

Если это так, то прокси можно пробовать.

Иногда подключение между проксями может умирать:

Dec 15 17:50:02 domain1.com squid[1989315]: ERROR: Connection to domain2.com failed
                                                       current master transaction: master9185

Само по себе, это не проблема, если подключение после этого восстанавливается. Проблема - это когда подключение умирает вообще:

Dec 15 16:58:28 domain1.com squid[12847]: Detected DEAD Parent: domain2.com
                                                     current master transaction: master376639

Обычно это означает, что зарубежный сервер (parent) упал или по какой-то причине не пускает к себе первый сервер. Если проблема всплывает после первой настройки, то проверяем, разрешили ли мы доступ от сервера 1 к серверу 2 в настройках squid и в файерволле и применили ли вообще новую конфигурацию. Смотрим логи. Проверяем, что на сервере РФ есть корректный squid-whitelist-zones.conf

Иногда, сервер умирает вообще просто потому что. Я заметил, что такое случается реже (или не случается вовсе), если для подключения от сервера к серверу использовать IP вместо домена.

В целом, проблемы с подключением изредка случаются, но особых неудобств не доставляют. Как правило, чинятся сами. В любой непонятной ситуации - перезагружайте сервисы squid, а если не поможет - сами сервера.

Пример конфигурации FoxyProxy:

Если у вас не статический IP, то нужно будет указать username и password. Есть смысл отключить проксирование локальных ресурсов - для этого есть пресеты в выпадающем меню слева, quick add. Следующим после прокси нужно будет добавить DIRECT.
Если у вас не статический IP, то нужно будет указать username и password. Есть смысл отключить проксирование локальных ресурсов - для этого есть пресеты в выпадающем меню слева, quick add. Следующим после прокси нужно будет добавить DIRECT.

6. Что еще можно сделать:

Все здесь будет коротко и тезисно, потому что либо гуглится, либо экспериментально.

Как я уже писал выше, раз уж у нас есть nginx, можно развернуть x-ui с websocket и grpc. Само-собой, это делается на забугорных серверах. Очень коротко:

Ставим x-ui, добавляем inbound, слушаем 127.0.0.1 с protocol vless и transmission grpc на произвольном порту (например, 8888). Service Name - это ваш путь для nginx. Таким образом, в nginx идет:

location /ServiceName {
        if ($content_type !~ "application/grpc") {
            return 404;
        }
        client_max_body_size 0;
        client_body_buffer_size 512k;
        grpc_set_header X-Real-IP $remote_addr;
        client_body_timeout 1w;
        grpc_read_timeout 1w;
        grpc_send_timeout 1w;
        grpc_pass grpc://127.0.0.1:8888;
        }

Аналогично для websocket:

Добавляем inbound, слушаем 127.0.0.1 с protocol vless и transmission websocket на произвольном порту (например, 8889). Path - это ваш путь для nginx. В nginx идет:

location /SecretPath {
        if ($http_upgrade != "websocket") {
          return 404;
        }
        proxy_pass http://127.0.0.1:8889;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 52w;
        }

В x-ui для обоих inbound включаем external proxy: TLS | вашдомен.com | 443, security: none.


Настроить ежедневное создание отчетов ИЛИ отключить логгинг squid.

Отключаем логи: на первом сервере в файле squid.conf строчку

access_log daemon:/var/log/squid/access.log hasRequest

Меняем на

access_log none

... либо настраиваем отчеты в Telegram (тоже РФ сервер):

Для меня и моих друзей не заблокированными остаются ~80% запросов. Большая часть заблокированных, само-собой, ютуб.
Для меня и моих друзей не заблокированными остаются ~80% запросов. Большая часть заблокированных, само-собой, ютуб.

Устанавливаем calamaris: apt install calamaris

Создаем скрипт (написано на коленке ChatGPT, но работает):

nano /etc/squid/send_squid_report_telegram.sh

Меняем строчки 4 и 6.

#!/bin/bash
set -x  # Enable command tracing
# Telegram Bot API Token
TOKEN="ваш:токен"
# Chat ID where to send the report
CHAT_ID="чатид"

# Generate Calamaris report in HTML format and save to a temporary file
REPORT=$(mktemp --suffix=".html")
calamaris -a --output-format html /var/log/squid/access.log.1 > "$REPORT"

# Generate plain text Calamaris report for parsing
TEXT_REPORT=$(mktemp)
calamaris -a /var/log/squid/access.log.1 > "$TEXT_REPORT"

# Extract Total Requests
TOTAL_REQUESTS=$(awk '/^Total amount:/ && /requests/ {print $NF}' "$TEXT_REPORT")

# Extract Total Bandwidth (in bytes)
TOTAL_BANDWIDTH=$(awk '/^Total Bandwidth:/ {gsub(/K$/, "", $NF); print $NF * 1024}' "$TEXT_REPORT")
if [[ "$TOTAL_BANDWIDTH" =~ ^[0-9]+$ ]]; then
    HUMAN_READABLE_BANDWIDTH=$(echo "$TOTAL_BANDWIDTH" | awk '{printf "%.2f MB", $1 / 1024 / 1024}')
else
    HUMAN_READABLE_BANDWIDTH="Data not available"
fi

# Extract Outgoing Requests by Destination (IPs, DIRECT, and percentage)
OUTGOING_STATUS=$(awk '/^# Outgoing requests by destination/,/^Sum/ {
    if ($1 != "#" && $1 != "Sum" && $1 != "------------------------------")
        if ($1 != "neighbor") print "- " $1 ": " $3 "%"
}' "$TEXT_REPORT")

# Fallback for extraction
if [ -z "$TOTAL_REQUESTS" ]; then TOTAL_REQUESTS="Data not available"; fi
if [ -z "$HUMAN_READABLE_BANDWIDTH" ]; then HUMAN_READABLE_BANDWIDTH="Data not available"; fi
if [ -z "$OUTGOING_STATUS" ]; then OUTGOING_STATUS="Data not available"; fi

# Message with key metrics
MESSAGE="Here's a summary of the daily Squid proxy report:
- **Total Requests**: $TOTAL_REQUESTS
- **Total Traffic**: $HUMAN_READABLE_BANDWIDTH

Outgoing Requests by Destination:
$OUTGOING_STATUS

Full report attached."

# Send the HTML report as a document with proper filename
curl -s -X POST \
    "https://api.telegram.org/bot$TOKEN/sendDocument" \
    -F chat_id="$CHAT_ID" \
    -F document="@$REPORT;filename=calamaris_report.html" \
    -F caption="$MESSAGE"

# Clean up temporary files
rm "$REPORT" "$TEXT_REPORT"

Разрешаем запуск chmod +x /etc/squid/send_squid_report_telegram.sh

Настраиваем запуск скрипта после logrotate:

nano /etc/logrotate.d/squid

#
#       Logrotate fragment for squid.
#
/var/log/squid/*.log {
        daily
                missingok
                notifempty
        compress
        delaycompress
                maxsize 100M
        rotate 2
        nocreate
        sharedscripts
        prerotate
                test ! -x /usr/sbin/sarg-reports || /usr/sbin/sarg-reports daily
        endscript
        postrotate
                test ! -e /run/squid.pid || test ! -x /usr/sbin/squid || /usr/sbin/squid -k rotate
                /etc/squid/send_squid_report_telegram.sh
        endscript
}

Добавить секьюрности в конец конфига:

forwarded_for delete
httpd_suppress_version_string on
via off

Не отдавать на запрос без логина и пароля 407.

Теоретически, немного поможет от active probing. Однако, такая конфигурация ломает доступ по IP без пароля (поэтому его из конфига я убрал).

Кроме того, такая конфигурация может быть небезопасна, потому что мы пускаем всех без разбору, а потом рубим подключение тем, кто зашел без пароля. Отклонять их до входа безопаснее, но такой подход отдает 407 из-за логики работы Squid, что раскрывает существование прокси.

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

* CONNECT tunnel: HTTP/1.1 negotiated
* allocate connect buffer
* Establish HTTP proxy tunnel to 2ip.ru:443
> CONNECT 2ip.ru:443 HTTP/1.1
> Host: 2ip.ru:443
> User-Agent: curl/8.5.0
> Proxy-Connection: Keep-Alive
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS alert, close notify (256):
* Proxy CONNECT aborted
* Closing connection
* Send failure: Connection reset by peer
curl: (56) Proxy CONNECT aborted

Для использования этого костыля, поменять нужно этот блок:

<...>
# Authentication settings
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/htpasswd
auth_param basic realm proxy

# Access control lists (ACLs)
acl primary dstdomain "/etc/squid/squid-whitelist-zones.conf"
acl hasRequest has request
acl unauthenticated proxy_auth NONE  # ACL for unauthenticated users

access_log daemon:/var/log/squid/access.log hasRequest

# Access rules
# Allow unauthenticated users to proceed without authentication but redirect to a different response or location
http_access allow unauthenticated

# Allow authenticated users
http_access allow !unauthenticated    # Allow any user who provides credentials (this won't trigger 407 for unauthenticated)
http_access deny all                  # Deny all others

# Redirect unauthenticated users to another response or URL
http_reply_access allow unauthenticated
deny_info TCP_RESET unauthenticated  # or use a custom error page

# Cache settings
<...>

С прямым доступом по location в nginx есть смысл что-то сделать. В идеале, разместить правдоподобные медиа-файлы, чтобы оправдать трафик. Если готовы быть тупым прокси для какого-нибудь сайта, то можно завернуть его целиком (не рекомендую, но как пример).

location / {
    proxy_pass https://vk.com/;
        }

Заблокировать РКН:

Я собрал скрипт для блокировки сомнительных адресов РФ. Теоретически, он слегка подпортит жизнь РКН. Я скажу честно, я даже не уверен, что он работает. По факту, это больше для души. Маскировке это только помешает:

nano /usr/local/bin/update_blocklist.sh

#!/bin/bash

# Variables
BLACKLIST_URL="https://raw.githubusercontent.com/C24Be/AS_Network_List/refs/heads/main/blacklists/blacklist.txt"
BLACKLIST_FILE="/tmp/blacklist.txt"
UFW_BEFORE_RULES="/etc/ufw/before.rules"
BACKUP_FILE="/etc/ufw/before.rules.bak"

# Function to validate IPv4 CIDR
validate_cidr() {
    local cidr="$1"
    if [[ "$cidr" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]]; then
        ip="${cidr%/*}"
        mask="${cidr#*/}"
        if [[ $mask -ge 0 && $mask -le 32 ]]; then
            return 0  # Valid CIDR
        fi
    fi
    return 1  # Invalid CIDR
}

# Download the blacklist
echo "Downloading blacklist from $BLACKLIST_URL..."
wget -q -O "$BLACKLIST_FILE" "$BLACKLIST_URL"
if [ $? -ne 0 ]; then
    echo "Error: Failed to download blacklist." >&2
    exit 1
fi

# Backup the original before.rules
if [ ! -f "$BACKUP_FILE" ]; then
    echo "Backing up $UFW_BEFORE_RULES to $BACKUP_FILE..."
    cp "$UFW_BEFORE_RULES" "$BACKUP_FILE"
fi

# Prepare new rules for BLACKLIST
BLACKLIST_RULES="/tmp/blacklist.rules"
{
    echo "# BLACKLIST rules"
    echo ":BLACKLIST - [0:0]"
    while IFS= read -r ip; do
        [[ "$ip" =~ ^#.*$ || -z "$ip" ]] && continue  # Skip comments and empty lines
        if validate_cidr "$ip"; then
            echo "-A BLACKLIST -s $ip -j DROP"
            echo "-A BLACKLIST -d $ip -j DROP"
        else
            echo "Skipping invalid CIDR: $ip" >&2
        fi
    done < "$BLACKLIST_FILE"
    echo "# Apply BLACKLIST chain"
    echo "-A ufw-before-input -j BLACKLIST"
    echo "-A ufw-before-output -j BLACKLIST"
    echo "-A ufw-before-forward -j BLACKLIST"
} > "$BLACKLIST_RULES"

# Integrate blacklist.rules into before.rules
echo "Integrating blacklist rules into $UFW_BEFORE_RULES..."
cp "$BACKUP_FILE" "$UFW_BEFORE_RULES"
sed -i '/# End required lines/ r /tmp/blacklist.rules' "$UFW_BEFORE_RULES"

# Reload UFW
echo "Reloading ufw to apply changes..."
ufw reload

# Clean up
rm "$BLACKLIST_RULES"

echo "Blacklist updated successfully!"

Разрешаем выполнение: chmod +x /usr/local/bin/update_blocklist.sh

Добавляем в cron crontab -e:

0 0 * * * /usr/local/bin/update_blocklist.sh

Уже упоминалось выше, но всю коммуникацию между серверами стоит разрешить в файеровлле ufw и в настройках самого squid.

ufw allow from и ufw allow to на каждом сервере к каждому другому серверу.

В squid.conf на забугорных серверах разрешаем сервер РФ по всем его адресам:

acl A_proxy src domain1.com
acl B_proxy src 111.222.333.444
acl С_proxy src [9999:9999:9999::2]

http_access allow A_proxy
http_access allow B_proxy
http_access allow С_proxy
http_access deny all

На этом, пожалуй, все.