Уже не школьник & Bсё ещё блогер
Прозрачный прокси: настройки

Введение

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

Автоматизация запуска

Юнит systemd

[Unit]
Description=Privoxy/Squid transparent proxy
After=network.target

[Service]
ExecStart=/root/startup_proxy.sh      # <--запуск
ExecStop=/root/stopdown_proxy.sh      # <--очистка
RemainAfterExit=yes
Type=simple

[Install]
WantedBy=multi-user.target

Он элементарен, на нём останавливаться не буду.

Скрипт запуска

Далее скрипт запуска:

  1. Для работы Proxy нужен отдельный netns, т.к. перехват трафика реализован через подмену DNS. Таким образом, для моих устройств ресурсы, требующие обхода, будут разрешать в IP, принадлежащий этому отдельному netns:
NETNS_NAME="proxy"   
NETNS_EXEC="ip netns exec $NETNS_NAME"   
  
ip netns add $NETNS_NAME || exit 10   
  1. Ну и настройка veth для связи между netns и доступом в Интернет из proxy. Отмечу также, что я решил повесить шейпер на исходящий из proxy интерфейс. Важно не забыть:
    1. Добавить маршрут из proxy в Интернет, т.к. маршрута по умолчанию там не создаётся;
    2. Поднять петлю lo, т.к. в новом netns он создастся, но не поднимется;
    3. Настроить SNAT из proxy в Интернет.
ip link add to_proxy type veth peer name to_global || exit 20   
ip addr add ${GLOBAL_NETNS_IP}/24 dev to_proxy || exit 21   
ip link set to_proxy up || exit 22   
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source ${OUTER_IP} || exit 23   
  
ip link set to_global netns $NETNS_NAME || exit 30   
$NETNS_EXEC ip addr add ${PROXY_NETNS_IP}/24 dev to_global || exit 31   
$NETNS_EXEC tc qdisc add dev to_global root tbf rate 15mbit latency 50ms burst 512k || exit 32   
$NETNS_EXEC ip link set to_global up || exit 33   
$NETNS_EXEC ip ro add default via ${GLOBAL_NETNS_IP} || exit 34
$NETNS_EXEC ip link set lo up || exit 35
  1. Запуск всех процессов. В данном списке нет Tor и ещё одного экземпляра Bind9. Они упущены потому, что они уже настроены и ими я пользуюсь и отдельно, и начал пользоваться задолго до поднятия прозрачного прокси.
    1. named — Bind9. Этот экземпляр настроен на пропуск всех запросов стороннему DNS, таким образом он будет разрешать DNS-имена в реальные IP, по которым будут ходить прокси;
    2. privoxy — Прокси-сервер. Он умеет в прозрачность, но не умеет в подмену сертификатов. Используется на 80 порту как прозрачный, и им же пользуется Squid как непрозрачным для MITM атаки на HTTPS на своём 443 порту;
    3. squid — Прокси сервер, который умеет в подмену сертификатов. Запускается он весьма экстравагантно, да. В моём репозитории довольно старая версия, возможно с этим связано. С тонной отладочных сообщений в консоли он — субъективное ощущение — работает стабильней, чем без.
$NETNS_EXEC /usr/sbin/named -4 -d 9 -u bind -c /etc/bind/named.conf.proxy || exit 40
$NETNS_EXEC /usr/sbin/privoxy /etc/privoxy/privoxy.config || exit 41
nohup $NETNS_EXEC /usr/sbin/squid -YCNX -d 5 -f /etc/squid/squid_privoxy.conf > /dev/null 2>&1 &  
disown

На этом скрипт запуска пока заканчивается.

Скрипт остановки

Со скриптом остановки всё проще, т.к. не надо расстраивать интерфейсы — достаточно их удалить.

  1. Убиваем все запущенные процессы в proxy. Squid иногда не хочет останавливаться, поэтому ему мы можем послать SIGKILL.
NETNS_NAME="proxy"
ip netns pids $NETNS_NAME | xargs -r kill
sleep 3
ip netns pids $NETNS_NAME | xargs -r kill -SIGKILL
  1. Подчищаем сеть, удалив один из veth(по идее, удалится вместо с братом при удалении netns, но на всякий случай я продублировал) и netns целиком:
ip link delete to_proxy
ip netns delete $NETNS_NAME
  1. Подчищаем правила iptables. По идее, дубликаты правила для SNAT погоды не сделают, т.к., судя по счётчикам, дубликаты не отрабатывают, но для красоты я их всё-таки подчищаю:
while iptables -t nat -D POSTROUTING -o eth0 -j SNAT --to-source ${OUTER_IP}; do : ; done

Конфигурационные файлы

Bind9 основной

В нём мне необходимо подменять ответы DNS-сервера на мой IP. Для этого я подключил файл, в котором перечислены зоны, которые я хочу подменять. Все они достаточно высокого уровня, то есть каждая охватывает конкретный домен, по сути.

Сам файл нет смысла приводить полностью, опишу пару записей для примера:

zone "xvideos.com" IN {
    type master;
    file "/etc/bind/db.myip.ru";
};
zone "xvideos-cdn.com" IN {
    type master;
    file "/etc/bind/db.myip.ru";
};

Как видно, эти настройки говорят обращаться к файлу db.myip.ru. А это уже стандартный файл БД для DNS-сервера:

;
; BIND data file for t-proxy
;
$TTL    604800
@       IN      SOA     . root. (
                              2         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      ${OUTER_IP}
@       IN      A       ${PROXY_NETNS_IP}
*       IN      A       ${PROXY_NETNS_IP}

Перед тем, как публиковать заметку, я максимально обобщил базу данных таким образом, чтобы на любой запрос она отправляла ${PROXY_NETNS_IP}. Работоспособность при этом не пострадала.

Bind9 для прокси

Здесь DNS-сервер уже должен разрешать домены в настоящие адреса, по которым будет ходить прокси. По большому счёту, из-за этого и заводился отдельный netns: чтобы разделить два DNS-сервера на уровне системы. Его конфиг, по сути, является стандартным конфигом Bind9, но с пробросом всех запросов на сторонний DNS.

options {
    directory "/var/cache/bind";
    forwarders {
        8.8.8.8;
    };
    dnssec-validation no;
    auth-nxdomain no;    # conform to RFC1035
    listen-on {
        127.0.0.1;
    };
    allow-query {
        127.0.0.1;
        10.48.0.0/24;
    };
};
include "/etc/bind/named.conf.default-zones";

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

zone "xvideos.com" IN {
    type forward;
    forwarders {
        ${OUTER_IP} port 9053; //TOR
    };
};
zone "xvideos-cdn.com" IN {
    type forward;
    forwarders {
        ${OUTER_IP} port 9053; //TOR
    };
};

Privoxy

Для Privoxy у меня почти стандартная конфигурация. Выглядит так, если отсеять комментарии:

user-manual /usr/share/doc/privoxy/user-manual
confdir /etc/privoxy
logdir /var/log/privoxy
actionsfile match-all.action # Actions that are applied to all sites and maybe overruled later on.
actionsfile default.action   # Main actions file
actionsfile user.action      # User customizations
filterfile default.filter
filterfile user.filter      # User customizations
logfile logfile
debug     8192 # Log the destination for each request Privoxy let through. See also debug 1024.
listen-address  ${PROXY_NETNS_IP}:80
toggle  0
enable-remote-toggle  0
enable-remote-http-toggle  0
enable-edit-actions 0
enforce-blocks 0
buffer-limit 4096
enable-proxy-authentication-forwarding 0
forward-socks5t   /               ${OUTER_IP}:9050 .
forwarded-connect-retries  0
accept-intercepted-requests 1
allow-cgi-request-crunching 0
split-large-forms 0
keep-alive-timeout 300
tolerate-pipelining 1
socket-timeout 300
max-client-connections 512
log-messages   1

По факту, наибольшую пользу наносят следующие две строчки:

listen-address  ${PROXY_NETNS_IP}:80   # <-- откуда слушать
forward-socks5t   /               ${OUTER_IP}:9050 .   # <-- куда проксировать

Squid

По традиции, конфигурация Squid тоже близка к стандартной:

http_access allow all
icp_access allow all
htcp_access allow all
dns_nameservers 127.0.0.1
https_port ${PROXY_NETNS_IP}:443 intercept ssl-bump options=ALL:NO_SSLv3:NO_SSLv2 connection-auth=off dynamic_cert_mem_cache_size=4MB cert=/etc/squid/ssl/squid.pem key=/etc/squid/ssl/squid.key
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER
acl step1 at_step SslBump1
ssl_bump peek step1 all
ssl_bump splice all
ssl_bump server-first all
ssl_bump none all
never_direct allow all
cache_peer ${PROXY_NETNS_IP} parent 80 0 no-query proxy-only no-digest default
cache_peer_access ${PROXY_NETNS_IP} allow all
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER
sslcrtd_program /usr/lib/squid/ssl_crtd -s /var/lib/ssl_db -M 16MB
coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320
logfile_rotate 4
pid_filename /var/run/squid.pid

Из особенностей здесь, как раз, настройка прозрачности HTTPS(Peek and splice):

acl step1 at_step SslBump1
ssl_bump peek step1 all
ssl_bump splice all
ssl_bump server-first all
ssl_bump none all
never_direct allow all

Ещё, по идее, для Squid нужно ключей сгенерировать. Приведу сюда листинг команд для генерации из старой заметки.

openssl genrsa -out /etc/squid/ssl/squid.key
openssl req -new -key /etc/squid/ssl/squid.key -out /etc/squid/ssl/squid.csr
openssl x509 -req -days 3650 -in /etc/squid/ssl/squid.csr -signkey /etc/squid/ssl/squid.key -out /etc/squid/ssl/squid.pem
openssl x509 -in /etc/squid/ssl/squid.pem -outform DER -out squid.der

2022-06-04 18:33