sshd“监听所有接口”行为,OSX 和 Ubuntu

sshd“监听所有接口”行为,OSX 和 Ubuntu

给定BoxA(可以运行 OSX 或 Linux)上127.0.0.X( X> ) 范围内的额外环回接口,我想要将这个额外环回接口的端口 22 绑定到指向 BoxB 的正向 SSH 隧道(即本地端口转发)。1

在 OSX 上,这工作得很好(回想起来很奇怪)。[取X= 2] 在使用 调出环回别名后ifconfig lo0 alias 127.0.0.2 up,SSH 可以与 建立隧道ssh -NfL 127.0.0.2:22:localhost:22 BoxB。然后在 BoxA 上的新 shell 中,ssh 127.0.0.2将我登录到 BoxB。

在 Ubuntu 上,我可以在 BoxA 上调出环回别名,但在尝试建立 SSH 隧道时,ssh会提示无法绑定(并因此转发)BoxA 的端口 22。随后ssh 127.0.0.2(在 BoxA 上的新 shell 中)会给出指纹警告,如果绕过该警告,我会重新登录到 BoxA。这很有道理 - sshdBoxA 正在监听全部接口。

查看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

这可以使用socat1.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 地址正确路由到正确的监听。

因此,行为差异肯定在于bindsetsockopt在 MacOS 和 Ubuntu 上对 ssh 进行了调用(也许 launchd 也起着一定的作用?)。使用strace,在工作socat案例中添加了以下内容:

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

更新
虽然我会回答 OP 问题的字面意思:让它发挥作用,但我还认为这个问题并没有说明更广泛的目标,而这个目标可能会通过完全不同的解决方案来实现。

因此,为了在绑定套接字时改变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_REUSEPORTinetinet6

将其放置在某处(例如,/usr/local/lib/reuseport-wrapper.so但这实际上并不重要,因为它像插件一样明确加载)。

编辑/etc/default/ssh并添加:

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

重新启动 ssh 服务器(例如service ssh restart

并以 root 身份运行任何其他工具(包括ssh)(请参阅下文原因)并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            

隧道将按预期运行。

相关内容