Предоставлен дополнительный петлевой интерфейс в 127.0.0.Диапазон X (X > 1) на BoxA (который может работать под управлением OSX или Linux), я хочу привязать порт 22 этого дополнительного интерфейса обратной связи к прямому туннелю SSH (то есть к локальному порту пересылки), который указывает на BoxB.

На OSX это работает нормально (странно, оглядываясь назад). [Принимая X = 2 ] после вызова псевдонима обратной связи с ifconfig lo0 alias 127.0.0.2 up , SSH может установить туннель с ssh -NfL 127.0.0.2:22:localhost:22 BoxB . Затем в новой оболочке на BoxA ssh 127.0.0.2 регистрирует меня в BoxB.

В Ubuntu я могу вызвать псевдоним loopback на BoxA, но при попытке установить SSH-туннель ssh жалуется на невозможность связать (и, следовательно, переслать) порт 22 BoxA. Последующий ssh 127.0.0.2 (в новой оболочке на BoxA) выдает предупреждение об отпечатке пальца, которое, если его обойти, возвращает меня обратно в BoxA. Имеет смысл - sshd на BoxA слушает все интерфейсы.

Рассматривая sshd_config в каждом, оба настроены на прослушивание 0.0.0.0:: для IPv6).

lsof для OSX дает:

launchd       1           root   40u  IPv6 0xddfcabed61001f0d      0t0  TCP *:ssh (LISTEN)
launchd       1           root   41u  IPv4 0xddfcabed6100413d      0t0  TCP *:ssh (LISTEN)
launchd       1           root   43u  IPv6 0xddfcabed61001f0d      0t0  TCP *:ssh (LISTEN)
launchd       1           root   44u  IPv4 0xddfcabed6100413d      0t0  TCP *:ssh (LISTEN)

и для Ubuntu:

sshd       1287              0    3u     IPv4           21903340        0t0        TCP *:ssh (LISTEN)

Так что оба слушают на всех интерфейсах, хотя я не уверен, почему OSX использует 4 процесса. В любом случае, Ubuntu дает ожидаемое поведение. Почему OSX ведет себя по-другому?

Следующий вопрос, конечно, заключается в том, как заставить Ubuntu вести себя как OSX в этом отношении.

Хотя я хотел бы, чтобы у sshd_config были состояния, подстановочные знаки и / или логические операторы (например, «не слушайте 127.0.0.* ; Слушайте 127.0.0.1 »), такие как iptables , похоже, это не так. ,

1 ответ1

0

Это можно воспроизвести, используя socat (1.7.3.2-2 , а не 1.7.3.1-2+deb9u1 который игнорирует reuseport) в Debian (так похоже на Ubuntu):

term1$ socat -d -d  TCP4-LISTEN:5555,reuseaddr,fork -
2018/08/03 08:20:43 socat[25084] N listening on AF=2 0.0.0.0:5555

term2$ socat -d -d TCP4-LISTEN:5555,bind=127.0.0.2,reuseaddr,fork -
2018/08/03 08:21:00 socat[25085] E bind(5, {AF=2 127.0.0.2:5555}, 16): Address already in use
2018/08/03 08:21:00 socat[25085] N exit(1)

Теперь, если вы добавите reuseport для обоих:

term1$ socat -d -d  TCP4-LISTEN:5555,reuseaddr,reuseport,fork -
2018/08/03 08:21:28 socat[25086] N listening on AF=2 0.0.0.0:5555

term2$ socat -d -d TCP4-LISTEN:5555,bind=127.0.0.2,reuseaddr,reuseport,fork -
2018/08/03 08:21:56 socat[25092] N listening on AF=2 127.0.0.2:5555


otherterm$ netstat -tnlp 2>/dev/null|grep :5555 
tcp        0      0 127.0.0.2:5555          0.0.0.0:*               LISTEN      25092/socat         
tcp        0      0 0.0.0.0:5555            0.0.0.0:*               LISTEN      25086/socat         

Использование другого socat или netcat для подключения покажет, что он правильно перенаправлен на правильный слушающий socat зависимости от IP-адреса.

Таким образом, разница в поведении, скорее всего, заключается в том, как выполняются вызовы bind и setsockopt в MacOS и Ubuntu для ssh (возможно, launchd тоже играет определенную роль?). Используя strace , это добавляется в рабочем случае socat :

 setsockopt(5, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0

ОБНОВЛЕНИЕ:
Хотя я коснусь буквы вопроса ОП: заставить его работать, я также думаю, что этот вопрос не говорит о более широкой цели, и эта цель может быть достигнута с помощью совершенно другого решения.

Таким образом, чтобы изменить поведение sshd и ssh когда они связывают сокет, можно использовать оболочку dlsym() , которая изменит поведение функции bind(3) до реального системного вызова bind(2) .

скомпилируйте файл reuseport-wrapper.c ниже с помощью:

gcc -shared -fPIC -o reuseport-wrapper.so reuseport-wrapper.c -ldl

#define _GNU_SOURCE
#include <dlfcn.h>

#include <sys/socket.h>
#include <stddef.h> /* NULL */

int bind(int socket, const struct sockaddr *address, socklen_t address_len) {
    static const int optval=1;
    static int (*orig_bind)(int, const struct sockaddr *,socklen_t)=NULL;

    if (orig_bind == NULL && (orig_bind=dlsym(RTLD_NEXT,"bind")) == NULL)
        return -1;
    if (address != NULL && (address->sa_family == AF_INET || address->sa_family == AF_INET6)) {
        if (setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof optval) != 0) {
            return -1;
        }
    }
    return orig_bind(socket, address, address_len);
}

Это поместит оболочку в bind(3) чтобы всегда устанавливать SO_REUSEPORT перед привязкой сокета inet или inet6 .

Поместите его куда-нибудь (например, как /usr/local/lib/reuseport-wrapper.so но это действительно не имеет значения, поскольку он явно загружен, как и плагин).

Отредактируйте /etc/default/ssh и добавьте:

LD_PRELOAD=/usr/local/lib/reuseport-wrapper.so

перезапустите сервер ssh (например, service ssh restart)

И запустите любой другой инструмент (включая ssh) от имени пользователя root (см. Ниже, почему) с экспортированным LD_PRELOAD=/usr/local/lib/reuseport-wrapper.so . Например:

$ sudo su -
[...]
# LD_PRELOAD=/usr/local/lib/reuseport-wrapper.so ssh -NfL 127.0.0.2:22:localhost:22 userB@BoxB

Эту команду нужно запускать с правами root по двум причинам: порт 22 требует привязки к нему root, и даже с некоторыми дополнительными возможностями в socket(7) описано дополнительное ограничение:

SO_REUSEPORT (начиная с Linux 3.9)
Разрешает привязку нескольких сокетов AF_INET или AF_INET6 к одному и тому же адресу сокета. Эта опция должна быть установлена на каждом сокете (включая первый сокет) до вызова bind (2) на сокете. Чтобы предотвратить перехват порта, все процессы, привязанные к одному и тому же адресу, должны иметь один и тот же эффективный UID. Эта опция может использоваться с сокетами TCP и UDP.

Как только это будет сделано:

# netstat -tnlp |grep -w 22
tcp        0      0 127.0.0.2:22            0.0.0.0:*               LISTEN      157/ssh             
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      143/sshd            
tcp6       0      0 :::22                   :::*                    LISTEN      143/sshd            

И туннель будет работать как положено.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .