IPv6 接口(至少具有可路由 IP)通常具有临时 IP 地址,该地址经常更改并且通常用于出站连接以提供更多隐私。
我怎样才能告诉 ssh 不要使用这个地址,而是绑定到给定出站连接尝试的非临时地址?
我知道我可以使用该-b
选项绑定到特定的 IP 地址,但这意味着我必须先查找出站接口的 IP 地址。
答案1
在 Linux 上,ssh 需要调用 setsockopt(IPV6_ADDR_PREFERENCES) 来请求特定的地址类型。(Linux setsockopt 文档中没有提到这一点,但代码存在于内核中,似乎符合 RFC 5014 规范。)
由于 OpenSSH 没有任何配置选项可以让你指定地址首选项,因此它必须像这个(仅使用不同的 sockopt)。您可以尝试要求 OpenSSH 开发人员添加这样的选项,但他们通常不想添加 OpenBSD 不支持的任何操作系统功能。
编写脚本来获取正确的地址可能更容易-b
;例如,如果您使用的是 Linux,则可以使用ip | jq
:
addr=$(ip -json -6 addr ls dev eno1 scope global \
| jq -r ".[].addr_info[]|select(.local)|select(.temporary|not)|.local")
ssh -b $addr ...
或者 – 完全关闭临时地址(隐私扩展)。
但是如果你想试试运气LD_PRELOAD
,你可以测试一下(我根本没有测试过;很可能它触发得太晚而没有任何效果):
/* gcc -shared -o ipv6pref.so ipv6pref.c */
/* LD_PRELOAD="$HOME/ipv6pref.so" ssh whatever */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <err.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
int setsockopt(int fd, int level, int name, const void *value, socklen_t len)
{
static int (*real_setsockopt)(int, int, int, const void *, socklen_t);
uint32_t flags = IPV6_PREFER_SRC_PUBLIC;
int r;
if (!real_setsockopt)
real_setsockopt = dlsym(RTLD_NEXT, "setsockopt");
if ((level == SOL_IP && name == IP_TOS) ||
(level == SOL_IPV6 && name == IPV6_TCLASS))
{
/* This is probably the TCP socket that will be used for SSH. */
r = real_setsockopt(fd, IPPROTO_IPV6, IPV6_ADDR_PREFERENCES, &flags, sizeof flags);
if (r != 0)
warn("Could not set address preference");
}
return real_setsockopt(fd, level, name, value, len);
}