===============
== ffff65535 ==
===============
十六个一[1111111111111111]

Portainer Edge Agent 连接超时问题解决:自定义 Tunnel URL

portainer edge-agent reverse-proxy base64 tunnel caddy traefik

当你将 Portainer Server 部署在 反向代理(如 Caddy、Nginx 或 Traefik)之后,并尝试添加 Edge Agent 时,可能会遇到如下错误:

2025/10/25 16:59:33 client: Connecting to ws://portainer.yourdomain.com:8000
2025/10/25 17:00:18 client: Connection error: dial tcp [your ip]:8000: i/o timeout
2025/10/25 17:00:18 client: Give up

该问题的根本原因在于:Portainer 默认生成的 Edge 连接信息中,Tunnel 地址为 域名:8000,但反向代理通常并未暴露 8000 端口,导致 Edge Agent 无法建立 WebSocket 隧道连接。

📌 此问题已在官方 GitHub 讨论:portainer/portainer#6251


🔍 问题原理

Portainer 在创建 Edge Agent 时,会生成一个 EDGE_KEY,其本质是一个 Base64 编码的字符串,解码后格式如下:

<Portainer URL>|<Tunnel URL>|<签名>|<版本>

例如:

https://portainer.yourdomain.com|portainer.yourdomain.com:8000|iVtj5ErwqxBoMGwbiLceFyopO9GcdOrXkFlfhwo2DoM=|7
  • 第一段:Portainer Web 界面地址(用于 API)
  • 第二段:Tunnel 地址(用于 WebSocket 连接,默认为 域名:8000

但在反向代理架构中,8000 端口未对外暴露,而 Tunnel 流量应通过 HTTPS(443)由反向代理转发到 Portainer 的 8000 端口。因此,必须将第二段改为完整的 HTTPS URL。


✅ 解决方案:手动修改并重新编码 EDGE_KEY

步骤 1:获取原始 EDGE_KEY

在 Portainer Web 界面创建 Edge Agent 后,会得到类似如下的 EDGE_KEY

aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218cG9ydGFpbmVyLnlvdXJkb21haW4uY29tOjgwMDB8aVZ0ajVFcndxeEJvTUd3YmlMY2VGeW9wTzlHY2RPclhrRmxmaHdvMkRvTT18Nw

步骤 2:Base64 解码

使用命令行解码(注意:需使用 URL-safe Base64 解码,但标准 base64 -d 通常兼容):

echo "aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218cG9ydGFpbmVyLnlvdXJkb21haW4uY29tOjgwMDB8aVZ0ajVFcndxeEJvTUd3YmlMY2VGeW9wTzlHY2RPclhrRmxmaHdvMkRvTT18Nw" | base64 -d

输出:

https://portainer.yourdomain.com|portainer.yourdomain.com:8000|iVtj5ErwqxBoMGwbiLceFyopO9GcdOrXkFlfhwo2DoM=|7

步骤 3:修改 Tunnel URL

将第二段 portainer.yourdomain.com:8000 替换为完整的 HTTPS 地址

https://portainer.yourdomain.com|https://portainer.yourdomain.com|iVtj5ErwqxBoMGwbiLceFyopO9GcdOrXkFlfhwo2DoM=|7

✅ 前提:你的反向代理(如 Caddy)已将 https://portainer.yourdomain.com8000 端口流量正确代理到 Portainer 容器的 8000 端口。

例如 Caddy 配置应包含:

portainer.yourdomain.com {
    import common_config
    
    # === 1. WebSocket 隧道:必须代理到 8000 端口 ===
    @websocket {
        header Connection *Upgrade*
        header Upgrade websocket
    }
    handle @websocket {
        reverse_proxy portainer:8000 {
            flush_interval -1
            header_up Connection {http.request.header.Connection}
            header_up Upgrade {http.request.header.Upgrade}
        }
    }

    # === 2. 所有 API 和 Edge 路径 → 9000 端口,无认证 ===
    @api path /api/* /edge/*
    handle @api {
        reverse_proxy portainer:9000 {
            header_up X-Forwarded-Proto https
            header_up X-Forwarded-Host {host}
        }
    }
    
    # === 3. 其余所有路径(Web UI)→ 启用 BasicAuth + 代理到 9000 ===
    handle {
        basicauth {
            admin $2a$14$VpF6lO1.BSuQepgowmHsz.kl9ELHfhwVHJ4F0nQQMBTU.IFbr6HRy
        }
        reverse_proxy portainer:9000 {
            header_up X-Forwarded-Proto https
            header_up X-Forwarded-Host {host}
        }
    }
}

步骤 4:重新 Base64 编码

将修改后的字符串重新编码:

echo -n "https://portainer.yourdomain.com|https://portainer.yourdomain.com|iVtj5ErwqxBoMGwbiLceFyopO9GcdOrXkFlfhwo2DoM=|7" | base64

⚠️ 务必加上 -n,避免换行符被编码。

输出示例:

部署后若仍存在问题,可尝试移除base64编码后的等号“=”

aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218aVZ0ajVFcndxeEJvTUd3YmlMY2VGeW9wTzlHY2RPclhrRmxmaHdvMkRvTT18Nw

步骤 5:使用新 EDGE_KEY 启动 Edge Agent

docker run 命令中替换 EDGE_KEY

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/volumes:/var/lib/docker/volumes \
  -v /:/host \
  -v portainer_agent_data:/data \
  --restart always \
  -e EDGE=1 \
  -e EDGE_ID=your_edge_id \
  -e EDGE_KEY=aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218aHR0cHM6Ly9wb3J0YWluZXIueW91cmRvbWFpbi5jb218aVZ0ajVFcndxeEJvTUd3YmlMY2VGeW9wTzlHY2RPclhrRmxmaHdvMkRvTT18Nw \
  -e EDGE_INSECURE_POLL=0 \
  --name portainer_edge_agent \
  portainer/agent:2.27.6

✅ 启动后,Edge Agent 将通过 wss://portainer.yourdomain.com 建立安全 WebSocket 隧道,不再尝试直连 8000 端口。


⚠️ 注意事项

  1. Base64 编码末尾的 = 是填充符,某些系统可能要求去除(如 URL 中),但 Portainer Agent 通常能正确处理。建议编码后再次解码验证内容是否一致
  2. 反向代理必须支持 WebSocket 代理,可能需显式配置。
  3. 若使用 HTTPS,确保 TLS 证书有效,否则需设置 -e EDGE_INSECURE_POLL=1(不推荐生产环境使用)。
  4. 此方法为临时 workaround,官方未来可能提供 UI 配置 Tunnel URL(见 issue #6251)。

通过以上步骤,你即可在反向代理架构下成功部署 Portainer Edge Agent,实现远程节点的安全纳管。