Прозрачное проксирование - как передать сокет на локальный сервер без изменений? - PullRequest
6 голосов
/ 30 апреля 2010

У меня есть программа, которая прослушивает порт 443, а затем перенаправляет на локальный сервер SSH или HTTPS в зависимости от обнаруженного протокола.

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

Однако это приводит к тому, что исходный хост на локальных серверах регистрируется как localhost.

Есть ли способ передать сокет непосредственно процессу локального сервера (а не просто установить новое TCP-соединение), чтобы параметры sockaddr_in (или sockaddr_in6) были сохранены?

Платформой для этого является Linux.

Ответы [ 3 ]

7 голосов
/ 30 апреля 2010

Вот фрагмент кода, взятый из stunnel (из client.c в функции local_bind, если вы хотите посмотреть весь код).

#ifdef IP_TRANSPARENT
int on=1;
if(c->opt->option.transparent) {
    if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on))
        sockerror("setsockopt IP_TRANSPARENT");
    /* ignore the error to retain Linux 2.2 compatibility */
    /* the error will be handled by bind(), anyway */
}
#endif /* IP_TRANSPARENT */

memcpy(&addr, &c->bind_addr.addr[0], sizeof addr);
if(ntohs(addr.in.sin_port)>=1024) { /* security check */
    if(!bind(c->fd, &addr.sa, addr_len(addr))) {
        s_log(LOG_INFO, "local_bind succeeded on the original port");
        return; /* success */
    }
    if(get_last_socket_error()!=EADDRINUSE
#ifndef USE_WIN32
            || !c->opt->option.transparent
#endif /* USE_WIN32 */
            ) {
        sockerror("local_bind (original port)");
        longjmp(c->err, 1);
    }
}

Ранее c-> bind_addr был установлен на адрес подключающегося узла с этим кодом:

    else if(c->opt->option.transparent)
    memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST));

Документация Stunnel содержит этот совет для последних ядер Linux:

Удаленный режим (либо 2.2.x и> = 2.6.28) требует выполнения stunnel от имени пользователя root. Параметр setuid также нарушит эту функцию.

Linux> = 2.6.28 требует следующей настройки для iptables и маршрутизации (возможно, в /etc/rc.local или эквивалентном файле):

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -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
6 голосов
/ 30 апреля 2010

Если у вас есть контроль над процессами на локальном сервере, вы можете сделать это. Создайте постоянное соединение сокетов домена UNIX между прокси-процессом и процессом сервера, затем используйте sendmsg() на этом сокете домена UNIX для передачи сообщения SCM_RIGHTS, содержащего файловый дескриптор сокета TCP. Процесс прокси может затем закрыть свой дескриптор для сокета TCP.

Когда серверный процесс получает дескриптор файла от прокси в сообщении SCM_RIGHTS, он просто должен добавить его к своему обычному набору клиентских сокетов, как если бы он пришел из accept().

1 голос
/ 25 февраля 2018

Используя https://stackoverflow.com/a/2741535/388191,, это можно сделать с трафиком на localhost, используя connmark, чтобы отслеживать входящее локальное прозрачное соединение и пометить исходящие пакеты, чтобы перенаправить их обратно на локальный хост:

iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -i lo -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j CONNMARK --set-mark 2
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -A OUTPUT -m connmark --mark 2 -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
...