V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
LnTrx
V2EX  ›  宽带症候群

IPv6 环境下一机多 IP 玩法的讨论

  •  
  •   LnTrx · 2022-09-05 20:43:12 +08:00 · 4180 次点击
    这是一个创建于 812 天前的主题,其中的信息可能已经有所发展或是发生改变。

    IPv6 的地址空间很大,在 SLAAC 的配置下很难被外部主动扫到。但是通过自己的主动访问暴露,例如浏览恶意网站、运行 p2p 应用等,还是可能的。这种暴露的风险相比公网 IPv4 要小不少、而且设备自身的安全建设也更加根本,但如何采取其他措施加以预防也值得讨论。IPv4 时代的传统解决方案是防火墙,可是目前 IPv6 路由器的防火墙功能普遍残缺,即使有也需要固定机器的后缀。同时 PCP 等自动化机制也缺少应用,日常配置起来还是颇为麻烦。

    楼主认为,IPv6 时代应该适应每台机器都是公网的零信任环境,不应该过度依赖网关和内网来保证安全。对于之前提到的暴露风险,如果转换思路,可以想到利用 IPv6 超大的地址空间,给 bt 、ipfs 这类应用分配独立的地址,从而仅把安全的、与敏感数据隔离的服务主动暴露给外部。搜了一下,IPv6 环境下一机多 IP 成熟的经验还比较少。虽然也存在一些麻烦,但个人感觉自动化的潜力比较很大。在此,楼主想分享在不同场景下非常初步的经验,希望可以起到抛砖引玉的作用:

    一、服务器场景

    提供 IPv6 的商家,有的是给一个子网随便玩,有的是需要论个申请但可以申请一堆,仅有少数是只能用一个的(如 Lightsail )。
    由于服务端 IP 比较固定,通常比较好配置,同时可以沿用一些多公网 IPv4 的经验。
    基本的玩法就是监听时不再写[::]:80 ,而是绑定[IPv6]:80 ,这样多个服务就可以分别通过独立的 IPv6 地址访问、解析。特别是当端口打架的时候,就不再需要基于 Host 或者 SNI 的反代来分流了。即使一个源站 IP 暴露了,相对不容易牵连其他服务,也便于换一个新的。

    二、家用 PC 场景

    在 Windows 下如何让一个应用的所有入站和出站都走指定的 IP 地址,除了虚拟机暂时没有想到很好的方法。
    楼主在家宽环境下用 VMware 实测,当网络配置为桥接模式(不复制物理主机状态),虚拟机中的 Windows 和 Ubuntu 都可以获得独立于主机的 SLAAC IPv6 地址,且都可以被公网访问。traceroute 不会经过宿主的 IPv6 地址。其他虚拟化软件还未测试。

    三、家用 NAS 场景

    在 NAS 配置服务常用的是 Docker 。虽然 Docker 支持 IPv6 ,但相关的功能感觉还比较简单,很多时候需要固定的 CIDR 。楼主见过的 Docker+IPv6 玩法,一种是配 IPv6 私有地址然后做转发,还有一种是主机模式直接用宿主的地址。如果只是为了能用公网,后者相对简单。但想用独立的公网 IPv6 地址,就需要其他的方法。其中,bridge 模式虽然可行(需手动处理 NAP ),但会增加主机网关的一跳,从而暴露宿主的 IP 地址。所以这里以 IPvlan 模式为例,提供一个原理验证流程:

    1.建立网桥

    假设网卡为 eth0

    docker network create -d ipvlan \
    --ipv6  --subnet=$ip/80 \
    -o parent=eth0 v6ipvlan1
    

    其中$ip可以从公网 IP 裁切( curl -6 ip.sb ),也可以直接指定私网 IP ( fd00::)。无论那种,容器都可以额外从配置 SLAAC 的网关获得的公网 IPv6 地址。

    2.运行一个能响应 Web 的 Docker

    docker run --name socat --rm --net=v6ipvlan1\
        alpine/socat -v -d -d \
        TCP6-LISTEN:12080,crlf,reuseaddr,fork \
        SYSTEM:"
        echo HTTP/1.1 200 OK; 
        echo Content-Type\: text/plain; 
        echo; 
        echo \"Server: \$SOCAT_SOCKADDR:\$SOCAT_SOCKPORT\";
        echo \"Client: \$SOCAT_PEERADDR:\$SOCAT_PEERPORT\";
        "
    

    3.进到容器里看看

    docker exec -it socat sh
    

    查询本容器的公网 IPv6 (设为 2400:11:22:33:aa:bb:cc:dd )

    ip addr
    

    有时需要有主动的 IPv6 出站才能打通,例如:

    ping6 240c::6666
    

    至此,只要没有其他防火墙拦着,[2400:11:22:33:aa:bb:cc:dd]:12080 应该就可以从外部访问了。
    由于地址独立于宿主生成,且 traceroute 不会经过宿主的一跳,或可以避免宿主和其他容器公网 IPv6 的暴露。

    需要重申的是,以上仅是粗糙的原理验证,希望可以启发更多关于多 IP 玩法的讨论。

    其他问题

    • 动态 IP 下指定后缀
      很多时候后缀直接用 SLAAC 的就完事了( bt 、ipfs ),也可以在 Docker 内跑个 DDNS ( crontab+sh 脚本)。
      但如果确需要指定,因为 Shell 处理 IPv6 地址比较麻烦,且网桥可能还得重新建立,所以暂时还未有自动化方案。

    • 公网 v6 的非预期暴露
      从之前的 docker 命令可以看出,配置公网 IPv6 后,docker 内部对[::]的监听即是对公网的监听,不需要 -p 来映射。
      但容器的制作和使用者仍可能延续 NAT 时代的惯性,默认内网安全,从而导致端口意外暴露在公网。

    第 1 条附言  ·  2022-09-05 22:23:31 +08:00

    DDNS也可在宿主机配置。以 dnspod-shell 的方案为例,在原本配置的基础上,可以修改 ardnspod 的 arWanIp6() 为:

    arWanIp6() {
    
        local hostIp
    
        local lanIps="(^$)|(^::1$)|(^fe[8-9,A-F])"
    
        if [ -z "$hostIp" ]; then
            hostIp=$(docker exec socat wget -qO - $arIp6QueryUrl)
        fi
    
        if [ -z "$hostIp" ]; then
            arLog "> arWanIp6 - Can't get ip address"
            return 1
        fi
    
        if [ -z "$(echo $hostIp | grep -E '^[0-9a-fA-F:]+$')" ]; then
            arLog "> arWanIp6 - Invalid ip address"
            return 1
        fi
    
        echo $hostIp
    
    }
    

    如此就可将 socat 容器中的 IPv6 更新到DNS。

    第 2 条附言  ·  2022-12-15 19:29:52 +08:00

    因为docker内做定时比较复杂,如需DDNS,推荐在宿主机配置。

    总体思路就三步:
    1.找一个宿主机能用的ddns脚本
    2.把脚本中获取本机ip的部分改成获取容器ip,原理示例:

    docker_name=qbittorrent
    ip=$(docker exec $docker_name curl -s -6 https://api.ip.sb/ip -A Mozilla)
    echo "$docker_name's IPv6 is: $ip"    
    

    3.在图形UI或者crontab添加计划任务

    28 条回复    2022-09-08 23:30:44 +08:00
    fetich
        1
    fetich  
       2022-09-06 00:42:27 +08:00
    为 OP 的折腾精神点赞
    fetich
        2
    fetich  
       2022-09-06 00:43:06 +08:00
    @fetich 「折腾」精神
    cnbatch
        3
    cnbatch  
       2022-09-06 02:05:09 +08:00
    单独给每个应用分配 IPv6 临时地址,这就需要操作系统的支持才行了。

    不是做不到,毕竟现在已经可以做到单独让某个 /某类软件走特定通道。就看有没有人愿意贡献代码(仅限于开源系统),或者操作系统的开发商愿不愿意实现。
    mxT52CRuqR6o5
        4
    mxT52CRuqR6o5  
       2022-09-06 02:28:01 +08:00 via Android
    我感觉这种多 ip 只有类似于虚拟机、docker 之类的方案能增加安全性,如果被分配了独立地址的应用没有被跑在沙箱里,那我认为并不会有理论上的安全性提升
    Jirajine
        5
    Jirajine  
       2022-09-06 08:00:52 +08:00
    @cnbatch 操作系统当然支持,ipvlan 配合 netns 就可以做到。不过正常来说也不是完全必须隔离开,如果 host 上的程序都是你可控的话,直接给网卡加个 IP 然后让特定应用绑定,自行注意避免[::]就行了。
    lovelylain
        6
    lovelylain  
       2022-09-06 08:18:47 +08:00 via Android   ❤️ 4
    玩这么复杂是想实现啥?路由器上用 iptables 阻止所有 ipv6 入站连接,有端口开放请求的话,路由器上部署 frp 监听 ipv6 地址,然后 frp 转发到内网的 ipv4 地址,不就跟 ipv4 一样安全又方便了吗,当然也可以用 socat 等,我喜欢用 frp 一是内网穿透玩了这个很熟悉,而是它支持 proxy_protocol 对于 http 可以保留原始的客户端 ip 。
    neroxps
        7
    neroxps  
       2022-09-06 08:27:49 +08:00 via iPhone
    @jobmailcn 终于有一个清醒人 哈哈
    titanium98118
        8
    titanium98118  
       2022-09-06 09:02:51 +08:00 via Android
    现在客户端是不是会主动发送 sni ?
    kimigaooo
        9
    kimigaooo  
       2022-09-06 11:10:21 +08:00 via iPhone
    用无状态分配 ipv6 ,当宽带自动重播的时候系统会获得多个 ipv6 导致无法正常访问这个问题怎么解决呢?
    acbot
        10
    acbot  
       2022-09-06 11:34:01 +08:00   ❤️ 1
    @kimigaooo SLAAC 模式下 OP 路由器重新拨号以后 路由器会把旧前缀 RA 消息的 valid_lifetime / preferred_lifetime 这两个参数置零,并且发送出去,系统收到之后就会将旧的地址从首选变更为其他状态,新的地址就是首选了。 当然我用的国产路由器这个就处理不太好需要手动去处理或者是等待 旧的 valid_lifetime / preferred_lifetime 失效
    LnTrx
        11
    LnTrx  
    OP
       2022-09-06 12:28:16 +08:00
    一处笔误:NAP → NDP
    LnTrx
        12
    LnTrx  
    OP
       2022-09-06 12:43:36 +08:00
    @mxT52CRuqR6o5 以上设置的初衷是防止应用暴露主机 IP ,导致主机上其他有弱点的入站端口被发现,所以安全性提升是有的。沙箱是防止应用的端口被攻陷、获取高权限后在主机侵染其他应用,这是另一层面的安全性。
    LnTrx
        13
    LnTrx  
    OP
       2022-09-06 12:52:39 +08:00
    @jobmailcn 其实文中已经提过,例如内网设备要开 bt 、ipfs ,这些软件能知道你设了个 frp 么?是不是也要配置一番,除非 All in 网关。但如果很在意安全性,在网关监听外网端口反而不推荐。因为网关是整个内网较容易被发现的设备,如果网关自身的入站端口有安全弱点,同时下属设备都假设内网的安全性,那恐怕更加危险。
    cnbatch
        14
    cnbatch  
       2022-09-06 15:14:18 +08:00
    @Jirajine 这是需要手动操作,而不是全自动模式。全自动模式还是需要开发商修改一番的。
    总不能每次打开浏览器都手动弄一次(哪怕用脚本),玩游戏弄一次,听音乐弄一次,打开 IM 软件再弄一次……
    所以全自动还得有人给系统修改代码
    cnbatch
        15
    cnbatch  
       2022-09-06 15:26:02 +08:00
    对于家用内网,其实还可以配置成内网 IPv6 私网地址 + 网关转换公网 IPv6 ,一对一转换,也就是 NPTv6 (可以看成是 NAT 的进化版)。这样的话,网关防火墙仍旧可以起作用,依然能够像 IPv4 时代那样手动指定 DMZ ,设定哪些主机允许外部主动连进来、哪些继续阻止主动入站。

    目前内置 NPTv6 支持的就只有 FreeBSD ,其他系统只能靠第三方软件才能实现,变相等于阻碍了 NPTv6 的推行,导致基于 NPTv6 的方式目前还没人做出专门的 WebUI 配置项。
    malash
        16
    malash  
       2022-09-06 17:06:49 +08:00
    理论上可行,实际操作起来很麻烦。

    家庭 PPPOE 每次拨号后前缀都会变化,SLAAC 本身也有( 24 小时?)的超时时间。你的所有服务都要不停的检测 IPv6 地址变化然后重新监听。这样做成本是很高的,举个例子,你买了一个小米网关开放了一个有漏洞的接口,你要如何刷固件让它监听指定的 IP ?如果买了更多设备呢,每一个设备都要有监听方案

    还有一个例子,就是小米网关的监听服务是有漏洞的,并且这个漏洞是一个“有益”的功能,可以用于破解后连接 Home Assistant (见 https://github.com/AlexxIT/XiaomiGateway3 )。我用这个例子是想说明内网许多服务是需要以“不安全”的形式进行运行的,内网安全不太可能和外网安全等同。你的方案对于 VPS 等场景会更加适合,对于内网就不那么合适了。

    其实我们不妨做一个思想上的转变,广域网 /局域网地址不等同于公网 /内网。IPv6 下确实每一个设备都有一个唯一的地址,但并不是所有的地址都是“公网”地址。公网和内网的区别其实只是“本地网络”和“本地之外的网络”,假如你在网关 /路由器的防火墙禁用了 IPv6 的开放端口,内网设备即使分配到了 IPv6 地址,依然也只有内网的服务能力,对吧?

    如果不太好理解可以想象这样一个例子,在 IPv4 下路由器路由器使用 1.1.1.1 ,我的电脑使用 1.1.1.2 ,笔记本用 1.1.1.3 ,可以吗?技术上当然可以。虽然它们都是公网 IP ,但我只在内网使用并且不会对外(公网)广播,公网也 ping 不到我的这些设备,那么其实它们就是些内网服务而已。就像一些厂商局域网 IP 都用光了,连美国国防部的 IP 都拿来用了 https://www.v2ex.com/t/365031 ,是一样的道理

    因此通过使用网关防火墙,人为划分了公网和内网的界限,并没有太大的问题,并且会比为每一个设备配置防火墙要更简单、可行和安全
    neroxps
        17
    neroxps  
       2022-09-06 17:41:12 +08:00 via iPhone
    @LnTrx ??? ip6tables 只开放匹配后缀(解决动态 pd 前缀问题)匹配目的端口的端口转发,其他一律禁掉。和 nat 没区别吧。

    为何要弄的如此复杂?
    LnTrx
        18
    LnTrx  
    OP
       2022-09-06 17:44:23 +08:00
    @malash 一机多 IP 是针对 PC 、NAS 等现代操作系统场景。我没有建议在家庭场景监听指定的 IP 。简单的物联网设备没有服务间隔离的问题,也不太会随便主动外访,默认的 SLAAC 一般就行了。
    内网一般就指局域网,你指的可能是可公网入站和仅内网入站,但重新定义概念恐怕会引发混乱。防火墙的低支持度、需要固定后缀、自动化打洞等问题和可移动设备接入外部网络的风险我就不再赘述了。至少对我来说,如果有防火墙可根据 MAC 地址来配置,才会觉得比较方便。
    neroxps
        19
    neroxps  
       2022-09-06 17:44:42 +08:00 via iPhone
    @acbot 我之前就是 ros 的生存周期太长,导致 linux 获得超过 16 个 ipv6 地址 一直获不到新的 ip
    LnTrx
        20
    LnTrx  
    OP
       2022-09-06 17:54:59 +08:00
    @neroxps 现在广泛售卖的路由器能匹配后缀的不多。现代操作系统有的后缀是会跟着前缀变的,需要关掉隐私扩展。NAT 时代的自动化端口映射大多不兼容 IPv6 ,例如 PCP 在网关和应用的支持都比较少,所以开端口基本就要手动。
    acbot
        21
    acbot  
       2022-09-06 18:18:31 +08:00
    @neroxps 虽然重拨号 OP 处理没有问题,但目前 OP 在自定义 valid_lifetime / preferred_lifetime 方面还是不是太完善,这么改都不生效。以前他们说新版本解决了结果我从 18 换到新版本还是一样,最后也就放弃了
    Jirajine
        22
    Jirajine  
       2022-09-06 23:17:15 +08:00
    @cnbatch 你还想要多自动,写个 launcher 脚本,然后修改你想要的程序的服务文件 /快捷方式 把启动命令用 launcher 脚本包装一下不就完了。
    malash
        23
    malash  
       2022-09-07 00:07:28 +08:00
    @LnTrx 哦哦,是我看错了。我是看到你说“ 基本的玩法就是监听时不再写[::]:80 ,而是绑定[IPv6]:80 ,这样多个服务就可以分别通过独立的 IPv6 地址访问、解析”,这种方案确实只适合服务器,不适合家庭网络环境。

    iptables 支持根据 MAC 地址过滤的(使用 --mac-source 参数),OpenWRT 后台也有图形界面可以填写 MAC 地址,不过我没有测试过,您可以试试。另外还有个办法可以尝试,就是在网关上使用 iptables 进行端口转发,这样相当于是白名单,更加安全也更加可控。这个方案和 @jobmailcn 所说的 frp 比较类似,目的是一样的,只是实现上有差异。

    其实这两种方案都是依赖网关的安全性来保证内网安全的,如您所说“如果网关自身的入站端口有安全弱点,同时下属设备都假设内网的安全性,那恐怕更加危险”。但从另一个角度讲,让每个设备只负责自己最专业的领域——NAS 就负责提供服务,路由器就负责网络管理和防火墙——个人认为是符合“关注点分离”的原则的( https://zh.m.wikipedia.org/zh-hans/%E5%85%B3%E6%B3%A8%E7%82%B9%E5%88%86%E7%A6%BB )。这也是为什么从安全的角度上我并不推荐在软路由上安装各种非网络相关的的服务,像 SMB 文件共享这种。进一步想,如果你担心网关本身的安全问题,可以把防火墙的功能单独抽出来,配置一台硬件防火墙或者一台安装了 pfSense 的软防火墙,这样即使网关被攻破了,防火墙依然生效。这也是很多企业网络的做法。
    cnbatch
        24
    cnbatch  
       2022-09-07 00:51:32 +08:00
    @Jirajine 那当然是无感知、自动进行的更好,够方便,连脚本都不需要写。
    要不然每次启动程序前都要想一想,“这个程序我有没有写脚本,啊,没写,先弄一个”。再加上一些比较冷门的、不太常用但偶然会用的软件,只会把脚本越弄越多。
    需要自己写前置脚本的办法,总地来说并不是最方便的做法,而是临时解决方案。这样就没办法大量推广使用了。
    如果在应用程序启动时,操作系统就自动做 IPv6 地址分配操作,全自动进行,那就能够连小白都能不知不觉的用上。
    LnTrx
        25
    LnTrx  
    OP
       2022-09-07 00:52:03 +08:00
    @malash 感谢你提供的信息,“关注点分离”是值得留意的。传统网关是路由+防火墙,在 IPv6+移动互联网的当下,个人认为终端与网关应该在防火墙层面进一步解耦。IPv4+NAT 的方案不少可以照搬到 IPv6 ,但看到 NAT 时代搞出来的各种麻烦事居然还要在 IPv6 时代延续,我还是感到十分头疼。具体的麻烦就不再复述了。本文是想提示大家,不一定要沿袭过去的做法,活用 IPv6 的新特性也是一条可能的方向。
    deorth
        26
    deorth  
       2022-09-07 10:06:45 +08:00 via Android
    用 pve 的 LXC 即可
    LnTrx
        27
    LnTrx  
    OP
       2022-09-08 21:09:11 +08:00
    @malash --mac-source 可以指定外网入站内网设备的 MAC 么?
    malash
        28
    malash  
       2022-09-08 23:30:44 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2421 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 62ms · UTC 16:01 · PVG 00:01 · LAX 08:01 · JFK 11:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.