Slack через IPv6 прокси: автоматизация воркспейсов и обход лимитов API
Содержание
- Как Slack видит ваш IP
- Почему IPv6, а не IPv4
- Схема проксирования
- Настройка пула IPv6 на сервере
- !/bin/bash
- SOCKS5 прокси на Python
- Ротация адресов и привязка к воркспейсу
- Обход rate limit через несколько токенов
- Обработка 429 и Retry-After
- Кейс: Slack audit logs и геолокация IP
- Кейс: 429 на conversations.history при синхронизации
- Кейс: блокировка по /64 в OVH
- Мониторинг и health-check
- !/bin/bash
- Ограничения подхода
Как Slack видит ваш IP
Slack не банит по IP. Он считает запросы. Каждый вызов Web API привязан к токену, а токен — к воркспейсу. Rate limit живёт на трёх уровнях: per-method (например, `conversations.history` — 1 запрос в секунду, bursts до 15), per-workspace (общий пул на все методы), и per-app. Когда вы дёргаете API из одного адреса, вы упираетесь в первый же лимит и получаете `429 Too Many Requests` с заголовком `Retry-After`.
Проблема в том, что Slack смотрит не только на токен. Он логирует source IP в audit logs для Enterprise Grid, применяет risk-scoring к сессиям, а при аномалиях (200 запросов с одного адреса за минуту) может временно заморозить app. IPv6 здесь даёт то, чего нет у IPv4: /64 префикс — это 2^64 адресов на одну подсеть. Хостер выдаёт /64 бесплатно, и вы получаете пул, в котором каждый запрос может уйти с нового адреса.
Это не значит, что нужно рандомизировать IP на каждый вызов. Slack видит частую смену адреса у одного токена как подозрительную активность. Нужен баланс: пул адресов, ротация по расписанию, привязка адреса к воркспейсу.
Почему IPv6, а не IPv4
IPv4-прокси стоят денег. Резидентные — от \$3 за гигабайт, датацентровые — \$0.5-1 за IP в месяц, но их быстро жгут. Slack держит базы датацентровых подсетей (AWS, DigitalOcean, Hetzner) и помечает трафик с них как высокорисковый. При логине из такого IP прилетает email-подтверждение или требование 2FA.
IPv6-адресов у провайдеров в избытке. Один /64 от Hetzner или OVH даёт миллиарды адресов. Slack не имеет смысла банить целые /64 — под нож попадут легитимные пользователи. На практике блокировки идут на уровне /128 (отдельный адрес) или /80, если адрес замечен в абьюзе.
Ещё один плюс — маршрутизация. IPv6 не проходит через NAT, нет CGNAT-проблем, нет коллизий портов. Каждое соединение идёт напрямую с уникального адреса.
Схема проксирования
Классическая схема — SOCKS5 или HTTP CONNECT прокси поверх IPv6. Клиент подключается к прокси-серверу, тот открывает исходящее соединение с указанного IPv6-адреса.
```
Client (Slack SDK) -> SOCKS5 proxy -> [2001:db8::1] -> slack.com
```
Прокси-сервер держит пул адресов на интерфейсе. На Linux это делается через `ip -6 addr add`. Один /64 можно раскидать на 1000+ адресов без проблем — ядро держит их в routing table, память не жрёт.
Ключевой момент: Slack использует HTTPS. SNI и Host-заголовок видны прокси, но не видны промежуточным узлам. Прокси должен поддерживать CONNECT для 443 порта и не трогать TLS.
Настройка пула IPv6 на сервере
Берём /64 от провайдера, например `2001:db8:1234::/64`. Добавляем адреса на интерфейс:
```bash
!/bin/bash
PREFIX="2001:db8:1234"
IFACE="eth0"
for i in \$(seq 1 100); do
ip -6 addr add \${PREFIX}::\${i}/64 dev \${IFACE}
done
ip -6 route add default via \${PREFIX}::1 dev \${IFACE}
```
Проверка:
```bash
ip -6 addr show dev eth0 | grep inet6 | wc -l
curl -6 --interface 2001:db8:1234::42 https://api.slack.com/api/api.test -X POST -d "token=xoxb-..."
```
Если провайдер требует NDP-proxy или routed /64 (обычно routed), адреса работают сразу. Если это on-link /64 с общей подсетью — нужен `net.ipv6.conf.all.proxy_ndp=1` и NDP-проксирование на шлюз.
SOCKS5 прокси на Python
Пример рабочего прокси с привязкой к IPv6-адресу. Используем `asyncio` и `socks5` протокол:
```python
import asyncio
import socket
import struct
BIND_ADDR = "2001:db8:1234::42"
async def handle_client(reader, writer):
data = await reader.read(262)
if data[0] != 0x05:
writer.close()
return
writer.write(b"\x05\x00")
await writer.drain()
req = await reader.read(262)
if req[1] != 0x01:
writer.close()
return
addr_type = req[3]
if addr_type == 0x03:
length = req[4]
host = req[5:5+length].decode()
port = struct.unpack(">H", req[5+length:7+length])[0]
else:
writer.close()
return
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.bind((BIND_ADDR, 0))
sock.setblocking(False)
try:
await asyncio.get_event_loop().sock_connect(sock, (host, port))
except Exception:
writer.write(b"\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00")
await writer.drain()
writer.close()
return
writer.write(b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
await writer.drain()
remote_reader, remote_writer = await asyncio.open_connection(sock=sock)
async def pipe(r, w):
try:
while True:
chunk = await r.read(65536)
if not chunk:
break
w.write(chunk)
await w.drain()
except Exception:
pass
finally:
w.close()
await asyncio.gather(
pipe(reader, remote_writer),
pipe(remote_reader, writer)
)
async def main():
server = await asyncio.start_server(handle_client, "::", 1080)
async with server:
await server.serve_forever()
asyncio.run(main())
```
Это минимальный вариант. В продакшене добавляют логирование, выбор адреса из пула, health-check. Для ротации адресов достаточно менять `BIND_ADDR` на основе хеша от токена или round-robin.
Ротация адресов и привязка к воркспейсу
Ротация на каждый запрос — плохо. Slack видит, что один и тот же `xoxb` токен ходит с 50 разных IP за минуту. Это триггерит их anomaly detection.
Правильный подход: один воркспейс — один IPv6 из пула. Воркспейс A всегда ходит с `2001:db8:1234::10`, воркспейс B — с `::11`. Если воркспейсов больше, чем адресов, ротация по времени: каждые 6 часов воркспейс переезжает на новый адрес.
Распределение через хеш:
```python
import hashlib
POOL = [f"2001:db8:1234::{i}" for i in range(2, 254)]
def pick_addr(workspace_id: str) -> str:
h = hashlib.sha256(workspace_id.encode()).digest()
idx = int.from_bytes(h[:4], "big") % len(POOL)
return POOL[idx]
```
Такой подход стабилен: один воркспейс всегда получает один адрес, пока пул не меняется. При добавлении адресов индекс сдвигается — тут нужен consistent hashing, иначе все воркспейсы разом переедут.
Обход rate limit через несколько токенов
IPv6-прокси не увеличивает лимиты. Slack считает запросы по токену, а не по IP. Если у вас один бот с одним токеном, хоть 1000 адресов — 1 запрос в секунду на `conversations.history` останется.
Обход строится на нескольких токенах. Один воркспейс, несколько приложений — у каждого свой токен и свой пул лимитов. Slack допускает это: apps независимы. Дальше распределяем запросы между токенами и адресами.
```python
import time
import random
from slack_sdk import WebClient
TOKENS = ["xoxb-token1", "xoxb-token2", "xoxb-token3"]
PROXIES = ["socks5://2001:db8:1234::10:1080",
"socks5://2001:db8:1234::11:1080",
"socks5://2001:db8:1234::12:1080"]
def fetch_history(channel: str, limit: int = 1000):
results = []
cursor = None
while True:
idx = random.randrange(len(TOKENS))
client = WebClient(token=TOKENS[idx], proxy=PROXIES[idx])
resp = client.conversations_history(
channel=channel, limit=200, cursor=cursor
)
if resp.status_code == 429:
wait = int(resp.headers.get("Retry-After", 30))
time.sleep(wait)
continue
results.extend(resp["messages"])
cursor = resp.get("response_metadata", {}).get("next_cursor")
if not cursor or len(results) >= limit:
break
time.sleep(1.2)
return results
```
Задержка 1.2 секунды — чуть больше лимита в 1 rps. Slack режет burst: если отправить 5 запросов за секунду, 4 уйдут в 429. Тише едешь — дальше будешь.
Обработка 429 и Retry-After
Slack отдаёт `Retry-After` в секундах, обычно 30-60. Игнорировать его — прямой путь к блокировке app. Их документация прямо говорит: exponential backoff обязателен.
Правильная стратегия: при 429 ждём ровно `Retry-After`, потом повторяем. Если снова 429 — удваиваем. Максимум 3 попытки, потом сдаёмся и логируем.
```python
def call_with_retry(client, method, **kwargs):
delay = 1
for attempt in range(4):
try:
return getattr(client, method)(**kwargs)
except SlackApiError as e:
if e.response.status_code == 429:
wait = int(e.response.headers.get("Retry-After", delay))
time.sleep(wait)
delay *= 2
continue
raise
raise RuntimeError("rate limit exhausted")
```
Отдельная история — `Tier 4` методы (`users.list`, `conversations.list`). У них лимит 100+ запросов в минуту, но пагинация по 200 элементов. Для воркспейса с 50k юзеров это 250 запросов, растянутых на минуты.
Кейс: Slack audit logs и геолокация IP
Клиент жаловался: Slack Enterprise Grid показывал в audit logs входы из Нидерландов, Германии и Финляндии одновременно. Аккаунт один, пользователь один, а сессии разбросаны по трём странам.
Причина — IPv4-прокси с ротацией. Скрипт менял адрес на каждый запрос, адреса были из разных датацентров. Slack сопоставлял IP с GeoIP-базой (MaxMind) и видел несовместимые локации. Это триггерило security alert, админ воркспейса получал уведомление о подозрительной активности.
Решение: перешли на IPv6 /64 от одного провайдера в одной локации. Все адреса в пуле принадлежат одной AS и одной стране. Slack видит 200 разных адресов, но все — из Амстердама. Audit logs перестали показывать скачки геолокации, security alerts прекратились.
Технически: /64 от Hetzner в Falkenstein, ASN 24940. MaxMind ставит всем адресам в этом /64 одну страну — Germany. Slack смотрит на /64 как на одну сеть, не как на 1000 отдельных клиентов.
Кейс: 429 на conversations.history при синхронизации
Сервер на Python 3.11, `slack_sdk` 3.23.0, задача — выгрузить историю 400 каналов за сутки. Наивная реализация дёргала `conversations.history` в цикле без задержек. Через 12 секунд Slack начал отдавать 429 с `Retry-After: 60`.
Разбор: `conversations.history` — Tier 3, лимит 50+ запросов в минуту с burst 15. Скрипт отправлял 20 запросов в секунду. Slack резал всё, что выше burst. За 60 секунд ожидания накопилось 1200 необработанных каналов.
Решение: разбили на 4 воркспейс-токена, добавили `asyncio.Semaphore(3)` на каждый токен, задержку 1.2 секунды между вызовами. Плюс прокси на IPv6 — 4 адреса, по одному на токен. Пропускная способность выросла до 200 запросов в минуту без единого 429. Время выгрузки — 8 часов вместо ожидаемых 24.
Цифры: 400 каналов × ~50 страниц пагинации = 20 000 запросов. При 200 rpm — 100 минут чистого времени. Остальное — обработка и запись в БД.
Кейс: блокировка по /64 в OVH
Воркспейс с 30 ботами работал через IPv6-прокси на OVH. Через месяц половина ботов начала получать `invalid_auth` при вызовах API. Токены валидные, воркспейс активный.
Причина: один из адресов в /64 попал в abuse-лист Slack. Кто-то из соседей по /64 (другой клиент OVH) спамил через Slack API. Slack заблокировал не /128, а весь /64 — 2^64 адресов. Боты, чьи адреса попадали в этот /64, отвалились.
Решение: заказали второй /64 у другого провайдера, разнесли ботов по двум пулам. Плюс мониторинг: раз в час `auth.test` на каждом токене, при ошибке — переключение на резервный пул. Такой мониторинг ловит блокировку за час, а не за сутки.
Урок: не держите все яйца в одном /64. Два-три провайдера, разные ASN, разные страны — минимальная страховка от каскадных блокировок.
Мониторинг и health-check
Прокси без мониторинга — чёрный ящик. Нужно знать: жив ли адрес, не блокирует ли Slack, работает ли TLS-хендшейк.
Минимальный health-check:
```bash
!/bin/bash
for addr in \$(seq 10 20); do
ip="2001:db8:1234::\${addr}"
code=\$(curl -6 --interface "\$ip" -s -o /dev/null -w "%{http_code}" \
-X POST https://slack.com/api/api.test \
--max-time 5)
echo "\$ip -> \$code"
done
```
`api.test` не требует токена и не считается в rate limit. Отдаёт `{"ok":true}` при успехе. Если код 000 — соединение не установилось, адрес мёртв. Если 403 — Slack блокирует. Если 200 — всё ок.
Метрики для Prometheus: успешность запросов по адресу, средняя задержка TLS-хендшейка, количество 429 на токен. Задержка до `slack.com` через IPv6 обычно 20-40 мс из Европы, 80-120 мс из США. Если видите 500+ мс — маршрут кривой, меняйте провайдера.
Ограничения подхода
IPv6-прокси не решает всё. Slack всё равно видит паттерны: одинаковые User-Agent, одинаковые интервалы между запросами, одинаковый порядок вызовов. Если 100 ботов работают по одному скрипту с точностью до миллисекунд — это палево, даже с 1000 адресов.
Enterprise Grid с включённым IP allowlist вообще не пустит трафик с чужих адресов. Тут прокси бесполезны — нужны адреса из разрешённого диапазона, а это уже вопрос к админу воркспейса.
И последнее: Slack периодически меняет rate limits и правила. То, что работало год назад, сегодня может отдавать 429 на каждом шаге. Тестируйте на dev-воркспейсе, читайте changelog, не полагайтесь на старые статьи.