最近遇到一个小问题,折腾了一番,发现背后其实挺有意思的。事情的起因是我们团队从飞书切换到钉钉,在适配钉钉群机器人的消息推送时报错,提示“系统异常,请稍后再试”或者“ip 不在白名单中”。一开始以为是自己配置的不对,到后来经过多次验证以为是钉钉的bug,心想大厂产品不应该啊,结果最终排查下来,竟然是因为IPv6。
一、问题的由来
我们团队之前用的是飞书的群机器人,后来因为一些原因切换到了钉钉。刚开始配置钉钉机器人的时候,一切都挺顺利,按照文档一步步来,结果在测试消息推送时,服务器端总是返回错误:
{"errcode":500100,"errmsg":"错误描述:系统异常,请稍后再试;"}
{"errcode":310000,"errmsg":"错误描述:ip 不在白名单中;解决方案:请联系对应群的管理员,设置机器人安全ip地址段"}
钉钉机器人有个安全机制要求,即关键字、加签或者IP地址(段)三选一,否则无法创建群机器人。于是我第一时间怀疑是不是 IP 没加对。
二、排查过程
1. 反复确认 IP
虽然这台服务器的IP地址我本来就记得滚瓜烂熟,但还是用 curl 查了下出口 IP:
curl cip.cc
IP : ***
地址 : ***
运营商 : ***
数据二 : ***
数据三 : ***
URL : http://www.cip.cc/***
得到的 IPv4 地址和白名单里的完全一致,没问题。
这时我陷入了困惑:为什么钉钉还是说我的 IP 不在白名单里?
2. 跨平台验证
又换了 curl ifconfig.me/all 试试:
curl ifconfig.me/all
ip_addr: 2001:***
remote_host: unavailable
user_agent: curl/7.81.0
port: 51554
language:
referer:
connection:
keep_alive:
method: GET
encoding:
mime: */*
charset:
via: 1.1 google
forwarded: ***
结果发现除了 IPv4,这台服务器还有一个 IPv6 地址。这才意识到,服务器其实有两个出口地址。
3. DNS 解析钉钉 API
继续查 oapi.dingtalk.com 的 DNS 记录:
dig oapi.dingtalk.com AAAA
;; QUESTION SECTION:
;oapi.dingtalk.com. IN AAAA
;; ANSWER SECTION:
oapi.dingtalk.com. ... IN CNAME oapi.dingtalk.com.gds.alibabadns.com.
oapi.dingtalk.com.gds.alibabadns.com. ... IN CNAME v6-cname.dingtalk.com.
...
cn-v6-cname.dingtalk.com.gds.alibabadns.com. ... IN AAAA 2401:b180:2000:60::f
cn-v6-cname.dingtalk.com.gds.alibabadns.com. ... IN AAAA 2401:b180:2000:70::e
cn-v6-cname.dingtalk.com.gds.alibabadns.com. ... IN AAAA 2401:b180:2000:50::b
cn-v6-cname.dingtalk.com.gds.alibabadns.com. ... IN AAAA 2401:b180:2000:80::d
果然,钉钉 API 域名有 IPv6 解析,也就是说,系统很可能默认用 IPv6 访问钉钉。
4. 白名单只能填 IPv4
我试着把 IPv6 地址填进钉钉机器人的安全设置页面,结果提示“请输入正确的IP地址”。钉钉的白名单只接受 IPv4,IPv6直接被拒绝。
三、问题分析
事情至此真相大白:
- 服务器自动分配了 IPv6 地址
- 钉钉 API 域名支持 IPv6
- 系统默认优先用 IPv6 访问外网
- 钉钉白名单只支持 IPv4,不支持 IPv6
所以,虽然我的 IPv4 在白名单里,但实际请求用的是 IPv6,导致被钉钉拒绝。
四、解决方案
既然钉钉不支持 IPv6,而我也不需要 IPv6 那就让服务器也不用 IPv6:
临时关闭 IPv6:
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
永久关闭 IPv6:
-
编辑
/etc/sysctl.conf
,添加以下两行:net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1
-
保存并退出,然后运行
sudo sysctl -p
使配置生效。
关掉 IPv6 后,钉钉消息推送恢复正常。
五、一些感想
这个小问题其实挺有代表性:IPv6 越来越普及,很多平台(包括钉钉)却还没完全适配 IPv6。随着 IPv6 的普及性进一步提高,这类兼容性问题肯定会越来越多。希望钉钉早日支持 IPv6 白名单,不然大家还得不停折腾网络设置。
如果你也遇到类似问题,不妨用 curl ifconfig.me/all
看看自己的服务器是不是也有 IPv6 地址。很多时候,问题就藏在这种你“从来没注意过”的细节里。
有些小 bug,看起来不起眼,背后却藏着技术演进的脚步。愿大家都能在“意料之外”多留个心眼,少踩点坑。