Не удается получить данные по TPROXY UDP.

Вопрос или проблема

Я считаю, что сталкиваюсь с проблемой маршрутизации на своей системе (это стандартная установка Ubuntu 22), но я не могу понять, как подойти к отладке данной проблемы.

Следуя уроку по прозрачному прокси из ядра, я пытаюсь внедрить прозрачный прокси для всего исходящего UDP-трафика с целевым портом 53 (то есть, я пытаюсь фильтровать все DNS-запросы на своей системе).

После множества онлайн-уроков, вот настройка, которая, как я думаю, должна работать (но на самом деле не работает):

# iptables -t mangle -N DIVERT
# iptables -t mangle -A PREROUTING -p udp -m socket -j DIVERT
# iptables -t mangle -A DIVERT -j MARK --set-mark 1
# iptables -t mangle -A DIVERT -j ACCEPT
# ip rule add fwmark 1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100
# iptables -t mangle -A PREROUTING -p udp --dport 53 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 5354

Код на C для фактического UDP-сокета, который ожидает входящий трафик:

void intercept()
{
    int fd;
    ssize_t bytes_received_len;
    char buffer[BUFFER_SIZE];

    struct sockaddr_in inbound_addr, forward_server_addr;
    socklen_t addr_len = sizeof(inbound_addr);

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("не удалось создать входной сокет");
        exit(EXIT_FAILURE);
    }

    int val = 1;
    setsockopt(fd, SOL_IP, IP_TRANSPARENT, &val, sizeof(val));

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(5354);

    bind(fd, (struct sockaddr *)&addr, sizeof(addr));

    for (;;)
    {
        printf("ожидание на порту 5354\n");

        bytes_received_len = recvfrom(fd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&inbound_addr, &addr_len);

        printf("получено %zd байт\n", bytes_received_len);

        if (bytes_received_len == 0)
        {
            continue;
        }
    }
}

Вся обработка ошибок опущена для ясности.

Как вы можете видеть из команд iptables, я пытаюсь перенаправить исходящий трафик на порт 53 в мой локальный прокси на 127.0.0.1: 5354. На самом деле я никогда не получаю ничего полезного из recvfrom, даже если у меня действительно есть DNS-запросы на системе, и они работают, но, похоже, они полностью обходят прокси.

Я что-то действительно упустил?

Я изучил все эти статьи:

Любая подсказка была бы очень полезна. Кажется, что я упускаю что-то действительно очевидное.

В конце концов, TPROXY сейчас не является хорошим решением для этого случая использования, так как он не подходит для проксирования трафика, генерируемого локально на одном и том же компьютере с процессом прокси.

Лучший способ решить это - реализовать модуль ядра и использовать API netfilter.

Если вы хотите проксировать локальный (не с внешних машин) трафик, вам нужно использовать OUTPUT вместо PREROUTING для обработки локальных пакетов, правильно?

И в этом случае вам не нужен --tproxy-mark. Команды будут такими:

# iptables -t mangle -A OUTPUT -p udp --dport 53 -j MARK --set-mark 1
# ip rule add fwmark 1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100
# iptables -t mangle -A PREROUTING -p udp --dport 53 -j TPROXY --on-port 5354

Смотрите мой пост на dev.to для подробностей.

Ответ или решение

Для успешного перехвата и проксирования UDP трафика через TPROXY на локальном сервере, вам нужно учитывать некоторые важные моменты, особенно если вы хотите перехватывать локальный трафик, созданный именно этой машиной. Ниже приводится подробное решение вашей проблемы:

Решение проблемы с TPROXY для локального трафика

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

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

    # Создание новой цепочки DIVERT
    iptables -t mangle -N DIVERT
    
    # Маркировка исходящих пакетов, направленных на порт 53
    iptables -t mangle -A OUTPUT -p udp --dport 53 -j MARK --set-mark 1
    
    # Настройка таблицы маршрутизации для подмены с размеченными пакетами
    ip rule add fwmark 1 lookup 100
    ip route add local 0.0.0.0/0 dev lo table 100
    
    # Перенаправление трафика на локальный прокси-сервер
    iptables -t mangle -A OUTPUT -p udp --dport 53 -j TPROXY --on-port 5354 --tproxy-mark 0x1/0x1
  2. Настройка сокета:
    Ваш C-код, который вы привели, выглядит в целом правильно. Но убедитесь, что вы настроили сокет так, чтобы он мог принимать пакеты, и у вас есть соответствующая обработка ошибок. Например, добавив вывод ошибок в случае неудачи:

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
       perror("bind failed");
       exit(EXIT_FAILURE);
    }
  3. Тестирование конфигурации:
    Убедитесь, что ваш сокет действительно находится в ожидании на порту 5354. Вы можете использовать утилиты, такие как netcat или dig, для отправки DNS-запросов и проверки, поступают ли они в ваш прокси:

    dig @127.0.0.1 -p 53 google.com
  4. Убедитесь, что TPROXY поддерживается:
    Проверьте, что ваше ядро поддерживает TPROXY, и все необходимые модули загружены. Вы можете выполнить команду:

    lsmod | grep tproxy

Если модуль не загружен, вам может потребоваться добавить его в конфигурацию ядра или установить необходимые пакеты.

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

    tcpdump -i lo -U port 5354

Заключение

Следуя этим шагам, вы должны быть в состоянии успешно перехватывать и проксировать исходящие DNS-запросы на вашем сервере Ubuntu. Убедитесь в правильной настройке сетевых правил и корректной работе сокета. Если у вас возникнут дополнительные сложности, дальнейшая отладка и использование сетевых утилит могут оказать вам помощь в обнаружении проблем.

Оцените материал
Добавить комментарий

Капча загружается...