深入Pyrlang源码:EPMD注册与Erlang分布式协议challenge-response握手认证实现原理

发布时间:2026/8/24 9:05:11
深入Pyrlang源码:EPMD注册与Erlang分布式协议challenge-response握手认证实现原理 深入Pyrlang源码EPMD注册与Erlang分布式协议challenge-response握手认证实现原理【免费下载链接】PyrlangErlang node implemented in Python 3.5 (Asyncio-based)项目地址: https://gitcode.com/gh_mirrors/py/PyrlangPyrlang是一个用 Python 3.5基于 Asyncio实现的 Erlang 分布式节点库它能让 Python 进程以标准 Erlang 节点的身份参与分布式系统。本文带你深入Pyrlang 源码拆解两大核心机制如何通过EPMD 注册让节点互相发现以及Erlang 分布式协议中challenge-response 握手认证是如何完成双向身份校验的——无需任何代码基础也能看懂整个流程。 为什么需要这两步EPMD注册 挑战应答握手想象 Python 节点要加入一个 Erlang 集群它必须完成两件事登记门牌向每台机器上的EPMD 守护进程Erlang Port Mapper Daemon默认监听 4369 端口报告我是谁、我的分布端口是多少这样其他节点才能通过名字找到它验证身份与对端节点建立 TCP 连接后双方通过challenge-response挑战-应答交换基于 cookie 的 MD5 摘要证明彼此持有相同的密钥cookie防止陌生节点混入集群。这两个过程分别由三个核心模块承担职责模块路径EPMD 连接、注册与查询pyrlang/dist_proto/epmd_client.py节点网络生命周期编排pyrlang/dist_proto/distribution.py握手状态机收发两侧pyrlang/dist_proto/base_dist_protocol.py、pyrlang/dist_proto/server.py、pyrlang/dist_proto/client.py EPMD注册全流程节点启动的三步走编排入口是ErlangDistribution.start_distribution()它在 distribution.py 中只有寥寥数行却勾勒出完整启动链第 1 步先起本地监听器。start_server()在0.0.0.0的随机端口上创建 TCP 服务distribution.py记下分配到的in_port_。端口随机的意义在于一台机器上可以跑无数个 Python 节点互不冲突。第 2 步连接 EPMD。epmd_client.py 中的EPMDClient.connect()会尝试最多5 次、每次间隔 5 秒地连接本机127.0.0.1:4369并给出epmd -daemon的提示。注意这条连接是长连接——EPMD 靠连接断开来感知节点退出所以close()注释里写明关闭连接即从 EPMD 节点列表中注销自己epmd_client.py。第 3 步发送 ALIVE2 注册报文。这是 epmd_client.py 的alive2()方法报文结构在_make_req_alive2()中打包epmd_client.py命令字REQ_ALIVE2 120附带本节点类型普通节点72/ 隐藏节点77、协议类型0即 TCP/IP、支持的分布式协议版本区间(5, 6)见 version.pyv5 覆盖到很老的 R6Bv6 自 OTP-23 引入并在 OTP-25 成为强制节点名取之前的部分如py127.0.0.1只注册py。EPMD 的应答分两种ALIVE2_RESP [121, 0, Creation:16位]或双方都支持 v6 时的ALIVE2_X_RESP [118, 0, Creation:32位]。解析逻辑见 _read_alive2_reply()。这里返回的Creation 值非常关键——Pyrlang 用 EPMD 颁发的 creation 参与生成每个进程 PID 的唯一标识node.py这也是启动流程中先注册 EPMD、再派生进程的原因。 反向查询其他节点如何找到它当 Python 节点要主动连接远端时走的是即发即忘的查询通道EPMDClient.query_node()epmd_client.py。它把后的主机名做 DNS 解析向目标机器的 EPMD 发送PORT_PLEASE2 (122)命令读完RESP_PORT2 (119)应答就立即断开_fire_forget_query()。应答中包含对方端口、节点类型、协议与版本区间Pyrlang 会先用dist_version_check()判断版本是否重叠再取双方最大公共同版本epmd_client.py——这一步决定了后续握手走 v5 还是 v6 报文格式。 challenge-response握手四步完成双向认证TCP 连接建立后真正的Erlang 分布式协议握手才开始。整个流程被建模为状态机状态定义集中在 base_dist_protocol.py。有一个容易被忽略的细节握手阶段数据包长度前缀是 2 字节认证完成后切换为 4 字节packet_len_size_见 base_dist_protocol.py。客户端发起方 服务端被动方 │ │ │──── ① n/N: 节点名flags ──────▶│ │◀─── ② s:ok n/N: 挑战值C1 ────│ │──── ③ r: 自报C2 MD5(C1) ────▶│ │◀─── ④ a: MD5(C2) ─────────────│ │ 切换4字节包头进入CONNECTED① 交换名片RECV_NAME 状态发起方connection_made()后立即发送欢迎包client.pyv5 是n 版本 能力标志dflags 节点名v6 是N 64 位 flags creation 名字。被动方on_packet_recvname()server.py解析后先回一个简短的sok确认随后生成一个随机挑战值my_challenge_ int(random.random() * 0x7fffffff)并立即发出自己的挑战包。② 发起挑战CHALLENGE挑战包由_send_challenge()发出server.py携带本节点名、flags 和那个随机数。随机数即challenge——每次连接都不同保证摘要不可重放。③ 应答摘要CHALLENGE_REPLY客户端收到挑战后在 _send_challenge_reply() 中计算MD5(cookie str(对方挑战值))作为摘要连同自己生成的新挑战值 C2一起用r包发出。④ 双向确认CHALLENGE_ACK服务端在on_packet_challengereply()server.py中用自己的 cookie重算摘要并比对比对通过后才用 C2 和 cookie 算出确认摘要以a包回发并把包头切换为 4 字节、置为CONNECTED。客户端在 on_packet_recvchallenge_ack() 做同样的反向校验任何一方摘要不符都会直接抛DistributionError断开。 这就是challenge-response的精髓双方各自出题、对方交卷从而相互证明都持有同一 cookie。摘要算法集中在 base_dist_protocol.py 的make_digest()/check_digest()两个静态方法中# 摘要 MD5(cookie的ASCII字节 挑战值的字符串形式) result md5(bytes(cookie, ascii) bytes(str(challenge), ascii)).digest() cookie从哪来常见失败点与调试技巧cookie 在创建节点时传入Node(py127.0.0.1, COOKIE)node.py由DistributionFlags统一保管flags.py。默认能力位DEFAULT_DFLAGS也在此定义其中DFLAGS_HANDSHAKE_23是 OTP 25 强制要求的新版握手标志位flags.py。对照源码排查连接失败可沿以下线索连不上 EPMD日志会提示 Is local EPMD running? Tryepmd -daemon对应 epmd_client.py 的重试逻辑cookie 不匹配服务端日志打印Disallowed node connection (check the cookie)server.py——这是最常见的配置错误检查 Erlang 侧~/.erlang.cookie与 Python 侧传入的 cookie 是否一致版本不重叠EPMD 查询阶段就会报 supports protocol version %s and we support %sepmd_client.py重复连接若对端已存在连接会收到salive状态并协商true/false决定新连接是否保留client.py保活机制连接建立后每 15 秒发空心跳、20 秒检查一次活跃度、30 秒无交互即销毁连接base_dist_protocol.py。值得一提的是握手成功后 Pyrlang 节点里注册了net_kernel进程它会响应 Erlang 侧net_adm:ping发出的is_auth探测net_kernel.py这与你日常用net_adm验证节点互信的操作直接对应。 小结一张图记住 Pyrlang 的分布式认证链路阶段关键动作源码位置启动随机端口监听 → EPMD 长连接 → ALIVE2 注册拿 creationdistribution.py发现PORT_PLEASE2查询远端端口与协议版本epmd_client.py握手n/N → sok → 挑战 → r摘要 → a确认2 字节转 4 字节包头server.py、client.py认证MD5(cookie challenge)双向比对base_dist_protocol.py至此Pyrlang 的 EPMD 注册解决了被发现的问题challenge-response 握手认证解决了被信任的问题——两者叠加Python 节点才能真正无缝融入 Erlang 分布式生态。想继续深入可以从 examples/ 目录下的端到端示例入手例如 Python 与 Erlang 节点互相监控的演示脚本。【免费下载链接】PyrlangErlang node implemented in Python 3.5 (Asyncio-based)项目地址: https://gitcode.com/gh_mirrors/py/Pyrlang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻