Шейпинг исходящего трафика для TCP порта в Linux
Содержание
- 1 Шейпинг исходящего трафика для TCP порта в Linux
Шейпинг исходящего трафика для TCP порта в Linux
Инструкция описывает, как ограничить (шейпить) исходящую полосу пропускания для конкретного TCP-порта в зависимости от гео-принадлежности IP-адреса клиента (страны), используя nftables и tc/HTB.
Описание
Готовый скрипт geo-shaper.sh делает следующее:
- скачивает базу MaxMind GeoLite2-Country (CSV) и строит из неё списки подсетей (CIDR) по странам;
- через nftables помечает (fwmark) исходящие TCP-пакеты с заданного порта в зависимости от того, к какой группе стран относится IP-адрес назначения;
- через tc (HTB) создаёт по одному классу на каждую группу и ограничивает скорость каждой группы своим значением.
Важно: шейпинг применяется к исходящему (исходящему от сервера) трафику на указанном сетевом интерфейсе, для TCP-пакетов с source port, равным указанному порту.
Схема поддерживает три группы (тарифа) по полосе пропускания:
- группа 1 - свой список стран, свой лимит;
- группа 2 - свой список стран, свой лимит;
- группа 3 - всегда catch-all, то есть ловит весь трафик на этом порту, который не попал в группы 1 и 2 (даже если для неё указан непустой список стран - она всё равно работает как «всё остальное», см. раздел «Параметры» ниже).
Требования
Пример установки приведён для Ubuntu 22.04 LTS / 24.04 LTS (для Debian 11/12 команды идентичны, пакет-менеджер тот же - apt).
Необходимые пакеты:
| Пакет | Назначение |
|---|---|
| nftables | классификация трафика (fwmark) по подсетям стран |
| iproute2 | утилита tc - собственно шейпинг (HTB)
|
| curl | загрузка скрипта и базы GeoIP |
| unzip | распаковка архива с базой MaxMind GeoLite2-Country CSV |
| gawk | разбор и объединение CSV-файлов базы GeoIP |
Установка пакетов
sudo apt update sudo apt install -y nftables iproute2 curl unzip gawk sudo systemctl enable --now nftables
Загрузка скрипта
Скрипт можно скачать по прямой ссылке и сохранить, например, в /opt/geo-shaper.sh:
sudo curl -fsSL -o /opt/geo-shaper.sh \ https://wiki.myarena.ru/soft/geo-shaper.sh sudo chmod +x /opt/geo-shaper.sh
Как узнать имя сетевого интерфейса
Перед запуском скрипта нужно знать имя интерфейса (например, eth1, enp1s0), через который сервер отдаёт трафик наружу.
Способ 1: автоматически, только имя
Команда определяет, через какой интерфейс реально уходит трафик в интернет, и выводит только его имя:
ip route get 1.1.1.1 | grep -oP 'dev \K\S+'
Пример вывода:
eth1
Способ 2: список всех интерфейсов и как выбрать нужный
ip -br addr show
Пример вывода:
lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 10.0.0.5/24 fe80::a00:27ff:fe.../64 eth1 UP 203.0.113.10/24 fe80::a00:27ff:fe.../64 docker0 DOWN 172.17.0.1/16
Как понять, какой интерфейс нужен:
-
lo(loopback) - всегда игнорировать, это не сетевая карта. - Виртуальные интерфейсы вроде
docker0,br-*,veth*,tun*,tap*обычно тоже не нужны, если только вы не шейпите именно докер-сеть. - Нужен интерфейс в состоянии UP.
- Выбирайте интерфейс, у которого назначен публичный/внешний IP-адрес сервера - тот, на который клиенты подключаются к вашему сервису. В примере выше это
eth1с адресом203.0.113.10. - Если сомневаетесь - сверьте с выводом команды из способа 1 (
ip route get 1.1.1.1), она укажет ровно тот интерфейс, что смотрит в интернет.
Более подробная информация (MAC-адрес, MTU, статистика) - ip addr show или классический ifconfig -a (если установлен пакет net-tools).
Синтаксис запуска
sudo ./geo-shaper.sh <интерфейс> <порт> <лимит1_bps> <страны1> <лимит2_bps> <страны2> <лимит3_bps> <страны3>
Параметры
- интерфейс - сетевой интерфейс, на котором шейпится исходящий трафик (например,
eth1,enp1s0). - порт - TCP-порт (source port), для которого применяется шейпинг, например
443или80. - лимит1_bps / лимит2_bps / лимит3_bps - лимит полосы в битах в секунду (не байтах), например
700000000= 700 Мбит/с. - страны1 / страны2 - список ISO-кодов стран через запятую в кавычках, например
"RU,UA,KZ,BY". Для этих стран действует свой лимит. - страны3 - список стран для третьей группы. Группа 3 всегда работает как «все остальные страны» - то есть ловит весь трафик на данном порту, не попавший в группы 1 и 2, независимо от того, что указано в этом параметре. Обычно сюда передают просто пустую строку
"".
Скрипт сам скачивает и кэширует базу MaxMind GeoLite2-Country (обновляется раз в 7 дней), поэтому первый запуск может занять чуть больше времени, чем последующие.
Пример: лимит для стран СНГ 700 Мбит/с, для остальных - 200 Мбит/с
В этом сценарии нужны фактически только два тарифа, поэтому вторая группа оставляется с пустым списком стран (не используется), а вся «остальная» полоса (третья группа) ограничивается 200 Мбит/с.
sudo /opt/geo-shaper.sh eth1 80 \ 700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" \ 200000000 "" \ 200000000 ""
Что при этом происходит:
- 700 Мбит/с - суммарный лимит для трафика в адрес IP из стран СНГ: RU, BY, KZ, KG, TJ, TM, UZ, MD, AM, AZ, UA (список условный, набор стран можно скорректировать под свои задачи);
- вторая группа не используется (пустой список стран, никакой трафик под неё не попадёт);
- 200 Мбит/с - лимит для всего остального трафика на порту 80 (все страны, не вошедшие в список выше).
Замените eth1 на свой интерфейс и 80 на реальный порт сервиса.
Проверка работы
Убедиться, что правила загружены и трафик классифицируется:
nft list table inet geoshaper tc -s class show dev eth1 tc -s qdisc show dev eth1
Счётчики пакетов/байт в выводе этих команд должны расти по мере того, как идёт трафик на шейпленный порт - так можно убедиться, что классификация по странам и ограничение скорости реально работают.
Автозагрузка (запуск при старте сервера)
Правила tc и nftables, которые создаёт скрипт, не сохраняются при перезагрузке сервера - после ребута их нужно применить заново. Проще всего сделать это через systemd-сервис, который будет запускать geo-shaper.sh автоматически после старта сети.
1. Создать unit-файл
sudo nano /etc/systemd/system/geo-shaper.service
Содержимое файла (замените интерфейс, порт и параметры на свои - в примере используются значения из раздела «Пример» выше):
[Unit] Description=GEO-based TCP traffic shaping (geo-shaper.sh) After=network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/bash -c '/opt/geo-shaper.sh eth1 80 700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" 200000000 "" 200000000 ""' [Install] WantedBy=multi-user.target
Пояснения:
-
After=network-online.target/Wants=network-online.target- гарантирует, что сервис стартует только после того, как сеть реально поднята (интерфейс существует, есть доступ в интернет для загрузки базы GeoIP). -
Type=oneshot+RemainAfterExit=yes- скрипт не "висит" в фоне, а один раз настраиваетnftables/tcи завершается; при этом systemd считает сервис активным (не упавшим). - Команда обёрнута в
/bin/bash -c '...'- это надёжнее, чем передавать аргументы напрямую вExecStart, особенно из-за пустых строк""в параметрах стран, которые старые версии systemd могут разобрать некорректно без обёртки в bash.
2. Включить и запустить сервис
sudo systemctl daemon-reload sudo systemctl enable --now geo-shaper.service
3. Проверить статус
systemctl status geo-shaper.service
Должно быть видно active (exited) - это нормальное конечное состояние для Type=oneshot с RemainAfterExit=yes.
Изменение параметров в будущем
Если нужно поменять лимиты или списки стран - отредактируйте строку ExecStart в файле /etc/systemd/system/geo-shaper.service, затем примените изменения:
sudo systemctl daemon-reload sudo systemctl restart geo-shaper.service