System administration
*nix
7 April 2014

Socks-сервер Dante или как одна буква может «съесть» пару суток времени

Каждый раз сталкиваясь с таким «рабочим моментом» я задумываюсь надо ли его решение давать миру или оно мелочно для других, но на этот раз решил-таки выложить. Эта статья больше из разряда заметки на манжетах и написана лишь из-за скудности информации о настройке Dante в нете и хроманием на обе ноги официальной документации.
В пятницу утром заказчик обратился с просьбой поднять socks-сервер на ~100 пользователей, с авторизацией по логину/паролю, привязкой IP и отправкой запросов с того же IP к которому конектится пользователь. При этом заказчик поинтересовался сроком выполнения работ и, хоть я не люблю делать прогнозы по времени установки/настройки, заверил его, что часа через 3-4 альфа-версия будет готова. Ну правда — погуглив выбрать подходящий socks-сервер, установить, почитать маны, подправить под себя дефолтный конфиг… в 4 часа должен вложиться.
ОС FreeBSD 9.2, но всё нижеописанное справедливо и для 10-ки.

Как ни странно, подходящих под запросы заказчика socks-серверов нашлось всего два: 3proxy 0.6.1 и Dante (в портах 1.3.2). Может я, конечно, что-то упустил, но либо нет авторизации, либо нет режима int_ip -> ext_ip. Возможно этим запросам соответствует squid, но ставить этого монстра ради простой задачи не хотелось.
Я ничего не имею против 3proxy, сам с ним работаю не первый год в режиме портмэппинга, нареканий особых нет, но разработка его стоит с 2009 года, код грязноват и слышал неоднократные отзывы о его прожорливости под большой нагрузкой.
Итак, Dante.
До версии 1.3 поддержки int_ip -> ext_ip Dante не имел, точнее имеется схожая реализация в платной версии по весьма недедемократичной цене в EUR400, однако, Lysenko Konstantin добавил данный функционал в виде патча «same-same» в Dante 1.2.2 и он был включён в релиз 1.3.0.
Не берусь утверждать работала ли данная конструкция в 1.3.0, но в 1.3.2 запросы упорно уходят с первого найдённого в конфиге external-ip. Перелопатив скудный ман я обратился к странице разработчика. Информации там несколько больше, но запустить same-same так как мне требовалось не удалось. Однако, с ноября 2013 года на сайте есть версия 1.4, которая почему-то не включена в порты. Качаем, собираем.
Надо отметить, что конфиг в 1.4 претерпел косметические изменения, хотя в манах по прежнему приводятся примеры с параметрами прежних версий, на которые Dante ругается как deprecated и, иногда, подсказывает верные новые параметры.

Тестовый конфиг:
/usr/local/etc/sockd.conf
# cat /usr/local/etc/sockd.conf
logoutput: /var/log/socks/socks.log

debug: 0

internal: 11.12.13.1          port = 1080
internal: 11.12.13.2          port = 1080
internal: 11.12.13.3          port = 1080
external: 11.12.13.1
external: 11.12.13.2
external: 11.12.13.3

external.rotation: same-same

socksmethod: username

user.privileged: root
user.unprivileged: nobody
user.libwrap: nobody

compatibility: sameport

client pass {
        from: 0.0.0.0/0 port 1-65535 to: 0.0.0.0/0
}

client block {
        from: 0.0.0.0/0 to: 127.0.0.0/8
        log: connect error
}

client block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}


socks pass {
        from: 21.22.23.0/24 to: 0.0.0.0/0
        log: connect error
        user: chaturanga
}

socks block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}



И… вопреки ожиданиям в 1.4 same-same также не заработал. На этот раз в отличии от 1.3 посыпались ошибки типа

warning: getoutaddr(): using external.rotation = same-same, local address 21.22.23.48 was selected for forwarding from our local client 21.22.23.48.45980 to target 77.72.80.15.80, but that local address is not set on our external interface(s).  Configuration error in /usr/local/etc/sockd.conf?


, где 21.22.23.48 — адрес моей локальной машины в то время как здесь должен быть internal-IP сервера к которому которому подключается клиент. Смутившись фразой «Configuration error in /usr/local/etc/sockd.conf?» курочу конфиг и штудирую маны, но, так как информации в них мало, лезу в лучший ман — исходники. Улыбаясь комментариям в виде
   /*
    * Just return the first address of the appropriate type from our internal
    * list and hope the best.
    */

нахожу-таки источник проблем (./sockd/sockd_request.c, line 4173):
/*
* Find address to bind for client. First the ipaddress.
*/
if (getoutaddr(&io->dst.laddr,
&io->src.raddr,
req.command,
target,
emsg,
emsglen) == NULL)
return -1;

Меняю &io->src.raddr на &io->src.laddr, пересобираю, запускаю и, наконец, вижу желанное:
info: pass(1): tcp/connect [: username%chaturanga@21.22.23.48.46050 11.12.13.3.1080 -> 11.12.13.3.27819 77.72.80.15.80

Тихо матерясь, оформляю баг-репорт разработчикам.
В итоге вместо заявленных 3-4 часов в чтении, додумывании конфигов, попытке запустить Dante не в jail'e, тестах на Centos вместо FreeBSD и копании в исходниках убил пару дней… Вот и обещай после этого…

UPD1: Пока писалась заметка ответил разработчик:
Hello, thank you for the bug-report. You are correct, there is an
error here. Your proposed solution is basically correct, though
we will probably implement the fix slightly differently.


UPD2: И в ходе дальнейшей переписки:
Depending on the current workload, I doubt I will be able to provide
you with our official patch for at least another month.

Ну и на том спасибо.

UPD3(2014-09-03):
Разработчик сообщил, что баг исправлен (v.1.4.1).
Проверил, всё работает как положено.

+23
30.9k 40
Comments 8