Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Edge-Cloud Bastion

边缘设备统一接入平台部署文档

安全连接边缘设备与云端 —— 基于 AWS 的边缘设备统一接入平台,两大能力:远程开发运维(JumpServer 堡垒机隐藏于 CloudFront 之后,车载/办公室等边缘设备经 frp 隧道反向接入,一个 Web 控制台纳管全部资产)+远程监控运维(边缘设备 node_exporter 指标经 SigV4 直写 Amazon Managed Prometheus,Grafana 统一看板)。 Connect edge devices to the cloud, securely — bastion access and metrics collection for edge fleets on AWS. 覆盖:JumpServer v4.x | frp v0.71.x | CloudFront VPC Origins | IAM Roles Anywhere | AMP / Grafana 两部分均按部署顺序组织,各自独立可用;全流程已在真实环境端到端验证。

目录

仓库结构 —— 目录布局 · 脚本清单(运行位置 / 凭证需求 / 执行频次)· 锁定的组件版本

系统总览 —— 平台定位 · 总体架构 · 两大能力 · 典型应用场景

第一部分:远程开发运维(JumpServer + frp + CloudFront)

  1. 架构与组件 —— 第一部分全景架构、组件清单与流量路径
  2. 部署准备 —— 前置条件、变量、交付物脚本清单
  3. 阶段一:JumpServer Web 入口(CloudFront + VPC Origin)
  4. 阶段二:frp 设备接入(frps → 设备入口 → 设备端 frpc)
  5. 阶段三:JumpServer 资产纳管(节点树 / 批量导入 / API)
  6. 阶段四:安全加固(WAF · 源站保护 · 日志)
  7. 安全模型说明(供客户安全团队评审)
  8. 运维手册(故障排查 / token 轮换 / 日常操作)
  9. 成本估算
  10. 回滚与清理

第二部分:远程监控运维(node_exporter + AMP + Grafana,零长期凭证)

  1. 监控架构与认证设计 —— Roles Anywhere 凭证链、方案选型
  2. 云端部署(手动创建 AMP / Grafana workspace + CloudShell 两步:init 引导、device 设备包)
  3. 设备端接入(一键安装:裸机全自动 / 已有组件增量)
  4. 监控链路故障排查
  5. 成本与日常运维(证书轮换 / 系列数控制)

仓库结构

本仓库 = 一份部署文档(本 README)+ 一套可直接执行的交付脚本。脚本按能力分三个目录,全部幂等可重跑,关键组件版本在脚本头部集中锁定,升级只改一处。

.
├── README.md                                  # 本文档(唯一权威说明,两部分按部署顺序组织)
├── edge-cloud-bastion.html                    # 单页离线版(可直接发给客户,无需网络与 Markdown 渲染器)
├── jumpserver/
│   ├── setup-cloudfront-jumpserver.sh         # 阶段一:CloudFront 入口① → JumpServer:80(VPC Origin)
│   └── AWS VPC - jp-demo项目.drawio           # 网络拓扑源图(draw.io / diagrams.net 可编辑)
├── frp/
│   ├── setup-frp-server.sh                    # 阶段二·1:frps EC2 上装 frps(含 token 生成与轮换)
│   ├── setup-frp-cloudfront.sh                # 阶段二·2:CloudFront 入口② → frps:7000(wss 接入)
│   ├── setup-frp-client.sh                    # 阶段二·3:设备端装 frpc(交互 / 批量刷机两种模式)
│   └── test-frp-link.sh                       # 阶段二·验证:入口②全链路连通性自检(无需真 token)
└── amp-edge-monitoring/
    └── setup-amp-edge.sh                      # 第二部分全部云端操作:init 引导 + device 设备包
脚本 运行位置 需要 AWS 凭证 执行频次
setup-cloudfront-jumpserver.sh AWS CloudShell(推荐)或本地 CLI v2 每套环境一次
setup-frp-cloudfront.sh AWS CloudShell(推荐)或本地 CLI v2 每套环境一次
setup-frp-server.sh frps EC2(root) 每套环境一次 + token 轮换时
setup-frp-client.sh 边缘设备(root) 每台设备一次
test-frp-link.sh 任意联网的 macOS / Linux 按需验证
setup-amp-edge.sh init AWS CloudShell 每账号一次
setup-amp-edge.sh device AWS CloudShell 每台设备一次
install-edge-collector.sh(由 device 子命令动态生成,在设备包内) 边缘设备(root) 每台设备一次

💡 设备端脚本一律零 AWS 凭证需求:frpc 只用 frp token,采集端只用 X.509 设备证书。所有需要 AWS 权限的动作都被收敛到 CloudShell 里执行。

脚本锁定的组件版本(升级即改脚本头部常量,无需改文档):

组件 版本 锁定位置
frp(frps / frpc) v0.71.0 三个 frp 脚本的 FRP_VER
aws_signing_helper 1.8.5(附 sha256 校验) setup-amp-edge.shHELPER_VERSION / SHA_X86 / SHA_ARM
node_exporter 1.11.1(附官方 sha256 校验) setup-amp-edge.sh 内嵌安装器的 NE_VERSION
Prometheus(agent 模式) 3.13.2 LTS(附官方 sha256 校验) setup-amp-edge.sh 内嵌安装器的 PROM_VERSION
JumpServer v4.x(官方 quick_start 安装,本方案不代管其安装)

两部分的独立性jumpserver/ + frp/(第一部分)与 amp-edge-monitoring/(第二部分)没有任何依赖关系,可只部署其一。同一批设备同时接入两条通道时,两者互不影响。

系统总览

这套系统是什么

Edge-Cloud Bastion 是一套构建在 AWS 上的边缘设备统一接入平台:把分散在办公室 NAT 与车辆蜂窝网络之后、没有固定公网 IP 的边缘设备,与云上服务器一起接入统一管理平面。平台提供两大能力——远程开发运维(浏览器点击即连任意设备,全程审计)与远程监控运维(设备指标持续入云,统一看板与告警),分别对应本文档的第一、第二部分。

被管理对象 所在网络 接入方式
云上服务器(WorkNode EC2) AWS VPC 私有子网 VPC 内私网直连
办公室局域网设备 客户办公室 NAT 之后 设备 frpc 主动外连反向隧道
车载设备 蜂窝网络(普通 SIM,IP 漫游/CGNAT) 设备 frpc 主动外连反向隧道

同一批设备可同时接入两条通道:访问通道解决"连上去操作",监控通道解决"不连上去也随时知道它好不好"。

总体架构

flowchart LR
  U["运维 / 研发用户<br/>(浏览器)"]
  subgraph EDGE["现场边缘设备(办公室 NAT / 车辆蜂窝网络,无固定公网 IP)"]
    FRPC["frpc<br/>访问通道客户端"]
    NE["node_exporter +<br/>Prometheus agent<br/>监控通道采集端"]
  end
  subgraph AWSC["AWS(公网暴露面 100% 由托管服务承担,EC2 均无公网 IP)"]
    subgraph CFL["公网入口层(共享同一 WAF)"]
      CF1["CloudFront 入口①<br/>Web 访问"]
      CF2["CloudFront 入口②<br/>设备接入 (wss)"]
    end
    subgraph P1["第一部分:远程开发运维"]
      JMS["JumpServer 堡垒机<br/>纳管 · 授权 · 审计"]
      FRPS["frps<br/>设备隧道汇聚"]
    end
    subgraph P2["第二部分:远程监控运维"]
      RA["IAM Roles Anywhere<br/>X.509 证书 → 临时凭证"]
      AMP["Amazon Managed<br/>Prometheus(AMP)"]
      GRAF["Amazon Managed Grafana<br/>(IAM Identity Center 登录)"]
    end
  end
  U -- "① HTTPS 访问" --> CF1
  CF1 -- "私网回源" --> JMS
  FRPC -- "② wss:443 主动外连" --> CF2
  CF2 -- "私网回源" --> FRPS
  JMS -- "③ SSH 私网 :600x" --> FRPS
  NE -- "④ remote_write + SigV4" --> AMP
  NE -. "X.509 换临时凭证" .-> RA
  U -- "⑤ 看板 / 告警" --> GRAF
  GRAF --> AMP
  style JMS fill:#dbeafe,stroke:#1a56db
  style FRPS fill:#dbeafe,stroke:#1a56db
  style CF1 fill:#f3e8ff,stroke:#7c3aed
  style CF2 fill:#f3e8ff,stroke:#7c3aed
  style CFL fill:#faf5ff,stroke:#7c3aed,stroke-dasharray:4
  style AMP fill:#dcfce7,stroke:#16a34a
  style GRAF fill:#dcfce7,stroke:#16a34a
  style RA fill:#fef3c7,stroke:#d97706
Loading

两大能力

能力 解决的问题 技术栈 对应章节
远程开发运维(访问通道) 无固定公网 IP 的设备如何被安全地"连上去":浏览器点击即连,命令级审计 + 会话录像 JumpServer + frp + CloudFront VPC Origin + WAF 第一部分(1-10 章)
远程监控运维(监控通道) 不登录设备也能掌握其健康状态:CPU / 磁盘 / 温度 / 在线状态秒级入云,统一看板与告警 node_exporter + Prometheus agent + IAM Roles Anywhere + AMP + Grafana 第二部分(11-15 章)

💡 两条通道共享同一套设计哲学:① 一切连接由设备主动外连(frpc wss 与 remote_write 均为出站 443),现场网络零入站开口;② 零长期凭证——人用 IAM Identity Center 登录,机器用 X.509 证书经 Roles Anywhere 换临时凭证,全账号不创建 IAM User / 不下发 AK;③ 公网暴露面 100% 由 AWS 托管服务(CloudFront / AMP 端点)承担,所有 EC2 均无公网 IP;④ 全程可审计——访问通道命令审计 + 录像,监控与入口层 WAF / CloudFront 日志留存。

典型应用场景(智能汽车研发 · OEM / Tier1)

本方案的直接业务背景:OEM / Tier1 的自动驾驶研发体系中,测试车辆分散在各地路测,数采 / 标定 / 训练资源分布在办公室集群,两端都没有固定公网地址。研发团队需要一条安全、可审计、随时可达的访问通道,支撑数据采集与研发迭代——这正是本系统要建立的通道。

# 场景 谁在用 通过本系统怎么做
1 路测车辆远程诊断与数采运维 数采 / 自动驾驶测试工程师 车载域控(数采主机)经蜂窝网络 frpc 常驻在线,工程师浏览器点击车辆资产即进入车机终端:检查数采软件与传感器状态、启停采集任务、查看磁盘水位与日志——无需追车、无需回场
2 采集数据抽检与小样本回传 数据工程团队 事件片段、日志、标注样本等小体量数据经隧道 SFTP 直接拉回;TB 级整盘数据仍走硬盘回收 / 专线,隧道承担"先看后传"的远程抽检筛选,减少无效回传
3 办公室标定 / HIL / 训练集群统一纳管 算法与标定工程师 办公室 GPU 训练机、标定台架、HIL 台架经 frpc(或网域网关)接入,与云上资源在同一资产树;出差、居家均从统一入口访问,权限与审计一致
4 OEM ↔ Tier1 跨组织协作 双方项目团队 按 JumpServer 节点授权:Tier1 工程师仅能看到被授权的车辆与台架,命令级审计 + 会话录像全程留痕,满足数据安全与商密管理要求
5 试验车异常应急取证 质量 / 试验车队工程师 车辆在外出现异常,远程登入固定日志与现场数据,不必等车回场,缩短问题闭环周期

💡 该形态对汽车研发场景的关键适配:① 测试车使用普通互联网 SIM(CGNAT、IP 随基站漂移),无法反向寻址——frpc 主动外连模式天然解决;② 办公室 IT 只需放行 443 出站,零入站开口;③ 全程命令审计与会话录像,为智能网联汽车数据合规(采集范围、操作留痕)提供审计依据;④ 资产连通性状态即车辆 / 台架在线状态,免费获得车队在线监控。

边界说明:本通道定位为运维 / 控制平面(交互式终端、任务启停、小流量传输),不承载大规模数采回传的主数据链路——后者按数据量走硬盘回收、专线或 S3 直传方案。


第一部分:远程开发运维

浏览器统一入口远程访问云上服务器、办公室集群与车载设备 —— JumpServer + frp + CloudFront,全程审计

1. 架构与组件

1.1 全景架构

flowchart LR
  U["运维 / 业务用户<br/>(浏览器)"]
  subgraph AWS["AWS 东京区域(EC2 均无公网 IP)"]
    WAF["AWS WAF 共享 Web ACL<br/>IP信誉 · OWASP · SQLi · 速率限制"]
    CF1["CloudFront 入口①<br/>Web 访问"]
    CF2["CloudFront 入口②<br/>设备接入 (wss)"]
    subgraph PRI["私有子网"]
      JMS["JumpServer EC2<br/>纳管 · 授权 · 审计"]
      subgraph FRPSEC2["frps EC2(兼 WorkNode #1,同一台机器)"]
        FRPS["frps<br/>设备隧道汇聚点"]
        WN1["WorkNode #1"]
      end
      WN2["WorkNode EC2 #2"]
      WN3["WorkNode EC2 #N(按需扩展)"]
    end
    S3LOG["S3 日志桶<br/>CloudFront 访问日志 · 90天"]
    CWLOG["CloudWatch Logs<br/>WAF 拦截/放行日志 · 90天"]
  end
  subgraph DEV["现场设备(客户办公室 / 车辆)"]
    OFF["办公室设备 frpc"]
    CAR["车载设备 frpc"]
  end
  U -- "① HTTPS 登录" --> CF1 --> JMS
  JMS -- "② SSH :22 私网直连" --> WN1
  JMS -- "②" --> WN2
  JMS -- "②" --> WN3
  JMS -- "③ SSH :600x 私网" --> FRPS
  OFF -- "④ wss :443 主动外连" --> CF2
  CAR -- "④ wss :443 主动外连" --> CF2
  CF2 --> FRPS
  FRPS -. "⑤ 反向隧道" .-> OFF
  FRPS -. "⑤ 反向隧道" .-> CAR
  WAF -. "L7 边缘防护" .- CF1
  WAF -. "L7 边缘防护" .- CF2
  CF1 -. "访问日志" .-> S3LOG
  CF2 -. "访问日志" .-> S3LOG
  WAF -. "WAF 日志" .-> CWLOG
  style JMS fill:#dbeafe,stroke:#1a56db
  style FRPS fill:#dbeafe,stroke:#1a56db
  style FRPSEC2 fill:#eff6ff,stroke:#1a56db,stroke-dasharray:4
  style CF1 fill:#f3e8ff,stroke:#7c3aed
  style CF2 fill:#f3e8ff,stroke:#7c3aed
  style WAF fill:#fee2e2,stroke:#dc2626
  style S3LOG fill:#f1f5f9,stroke:#64748b
  style CWLOG fill:#f1f5f9,stroke:#64748b
Loading

1.2 组件清单与流量路径

组件 部署位置 角色
CloudFront 入口① AWS 托管(全球边缘) 用户浏览器访问 JumpServer 的唯一公网入口,VPC Origin 私网回源
CloudFront 入口② AWS 托管(全球边缘) 设备 frpc 的 wss 接入入口,VPC Origin 回源 frps:7000
JumpServer EC2 私有子网,独立一台 堡垒机本体:纳管、授权、审计、录像
frps EC2(兼 WorkNode #1) 私有子网,独立一台 设备隧道汇聚点:每台设备映射为本机一个私网端口(6001-6299);同机也作为一个被管节点
WorkNode EC2 #2…#N 私有子网,按需扩展 被管理的云上服务器,22 端口仅对 JumpServer 安全组开放
AWS WAF(阶段四) us-east-1 全球作用域 一个 Web ACL 同时保护两个 CloudFront 入口(含 frp 握手放行规则)
日志(阶段四) S3 桶(东京)+ CloudWatch Logs(us-east-1) 两个入口的 CloudFront 访问日志入 S3、WAF 拦截/放行日志入 CloudWatch,均保留 90 天
通道 流量路径 对应章节
人访问系统 浏览器 → 入口① → JumpServer → 云上/现场资产 阶段一
设备接入系统 设备 frpc --wss--> 入口② → frps ← JumpServer 私网 :600x 阶段二

设备接入的关键机制:设备端 frpc 主动向外拨号建立 WebSocket 反向隧道(办公室防火墙只需允许出站 443,车辆蜂窝网络天然支持),frps 把每台设备映射为自己的一个私网端口,JumpServer 把「frps私网IP:端口」当普通 SSH 资产纳管——frp 中间层对用户完全透明。

2. 部署准备

2.1 前置条件核对表

# 条件 检查 / 修复命令
1 已有 VPC,含私有子网、IGW、NAT 网关 aws ec2 describe-internet-gateways --filters Name=attachment.vpc-id,Values=<VPC-ID>(VPC Origins 硬性要求挂 IGW,即使实例在私有子网;不需要改私有子网路由)
2 VPC 启用 DNS support + DNS hostnames aws ec2 modify-vpc-attribute --vpc-id <VPC-ID> --enable-dns-support / --enable-dns-hostnames
3 VPC 未开 Block Public Access,或已建豁免 aws ec2 describe-vpc-block-public-access-exclusions;无豁免则 create-vpc-block-public-access-exclusion(BPA 会拦截 VPC Origin 的创建与回程流量,实际部署中遇到过)
4 JumpServer EC2 运行中(私有子网、监听 80) 主机上 ss -ltnp | grep 80
5 执行者 IAM 权限 cloudfront:CreateVpcOrigin/GetVpcOrigin/CreateDistribution、ec2:Describe*、ec2:AuthorizeSecurityGroupIngress、wafv2:、logs:、sts:GetCallerIdentity
6 操作终端 推荐 AWS CloudShell(凭据天然就位,省 -p 参数;保持浏览器标签页打开)或本地 CLI v2 + --profile customer不要用 Git Bash——MSYS 路径转换会破坏 file:// 参数

2.2 交付物脚本清单

脚本 位置 作用 对应章节
setup-cloudfront-jumpserver.sh jumpserver/ 一键创建入口①(VPC Origin + Distribution + 安全组放行) 阶段一
setup-frp-server.sh frp/ frps EC2 上一键安装 frps(下载/token/配置/systemd) 阶段二·步骤 1
setup-frp-cloudfront.sh frp/ 一键创建入口②(VPC Origin 指向 frps:7000 + Distribution + 安全组放行) 阶段二·步骤 2
setup-frp-client.sh frp/ 设备端一键安装 frpc(含 sshd 保活),支持交互与批量两种模式 阶段二·步骤 3
test-frp-link.sh frp/ 入口②连通性验证工具(macOS / Linux 通用),不需要真 token 即可判定 wss→CloudFront→VPC Origin→frps 全链路是否贯通 阶段二·验证

💡 所有脚本均幂等可重跑:VPC Origin / Distribution 按名称或关联关系自动复用,轮询超时后用相同参数重跑即可续接。三个 frp 脚本统一锁定 frp v0.71.0,升级版本只需改脚本头部的 FRP_VER

2.3 部署前设置环境变量

export AWS_DEFAULT_REGION=ap-northeast-1
VPC_ID=<VPC-ID>              # 例: vpc-0123456789abcdef0
PRI_SUBNET=<私有子网ID>       # frps 所在子网(与 JumpServer 同子网或可互通)
JMS_SG=<JumpServer安全组ID>
KEY_NAME=<EC2密钥对名称>
AMI_ID=$(aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 --query 'Parameters[0].Value' --output text)

3. 阶段一:JumpServer Web 入口(CloudFront + VPC Origin)

目标:浏览器经 https://<CloudFront域名> 登录私有子网中的 JumpServer

3.1 链路与设计要点

用户浏览器 --HTTPS:443--> CloudFront入口① --VPC Origin(ENI)--> JumpServer EC2:80(私有子网)
                                                                  │ SSH:22(JumpServer托管凭据发起)
                                                                  ▼
                                                              WorkNode EC2(22 仅对 JumpServer SG 开放)
设计 原因
JumpServer 私有子网、无公网 IP 零直接暴露面,入口只有 CloudFront
CloudFront VPC Origins 无需公网地址/公网 LB,CloudFront 直连私有子网 ENI 回源
WorkNode 22 端口只放行 JumpServer SG 强制所有 SSH 经堡垒机,无法绕行
CachingDisabled + AllViewerExceptHostHeader 动态应用 + WebSocket(Web 终端),不能缓存、需透传全部头
redirect-to-https 用户侧强制加密

能力边界:CloudFront 只承载 HTTP/HTTPS(含 WebSocket),覆盖 Web 登录、Web 终端、Web SFTP 全部使用场景;如未来需要原生 SSH 客户端直连 koko(ssh user@bastion -p 2222),需另行评估四层 TCP 入口,不在本方案范围。

3.2 执行部署(一键脚本)

bash setup-cloudfront-jumpserver.sh \
  [-p customer] \          # aws profile(CloudShell 中省略)
  -r ap-northeast-1 \      # 区域
  -i i-xxxxxxxxxxxxxxxxx \ # JumpServer 实例 ID
  [-P 80] \                # JumpServer HTTP 端口,默认 80
  [-g sg-xxxxxxxx]         # 实例挂多个安全组时明确指定要放行的那个

脚本自动完成:身份确认(防止跑错账号)→ 预检(实例/IGW/DNS 属性)→ 创建 VPC Origin(轮询至 Deployed,实测约 12 分钟)→ 创建 Distribution(策略要点见下表)→ 放行安全组(80 ← CloudFront-VPCOrigins-Service-SG,幂等)→ 输出 CloudFront 域名。

Distribution 配置项
Cache Policy CachingDisabled(托管策略 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
Origin Request Policy AllViewerExceptHostHeader(b689b0a8-53d0-40ab-baf2-68738e2966ac
AllowedMethods 全部 7 种(登录 POST、API PUT/DELETE 都需要)
ViewerProtocolPolicy redirect-to-https
OriginReadTimeout / KeepaliveTimeout 60s / 30s

3.3 必做手动步骤:JumpServer 信任 CloudFront 域名(DOMAINS)

⚠️ ⚠️ 本方案最容易踩的坑。DOMAINS 没配(或与浏览器实际使用的域名不匹配)有两种症状:① 登录报 CSRF 错误;② 登录一切正常,但所有资产的 Web 终端都连不上(WebSocket Origin 校验被拒)——第二种极具迷惑性,实际部署中曾被误判为资产/账号/密钥/网络问题排查数小时。新建或更换 Distribution 后必须同步追加新域名。

# 登录 JumpServer 主机(SSM Session Manager 或既有方式)
sudo su -
echo 'DOMAINS="dxxxxxxxxxxxx.cloudfront.net"' >> /opt/jumpserver/config/config.txt
cd /opt/jumpserver-installer-*   # 官方 quick_start 安装的默认路径
./jmsctl.sh restart

非官方脚本安装时 config.txt 位置以 jmsctl.sh 所在目录的 config 为准;已有 DOMAINS 行时用逗号追加域名,不要重复添加整行。

3.3.1 自定义域名 / 换域名(三件套,缺一即坏)

把入口从默认 *.cloudfront.net 换成自有域名(CloudFront 备用域名 + ACM 证书配好之后),JumpServer 侧必须同步改三处

# 配置项 位置 不改的症状
1 DOMAINS config.txt(改后需 ./jmsctl.sh restart 登录页直接报 "Configuration file has problems"
2 站点 URL Web 控制台 → 系统设置 → 基本设置 邮件/通知里的链接仍指旧域名
3 Endpoint.host Web 控制台 → 系统设置 → 终端管理 → 端点管理(改后即时生效) 登录一切正常,但所有资产 Web 终端"拒绝连接"——koko 的 connect URL 只用 Endpoint.host 生成,不读站点 URL;iframe 指向旧域名后跨站不带 Cookie → 被弹回登录页 → X-Frame-Options 拒绝渲染

⚠️ ⚠️ 本架构下 Endpoint.host 必须写死当前域名,不能留空。留空时 JumpServer 用 request.get_host() 跟随访问域名——但 CloudFront 的 AllViewerExceptHostHeader 源请求策略会剥离 Host 头,源站收到的 Host 永远是 VPC Origin 的内网名(如 ip-10-0-1-40...compute.internal),跟随机制在此架构下必坏。同理,依赖 request.get_host() 的功能(Passkey/WebAuthn origin 校验、CAS/OAuth2 回调)当前均不可用;未来要上 SSO/Passkey 需把源请求策略换成 Managed-AllViewer 并灰度验证。

3.4 WorkNode 安全组(新增节点时逐台执行)

aws ec2 authorize-security-group-ingress --region ap-northeast-1 \
  --group-id <WorkNode的SG> --protocol tcp --port 22 \
  --source-group $JMS_SG
# 并确认 22 端口没有对 0.0.0.0/0 或其他来源开放

3.5 验证清单

# 验证项 预期
1 aws cloudfront get-distribution --id <dist-id> --query 'Distribution.Status' Deployed(约 5-10 分钟)
2 浏览器打开 https://<CloudFront域名> JumpServer 登录页;初始 admin/ChangeMe,首次强制改密并立即启用 MFA
3 普通用户登录 → Web 终端连 WorkNode 成功落到目标机,会话有录像审计

4. 阶段二:frp 设备接入

目标:办公室/车载设备经反向隧道挂进 frps,成为 JumpServer 可管理的私网端口

4.0 资源与端口规划(先定表再动手)

项目 规划值 说明
frps 部署位置 复用 WorkNode #1 EC2(t3.small 级即可) 2000+ 设备内够用;也可独立小实例部署
公网入口② CloudFront wss:443 回源 frps:7000。用 443 是因为部分蜂窝网络/办公室防火墙只放行 443 出站
frps 控制端口 7000 仅接受 CloudFront VPC Origin 回源流量
车载设备映射端口 6001 – 6099 每车固定一个端口
办公室设备映射端口 6101 – 6199 同上
预留端口段 6201 – 6299 RDP/VNC/其他协议扩展(frp 是 TCP 层转发,可直接穿 VNC)
安全组 frps-sg 入站:7000 ← CloudFront 服务SG;6001-6299 ← JumpServer SG 最小权限,无任何 0.0.0.0/0

⚠️ ⚠️ 端口分配表必须留档:维护「设备 ↔ remotePort ↔ JumpServer 资产名」对照表(Excel/CMDB),frpc 配置与 JumpServer 资产都以它为准。frps 已配 maxPortsPerClient=4 防单设备占满端口池。安全组命名注意不能以 sg- 开头(AWS 资源 ID 前缀保留)。

4.1 步骤 1:安装 frps(在 frps EC2 上)

一键脚本(推荐):将 frp/setup-frp-server.sh 上传到 frps EC2(经 JumpServer SFTP、SSM 或 S3 中转),执行:

sudo bash setup-frp-server.sh
# 自动完成:架构检测 → 下载 v0.71.0 → 生成随机 token → 写配置 → systemd 注册 → 自检
# 结束会打印 TOKEN —— 妥善保存,所有设备端 frpc 用同一个
# 轮换 token:sudo bash setup-frp-server.sh <新token>(可重复执行,已有配置不覆盖)

手工等效配置(脚本不可用时),核心文件 /etc/frp/frps.toml

bindAddr = "0.0.0.0"
bindPort = 7000

auth.method = "token"
auth.token = "<openssl rand -hex 32 生成的64位随机串>"

# ⚠️ 不要开启 transport.tls.force —— frpc→CloudFront 段已是 wss(TLS),
# CloudFront→frps 回源是明文 ws,开 force 会拒掉回源导致全部设备无法接入

allowPorts = [ { start = 6001, end = 6299 } ]
maxPortsPerClient = 4

log.to = "/var/log/frps.log"
log.level = "info"
log.maxDays = 30

systemd 单元 /etc/systemd/system/frps.serviceExecStart=/opt/frp/frps -c /etc/frp/frps.tomlRestart=always),然后 systemctl enable --now frps,用 ss -lntp | grep 7000 确认监听。

4.2 步骤 2:创建设备接入入口②(CloudFront + VPC Origin)

# CloudShell 中执行
bash setup-frp-cloudfront.sh -i <frps实例ID> -j <JumpServer安全组ID>
# 自动完成:前置检查(IGW/BPA豁免,幂等) → VPC Origin 指向 frps:7000(Failed 旧资源自动清理)
#          → Distribution(WebSocket 调优)→ 安全组放行(7000←CloudFront服务SG、6001-6299←JMS SG)
#          → 输出 CloudFront 接入域名(即设备端 frpc 的 serverAddr)
# 全程约 10-20 分钟,大部分在等 AWS 部署

手工创建要点:VPC Origin 目标 = frps 实例 ARN、HTTPPort=7000、http-only;Distribution 缓存策略 CachingDisabled、源请求策略 AllViewerExceptHostHeader(WebSocket 的 Sec-WebSocket-* 头靠它转发)、Viewer 协议 https-only、Origin 超时 60s。技术依据:AWS 2026 年 5 月发布 VPC Origins 的 WebSocket 支持;CloudFront WebSocket 仅支持 HTTP/1.1(frpc 原生即是)。

4.3 步骤 3:设备端安装 frpc(办公室 / 车载通用)

一键脚本(推荐)frp/setup-frp-client.sh 自动检测 x86_64 / aarch64 / armv7l 架构:

# 交互模式:按提示输入 CloudFront接入域名 → 映射端口(按分配表) → 设备名 → token(静默输入)
sudo bash setup-frp-client.sh
# 批量部署(车队产线刷机):
sudo FRP_TOKEN=<token> bash setup-frp-client.sh <接入域名> 6001 car-001
# 显示 ✅ 即隧道建立成功,并打印该设备挂进 JumpServer 的资产参数

手工等效配置 /etc/frp/frpc.toml(车机-001 示例):

serverAddr = "<CloudFront接入域名>"
serverPort = 443
auth.method = "token"
auth.token = "<与 frps 相同的 token>"

transport.protocol = "wss"        # 走 WebSocket over TLS 穿 CloudFront

loginFailExit = false             # 蜂窝网络必配:首次连不上也持续重试
transport.heartbeatInterval = 30
transport.heartbeatTimeout = 90

[[proxies]]
name = "car-001-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6001                 # ★ 按端口分配表,每设备唯一(办公室设备用 61xx 段)

systemd 单元同 frps 模式(frpc -c /etc/frp/frpc.tomlRestart=alwaysAfter=network-online.target)。

4.4 步骤 4:sshd 会话保活(必配,一键脚本已自动完成)

CloudFront 对空闲约 10 分钟的 WebSocket 连接会超时断开;frp 心跳只保护控制连接,每条 SSH 会话是独立的 work connection。在设备端 sshd 开启保活即可彻底解决:

echo -e "ClientAliveInterval 60\nClientAliveCountMax 3" | sudo tee /etc/ssh/sshd_config.d/frp-keepalive.conf
sudo systemctl restart ssh    # Amazon Linux 等发行版服务名为 sshd

💡 补充:① JumpServer 自身的终端空闲超时(系统设置→安全设置)是独立安全策略,建议保留;② 设备上跑长任务用 tmux/nohup,网络波动断开后 tmux attach 继续。

4.5 验证清单

# 设备端:日志出现 login to server success / start proxy success
journalctl -u frpc -f
# frps 端:确认端口已映射
sudo ss -lntp | grep 6001
# JumpServer 机器上:应能直接 SSH 到设备
ssh -p 6001 root@<frps私网IP>

入口②链路自检(frp/test-frp-link.sh:在任意一台能上网的 macOS / Linux 上跑,用来把"设备连不上"拆成"链路问题"还是"设备/token 问题"——尤其适合在设备到场前先验收入口,或排查阶段四挂 WAF 后的集体掉线:

bash test-frp-link.sh <接入域名>          # 无 token:故意用错 token,只要 frps 回"token 校验失败"即证明全链路贯通
bash test-frp-link.sh <接入域名> <token>  # 有 token:看到 login success 即认证 + 链路全部通过

脚本自动按系统/架构下载 frpc v0.71.0,固定用预留段末位端口 6299 做测试映射(不与真实设备冲突,退出即释放),12 秒采样后直接给出结论与排查顺序。

5. 阶段三:JumpServer 资产纳管

目标:云上节点与现场设备统一进资产树,按节点授权、点击即连

5.1 节点树规划(先建目录再导资产)

/Default
├── AWS云资源
│   ├── WorkNode-01(即 frps 所在 EC2)
│   └── WorkNode-02 … N
└── 现场设备
    ├── 车载设备      (端口段 6001-6099)
    └── 办公室设备    (端口段 6101-6199)

按节点授权用户/用户组,可实现「车队运维只能看到车载设备」的最小权限分配。

5.2 添加资产的三种方式

方式一:手工单个(控制台 → 资产管理 → 资产列表 → 创建 → 主机):

字段 云上 WorkNode frp 现场设备
名称 WorkNode-02 车机-001
地址 该 EC2 私网 IP frps 私网 IP(所有 frp 设备相同)
协议/端口 SSH 22 SSH 600x(按端口分配表,每资产独立端口)
节点 /Default/AWS云资源 /Default/车载设备 或 /办公室设备
账号 录入 root/密钥或专用账号,用户连接时自动登录 同左

保存后点「测试可连接性」,绿色即打通。

方式二:CSV 批量导入(资产列表 → 导入 → 下载模板):

name,address,platform,protocols,nodes
车机-001,<frps私网IP>,Linux,ssh/6001,/Default/车载设备
车机-002,<frps私网IP>,Linux,ssh/6002,/Default/车载设备
办公室-NAS,<frps私网IP>,Linux,ssh/6101,/Default/办公室设备

方式三:API 自动注册(新车上线自动化):

curl -s -X POST https://<JumpServer地址>/api/v1/assets/hosts/ \
  -H "Authorization: Token <JMS-API-Token>" -H "Content-Type: application/json" \
  -d '{"name":"车机-003","address":"<frps私网IP>","platform":{"pk":1},
       "protocols":[{"name":"ssh","port":6003}],"nodes":[{"pk":"<车载设备节点ID>"}]}'

💡 ✅ 附带收益:JumpServer 的资产连通性测试走的就是 frps私网IP:端口,frpc 掉线时资产立即显示不可达——相当于免费获得设备在线状态监控。

⚠️ 首次执行「测试可连接性」若报错「未找到 Ansible Docker 镜像 jumpserver/ansible-executor:latest」:在宿主机 docker pull jumpserver/ansible-executor:latest,或在 系统设置→功能设置→作业中心 关闭「Ansible Docker 隔离」。

6. 阶段四:安全加固(WAF · 源站保护 · 日志)

三项均围绕 CloudFront 入口实施,全部通过 AWS 控制台配置,对已部署系统零改动、零停机

6.1 加固总览与实施顺序

顺序 加固项 作用层 作用对象
1(先开) 日志启用 可观测层 两个 Distribution 访问日志 + WAF 日志——先获得流量基线
2 源站保护核查 网络层 JumpServer / frps 实例的安全组与公网暴露面
3 AWS WAF L7 边缘 入口①+②共享一个 Web ACL

6.2 AWS WAF 配置

前提说明:

  • CloudFront 的 Web ACL 必须创建在 Global (CloudFront) 作用域——进入 WAF 控制台后在 Region 下拉框选择 Global (CloudFront)
  • 一个 Web ACL 可同时关联多个 Distribution,本方案两个入口共享一个
  • 下表规则合计约 1334 WCU,在默认容量上限 1500 之内

规则清单:

# 规则 说明 动作 WCU
0 自定义放行规则 allow-frp-tunnel(最高优先级,必配) frpc 的 WebSocket 握手请求(GET /~!frp)不携带 User-Agent 头,会命中 #4 中的 NoUserAgent_HEADER 子规则被边缘拦截,导致设备无法接入。须最先放行设备入口的该路径 Allow:Host == 设备入口域名 且 URI 前缀 == /~!frp 4
1 AWSManagedRulesAmazonIpReputationList 已知恶意 IP、僵尸网络、扫描源 Block 25
2 AWSManagedRulesKnownBadInputsRuleSet Log4Shell、主机头注入等已知漏洞利用特征 Block 200
3 AWSManagedRulesSQLiRuleSet SQL 注入 Block 200
4 AWSManagedRulesCommonRuleSet XSS、LFI 等 OWASP 通用攻击 Block,其中 SizeRestrictions_BODY 子规则改为 Count(该子规则拦截 >8KB 请求体,会影响 JumpServer 文件上传与批量导入) 700
5 AWSManagedRulesLinuxRuleSet Linux 路径穿越、命令注入 Block 200
6 速率规则 rate-limit-all CC / 洪水防护 单 IP 2000 请求 / 5 分钟,超限 Block(自动解封) 2
7 速率规则 rate-limit-login 登录爆破防护 URI 含 /auth 的请求单 IP 100 次 / 5 分钟,超限 Block 3

⚠️ 灰度建议(生产环境):#0/1/2/3/6/7 直接启用;#4、#5 两组先整组 Count 模式观察 1–2 周,在 WAF 日志中确认无正常运维流量命中后再切 Block。

控制台配置步骤:

  1. 控制台搜索进入 WAF & Shield → 左侧 Web ACLs → Region 下拉选 Global (CloudFront)Create web ACL
  2. Name 填 jumpserver-edge-waf;Resource type 选 Amazon CloudFront distributionsAssociated AWS resources → Add AWS resources → 勾选入口①与入口②两个 Distribution → Next
  3. Add rules → Add managed rule groups → AWS managed rule groups → 依次勾选上表 #1–#5 五个规则组(AWS 托管组免费)→ Add rules
  4. Core rule setEdit → 找到 SizeRestrictions_BODY → Rule action 选 Override to Count → Save。若采用灰度策略,同时对 Core rule set 与 Linux operating system 勾选 Set all rule actions to count
  • rate-limit-all:Type 选 Rate-based rule,Rate limit 2000,Evaluation window 5 minutes,Request aggregation 选 Source IP address,Action Block
  • rate-limit-login:Rate limit 100,勾选 Only consider requests that match the criteria in a rule statement,Inspect 选 URI path,Match type 选 Contains string,String 填 /auth,Action Block

6.3 CloudFront 源站保护

本方案采用 VPC Origins 架构,源站保护为结构性内置:源站无公网 IP、回源走 VPC 内私网 ENI、安全组仅放行 CloudFront 服务安全组、JumpServer DOMAINS 叠加 Host 校验。生产前在控制台核查三项:

  1. 确认源站无公网 IP:EC2 控制台 → 实例 → 分别选中 JumpServer 与 frps 实例 → 详情页 公有 IPv4 地址 应为空("–")
  2. 找到 CloudFront 服务安全组:EC2 控制台 → 安全组 → 搜索 CloudFront-VPCOrigins-Service-SG(创建 VPC Origin 时自动生成),记下其安全组 ID
  3. 核查源站安全组入站规则:打开 JumpServer 安全组与 frps 安全组的 入站规则 页签——JumpServer 的 80 端口与 frps 的 7000 端口的来源应仅为上一步的服务安全组;任何端口都不应出现 0.0.0.0/0。发现多余规则(如调试遗留)点 编辑入站规则 逐条删除

可选进阶(仅在客户合规清单明确要求"源站校验请求来源"时实施):CloudFront 控制台 → Distribution → Origins → Edit → Add custom header,注入 X-Origin-Verify 随机串并在源站 Nginx 校验。注意 JumpServer 的 Nginx 在容器内,升级重建会丢失该改动,需固化到部署脚本;VPC Origins 架构下该措施边际收益有限。

6.4 日志启用

第一步:准备 S3 日志桶

  1. S3 控制台 → Create bucket:名称 jumpserver-cf-logs-<账号ID>(桶名全局唯一,附账号 ID 避免冲突),区域选东京 ap-northeast-1,Block all public access 保持全部勾选 → Create
  2. 进入该桶 → Management 页签 → Create lifecycle rule:规则名 expire-90d,Apply to all objects,动作勾选 Expire current versions of objects,天数填 90 → Create rule

第二步:开启 CloudFront 访问日志(两个 Distribution 各一份)

  1. CloudFront 控制台 → Distributions → 选中入口① → Logging 页签 → Standard log destinationsAdd
  2. Destination 选 Amazon S3,Bucket 选上面创建的日志桶,Prefix 填 cf-web/;Output format 选 Parquet(便于 Athena 直查,选 Plain text + Gzip 亦可),Partitioning 勾选按日期 → Add(所需桶策略由控制台自动配置)
  3. 对入口②重复上两步,Prefix 填 cf-device/

第三步:开启 WAF 日志

  1. CloudWatch 控制台(us-east-1)→ Log groups → Create log group:名称 aws-waf-logs-jumpserver(必须以 aws-waf-logs- 开头),Retention 选 90 天
  2. WAF 控制台 → Web ACLs → jumpserver-edge-wafLogging and metrics 页签 → Enable logging → Destination 选 CloudWatch Logs log group → 选上一步的日志组 → Save
日志 回答什么问题 保留
CloudFront 访问日志 谁、何时、从哪个 IP 访问了入口;设备何时建立过隧道(一条 wss 隧道只产生一条握手记录) S3 90 天
WAF 日志 哪些请求被拦截 / 计数、命中哪条规则;也是灰度观察期的判定依据 CloudWatch 90 天
frps 日志(已有) 设备隧道级:哪台设备何时上下线、真实源 IP 本机 30 天
JumpServer 审计(已有) 人员操作级:谁登录哪台资产、执行什么命令、会话录像 平台内,定期归档

6.5 验收清单

# 验收项 预期
1 浏览器访问 https://<入口①域名>/?q=<script>alert(1)</script> 403 拦截页(WAF 生效)
2 正常登录 JumpServer + Web 终端连资产 + 文件上传 全部正常,无误拦
3 设备 frpc 重启后重建隧道(必测) frps 上对应端口重新出现映射、frpc 日志 login success。若 frpc 报 bad status:WAF 控制台 → 该 Web ACL → Sampled requests 查 /~!frp 是否被 NoUserAgent_HEADER 拦截 → 核对 #0 放行规则的域名与 URI 是否填写正确、优先级是否在最顶部
4 日志桶 1 小时内出现日志对象;WAF 日志组出现记录 非空
5 源站核查三项(6.3) 无公网 IP;入站仅服务安全组;无 0.0.0.0/0

6.6 新增成本(东京区,按需)

项目 计费 月成本(约)
WAF Web ACL $5/个(两入口共享 1 个) $5
WAF 规则 $1/条 × 8(5 个托管组 + 1 条放行 + 2 条速率) $8
WAF 请求处理 $0.60/百万请求,运维流量极小 < $1
S3 访问日志 + CloudWatch WAF 日志 存储 + 摄入,90 天生命周期 < $1
合计 ≈ $13 – 15 / 月

7. 安全模型说明(供客户安全团队评审)

本方案唯一的公网暴露面是两个 CloudFront 入口的 443 端口(AWS 托管域名)。车载设备使用普通互联网 SIM(无专用 APN),源 IP 随基站漫游且位于 CGNAT 之后,无法用源 IP 白名单收敛入口——安全边界依设计下沉到认证层。

7.1 入口防护分层

防护层 状态 说明
入口防护 ✅ AWS 托管 入口整体由 CloudFront 托管,账号内无自管公网安全组;各实例安全组均最小权限
DDoS 防护 ✅ 自带 AWS Shield Standard 免费内置,抵御 L3/L4 洪水;CloudFront 弹性承载海量并发
L7 防护 ✅ 阶段四 WAF:IP 信誉/已知漏洞/SQLi/OWASP/速率限制(见 第 6 章)
传输加密与认证 ✅ 强制 设备→CloudFront 全程 wss(TLS) + 64 位随机 token,未认证连接毫秒级断开

7.2 "端口对全网开放" ≠ "服务对全网开放"

攻击者扫到 443 后能做的全部事情:建 TCP 连接 → 被要求 TLS + token 校验 → 失败立即断开。拿不到 banner、版本号或服务指纹;64 位随机 token 暴力破解计算上不可行。该模型与 WireGuard / OpenVPN / AWS Client VPN 端点完全同级——"端口可达、边界在认证层"是移动设备远程接入的行业标准做法。

7.3 残余风险与缓解

风险 概率 缓解
frp 软件 CVE 低(历史有先例) 订阅 frp Release 及时升级;frps 与 JumpServer 是两台机器,即使 frps 被攻破,到 JumpServer 仍隔着安全组。注意:frps 与 WorkNode #1 同机,frps 失陷即该节点失陷——对该节点做最小化部署(不放敏感数据),或后续拆分为独立小实例
TLS 握手洪水耗 frps CPU WAF 速率规则先拦一层;CloudWatch CPU/连接数告警;frps 日志留设备真实源 IP 可 NACL 封禁
token 泄露(车机物理拆解) 中——最现实的泄露途径 季度轮换;maxPortsPerClient=4 限占用;泄露即换 token 全量下发。即使泄露,攻击者也只能把自己设备映射进端口池,无法访问 JumpServer 或其他设备

8. 运维手册

8.1 故障排查速查表

现象 排查方向
CloudFront 502/504(入口①) 安全组未放行 80←服务SG;或 JumpServer 容器没起(./jmsctl.sh status
登录页正常但登录报 CSRF DOMAINS 没配或没 restart(3.3 节)
登录正常但所有资产 Web 终端连不上 ① 首查 DOMAINS 是否含浏览器正在用的域名、改后是否 restart——全资产失败几乎必是它(旁证:宿主机 ssh -p 2222 admin@127.0.0.1 走 koko 直连口能通则坐实 Web 链路问题)② 确认缓存策略 CachingDisabled、AllowedMethods 全开
frpc 连不上(login failed / timeout) ① 设备端 curl -v telnet://<接入域名>:443 测出站,或直接跑 test-frp-link.sh <接入域名> 一次判定链路 ② token 是否一致 ③ transport.protocol="wss" 是否配置 ④ 浏览器访问接入域名返回 502 属正常(说明已回源到 frps);超时则回源链路有问题
入口②回源不通(访问超时) ① frps 进程/7000 监听 ② frps-sg 放行 7000←CloudFront 服务SG ③ VPC Origin 端口是否 7000
JumpServer 资产测试不可达 ① frps 上 ss -lntp | grep 600x(没有=frpc 离线)② frps-sg 放行 6001-6299←JMS SG ③ 端口对照分配表
换自定义域名后,登录正常但 Web 终端"拒绝了我们的连接请求"(iframe 指向旧域名) 终端管理 → 端点管理里的 Endpoint.host 还是旧域名——改为新域名即时生效(见 3.3.1,换域名三件套)
测试可连接性报 ansible-executor 镜像缺失 docker pull jumpserver/ansible-executor:latest 或关闭 Ansible Docker 隔离(5.2 节提示框)
能连上但立即断开 frpc localIP 与设备 sshd 监听地址是否一致(通常 127.0.0.1)
车机频繁掉线重连 正常(基站切换);确认 heartbeat 已配、systemd Restart=always
端口冲突(proxy already exists) 两台设备配了相同 remotePort,对照分配表修正
Web 终端闲置断开 确认设备端 sshd 保活已配(4.4);长任务用 tmux
VPC Origin 创建失败 VPC 没挂 IGW 或 BPA 未豁免(2.1 节前置条件 1/3)
挂 WAF 后全部设备掉线,frpc 报 bad status,frps 日志零记录 WAF 的 NoUserAgent_HEADER 在边缘拦掉了 frpc 握手(frpc 无 UA 头),请求不回源。查 WAF 抽样日志确认 /~!frp 被 BLOCK → 补 6.2 表 #0 放行规则(实际发生过的故障:WAF 上线后设备例行重连时集体掉线)
WAF 误拦正常操作 WAF 日志过滤该请求命中的规则 → 控制台把对应子规则 Override to Count → 复现确认后评估长期豁免

8.2 日常运维操作

操作 方法
新增 WorkNode 该机 SG 放行 22←JMS SG(3.4)→ 资产列表添加 → 授权
新增现场设备 端口分配表登记 → 设备跑 setup-frp-client.sh(4.3)→ 资产列表添加 ssh/600x(5.2)
更换入口域名 三件套:config.txt DOMAINS(restart)+ 基本设置站点URL + 端点管理 Endpoint.host(见 3.3.1);旧域名建议在 DOMAINS 保留作应急入口
token 季度轮换 frps 执行 sudo bash setup-frp-server.sh <新token> → 设备端灰度下发重启 frpc;过渡期可用 auth.additionalScopes 双 token
WAF Count→Block 切换 观察期后在 Web ACL 中把 Core/Linux 规则组的 Count 覆盖取消
设备在线监控 资产连通性测试即在线状态;可配定时任务批量测试 + 告警

9. 成本估算(东京区域,按需价格)

资源 计费说明 月成本(约)
frps 复用 WorkNode #1 无新增实例 $0(独立部署则 t3.small ≈ $21)
CloudFront 入口①+② 请求数 + 流出流量,SSH 运维流量极小 $2 – 8
WAF(阶段四) Web ACL $5 + 规则 $1×8 + 请求 $0.60/百万 $13 – 14
日志(阶段四) S3/CloudWatch 存储,90 天生命周期 < $1
NAT 流量增量 frps 出站极少(仅下载安装包) ≈ $0(复用现有 NAT)
合计 ≈ $15 – 22 / 月(frps 独立部署则 +$21)

精确报价以 AWS Pricing Calculator 为准;若后续隧道走大流量(如日志回传)需重估流量费。

10. 回滚与清理

按依赖顺序执行(先删引用者)。清理是纯减量操作,不影响 JumpServer 本体与既有环境:

# 0. WAF(如已部署阶段四):先从两个 Distribution 的 WebACLId 置空,再删 Web ACL
aws wafv2 delete-web-acl --scope CLOUDFRONT --region us-east-1 --name jumpserver-edge-waf --id <acl-id> --lock-token <token>
# 1. 禁用并删除 Distribution(先 disable,等 Deployed 后 delete)
aws cloudfront get-distribution-config --id <dist-id>   # 取 ETag,改 Enabled=false 后 update-distribution
aws cloudfront delete-distribution --id <dist-id> --if-match <etag>
# 2. 删除 VPC Origin
aws cloudfront get-vpc-origin --id <vo-id> --query 'ETag' --output text
aws cloudfront delete-vpc-origin --id <vo-id> --if-match <etag>
# 3. 移除安全组放行(80/7000 ← CloudFront服务SG、6001-6299 ← JMS SG)
aws ec2 revoke-security-group-ingress --group-id <SG> --protocol tcp --port <端口> --source-group <来源SG>
# 4. JumpServer 中删除对应资产/节点;设备端停用 frpc(sudo systemctl disable --now frpc)

第二部分:远程监控运维

边缘设备指标直写 Amazon Managed Prometheus —— node_exporter + Prometheus agent + IAM Roles Anywhere,设备零长期凭证

与第一部分的关系:第一部分解决"人访问设备"(交互式运维通道),本部分解决"设备上报指标"(监控数据通道)。两条链路方向相反、互相独立,可只部署其一;共同点是同一套设计哲学——设备无固定公网 IP 也能接入、设备端零长期 AK/SK、公网面由 AWS 托管服务承担。

11. 监控架构与认证设计

11.1 架构总览

flowchart LR
  subgraph EDGE["边缘设备(ARM / x86,Linux)"]
    NE["node_exporter :9100<br/>仅绑定 127.0.0.1"]
    PA["Prometheus agent<br/>WAL only · 30s 抓取"]
    HLP["aws_signing_helper<br/>credential_process"]
    CRT["X.509 设备证书+私钥<br/>(设备上唯一的秘密)"]
  end
  subgraph CLOUD["AWS"]
    RA["IAM Roles Anywhere<br/>Trust Anchor + Profile"]
    ROLE["IAM Role<br/>仅 aps:RemoteWrite 限单 workspace"]
    AMP["Amazon Managed Prometheus<br/>工作区"]
    GRAF["Amazon Managed Grafana<br/>统一看板"]
  end
  PA -- "scrape :9100" --> NE
  CRT -.-> HLP
  HLP -- "① 证书换1小时临时凭证" --> RA --> ROLE
  PA -- "② remote_write + SigV4" --> AMP
  HLP -. "凭证到期自动重取" .-> PA
  AMP --> GRAF
  style CRT fill:#fef3c7,stroke:#d97706
  style RA fill:#fee2e2,stroke:#dc2626
  style AMP fill:#f3e8ff,stroke:#7c3aed
  style GRAF fill:#f3e8ff,stroke:#7c3aed
Loading
组件 位置 角色
node_exporter 设备 采集本机指标,仅暴露给本机(127.0.0.1:9100),不出网
Prometheus(agent 模式) 设备 只留 WAL 无本地 TSDB,抓取后经 remote_write + SigV4 上报
aws_signing_helper 设备 用设备证书向 Roles Anywhere 换 1 小时临时凭证,SDK 到期自动重取
IAM Roles Anywhere 云端 Trust Anchor 信任自建 CA;信任策略含 SourceArn 条件防 confused deputy
IAM Role 云端 权限收紧为 aps:RemoteWrite 且限定单个 workspace ARN
AMP + Grafana 云端 托管 Prometheus 存储查询与统一可视化

11.2 认证选型:为什么用 Roles Anywhere

方案 额外成本 判断
IAM User + AK/SK $0 ❌ 长期凭证落盘设备、泄露即永久,反模式(多数企业已禁止创建 IAM User)
IAM Roles Anywhere $0(服务免费) ✅ 首选:设备只持 X.509 证书,临时凭证自动轮换、单设备可吊销——AWS 官方推荐的非云端工作负载凭证方案
IoT Core credentials provider ≈$0 机制等价,但需注册 IoT Thing、走 IoT 域名,仅客户已在用 IoT Core 时考虑
中心 sigv4 代理 +1 台 EC2 设备零凭证但需自管认证层且引入单点,不如托管方案

⚠️ CA 选型:PoC / 中小车队用自建 openssl CA($0,本方案脚本内置,CA 私钥必须离线备份);客户已有企业 PKI 则直接复用;AWS Private CA 通用模式($400/月)仅在需要托管与审计时选用。不要选 Private CA 短周期模式($50/月但证书 ≤7 天)——车辆离线超 7 天后证书过期即无法自行续期(续期本身需要有效凭证),形成死锁只能人工重灌。

11.3 自建 CA 的安全性与证书有效期

密码学上,自建 CA 与托管 CA 签出的证书没有强度差别(本方案:RSA-4096 根证书 + SHA-256 签名 + RSA-2048 设备证书,与商业 CA 同级)。更关键的是该 CA 的爆炸半径被架构刻意收窄:它是纯私有信任,仅被本方案的 Trust Anchor 认可、不进任何公共信任链;其背后的 IAM Role 仅有单 workspace 的 aps:RemoteWrite。极端情况下 CA 私钥泄露,攻击者能做的全部事情是伪造设备证书向 AMP 写入虚假指标(数据污染)——无法访问其他任何 AWS 资源、无法读取任何数据。

与托管 CA 的真实差距在运营三项:

维度 自建 openssl CA AWS Private CA($400/月对应的价值)
CA 私钥保管 文件形式(脚本已锁目录 700/私钥 600);须立即离线备份——CloudShell 家目录约 120 天不活动会被清理 FIPS 140-2 Level 3 HSM,私钥不可导出
签发审计 无内建记录,靠维护设备-证书清单 全量 CloudTrail
吊销 无现成 CRL 设施;小规模实用做法是换 CA 全量换证,或 openssl 生成 CRL 手动导入 Roles Anywhere 托管 CRL / OCSP

证书有效期(脚本默认值,可调):

证书 有效期 到期提醒 到期处理
CA 根证书 10 年(行业惯例 10-20 年) Trust Anchor 提前 90 天通知 换根:新 CA → 新 Trust Anchor → 设备分批换证,可提前平滑过渡
设备证书 默认 1 年(device 子命令可指定天数) Trust Anchor 提前 30 天通知 重跑 device 子命令签新证 → 替换设备 crt/key → 重启服务

设备证书取 1 年是泄露止损与轮换负担的平衡点:车机被物理拆解是最现实的证书泄露途径,1 年有效期给出自然的止损上限。规模结论:PoC 与几十台规模自建 CA 够用且是业界常见做法(CA 私钥须立即离线备份);上百台或有合规审计要求时优先复用客户企业 PKI,无企业 PKI 再考虑 AWS Private CA。

12. 云端部署

12.1 前置:手动创建两个托管服务(控制台各一次)

自动化脚本不代建这两个 workspace——它们是账号级长期资源,创建涉及区域与认证方式选型,应由管理员在控制台显式完成:

服务 控制台操作 要记下的信息
Amazon Managed Service for Prometheus(AMP) 控制台 → Amazon Managed Service for Prometheus → 创建工作区(选定区域后一路默认即可) workspace ID(ws-xxxx,init 步骤的 -w 参数)与所在 region
Amazon Managed Grafana 控制台 → Amazon Managed Grafana → 创建工作区:身份验证选 IAM Identity Center,权限选 Service managed 并勾选 Prometheus(详见 13.3) workspace URL(Grafana 登录地址)

AMP 创建后即可开始下面两步;Grafana 不阻塞采集链路,可在设备数据入库后再建(配置细节与看板导入见 13.3)。其余全部云端操作由 amp-edge-monitoring/setup-amp-edge.shCloudShell 完成——在 AMP workspace 所在区域打开 CloudShell 并上传脚本。

12.2 第一步 init —— 每账号一次,幂等可重跑(在 AWS CloudShell 中运行)

# AWS 控制台右上角 CloudShell 图标打开(确认区域 = AMP workspace 所在区域),上传脚本后执行:
bash setup-amp-edge.sh init -w <workspace-id> -r <region>

自动完成:身份确认(防跑错账号)→ 校验 workspace ACTIVE → 创建收紧版 IAM 策略(仅该 workspace 的 RemoteWrite)→ IAM Role → 自建 CA(RSA-4096 / 10 年 / 目录锁 700)→ Trust Anchor(含证书到期通知)→ 回填信任策略 SourceArn 条件 → Profile(会话 1 小时)→ 三串 ARN 落盘 ~/.amp-edge/amp-edge.env

12.3 第二步 device —— 每设备一次(在 AWS CloudShell 中运行)

# 同一 CloudShell 会话中执行(init 产物已在 ~/.amp-edge/ 持久保存,隔天重开 CloudShell 也可直接跑):
bash setup-amp-edge.sh device <设备名> [有效期天数,默认365]

签发设备证书(CN=设备名,clientAuth + CA:FALSE),并动态生成设备端安装脚本——设备专属参数(三串 ARN、remote_write URL、region、设备名)全部烤进脚本内,打包为 ~/.amp-edge/devices/device-<设备名>.tar.gz。CloudShell → Actions → Download file 下载。

💡 母子脚本关系setup-amp-edge.sh 只在 CloudShell 跑(需 AWS 权限);包内的 install-edge-collector.sh 只在设备上跑(零参数、零 AWS 凭证需求)。设备包含私钥,传输到设备后请删除中转副本。

13. 设备端接入(一键安装)

scp device-<设备名>.tar.gz user@device:/tmp/
# 设备上:
cd /tmp && tar xzf device-<设备名>.tar.gz && cd <设备名>
sudo bash install-edge-collector.sh
# 设备已有 Prometheus 且用户/服务名非默认时:
sudo PROM_USER=<用户> PROM_SERVICE=<服务名> bash install-edge-collector.sh

安装脚本按设备现状自动走两条路径之一:

设备状态 脚本行为
裸机 全自动到跑通:装 node_exporter(绑 127.0.0.1)与 Prometheus LTS(--agent 模式)→ 自动生成完整配置(scrape + sigv4 + 唯一 instance 标签)→ promtool 校验 → 启动。零人工合并环节
已有组件 跳过安装、完全不动现有配置;只装凭证链,最后打印待合入 prometheus.yml 的 sigv4 片段(profile: amp-edge 行必须保留)

两条路径共同完成:安装 aws_signing_helper(按架构、sha256 校验、自检)→ 证书私钥落盘(600/750/属主为采集用户)→ 写 AWS profile(credential_process)→ systemd drop-in 注入环境变量 → 扫描 AWS_* 环境残留并警告 → 以采集用户身份实换一次凭证自检。

13.1 prometheus.yml 参考示例(合并后的最终形态)

裸机路径自动生成的配置即此形态;已有组件路径手动合并后应与之等价。<设备名> / <region> / <ws-id> 以设备包打印的真实值为准:

global:
  scrape_interval: 30s
  external_labels:          # 可选:车队业务分组维度(附加到该设备的所有上报序列)
    env: prod               # 环境 / 用途
    site: tokyo-lab         # 站点 / 车队
    # ⚠️ 不要在这里写 instance——external_labels 无法覆盖抓取自动生成的
    #    instance 标签,必须用下方 relabel_configs(见第 14 章排查实案)

scrape_configs:
  - job_name: node
    static_configs:
      - targets: ['127.0.0.1:9100']
    relabel_configs:
      - target_label: instance
        replacement: <设备名>      # ← 每台设备唯一,与证书 CN 一致
                                    #   这就是 Grafana 里的设备选择器

remote_write:
  - url: https://aps-workspaces.<region>.amazonaws.com/workspaces/<ws-id>/api/v1/remote_write
    sigv4:
      region: <region>
      profile: amp-edge     # ← 必须保留!第一道凭证保障;缺失则 SDK 走
                            #   环境变量凭证链,易被设备残留带偏 → 403
    queue_config:           # 可选:边缘弱网 / 低配设备调优
      capacity: 2500              # 本地缓冲队列深度(断网期间暂存)
      max_shards: 2               # 并发上传分片上限(控内存与带宽)
      max_samples_per_send: 1000  # 单次发送样本数

⚠️ 标签分工instance(relabel 注入)= 设备唯一标识,全生态看板通用;external_labels 只放分组维度(env / site / fleet),不要放设备标识——两处职责不要混。

13.2 验证闭环

# 设备上
journalctl -u prometheus -f            # 不应出现 401/403
# CloudShell 上确认落库
pip3 install --user awscurl -q
awscurl --service aps --region <region> \
  "https://aps-workspaces.<region>.amazonaws.com/workspaces/<ws-id>/api/v1/query?query=up"
# 返回 value [..,"1"] 即数据入库;Grafana 加 AMP 数据源即可出看板
# 部署后 1 小时复查 journal:确认跨过首个凭证刷新周期无 401/403

13.3 Grafana 数据源与首块看板(实测路径)

前置:创建 Amazon Managed Grafana workspace 时,身份验证选 IAM Identity Center(其用户并非 IAM User,与企业禁令不冲突——人用 Identity Center、机器用 Roles Anywhere,全账号零长期凭证);权限选 Service managed 并勾选 Prometheus。创建后到 workspace 详情页 → Authentication → Assign new user or group 分配用户并给 Admin 角色。

  1. 加数据源(推荐):Grafana 菜单 → Apps → AWS Data Sources → Amazon Managed Service for Prometheus → 选 region → 自动发现 workspace → Add data source(自动配好 SigV4 与 endpoint)
  2. 手动备选:Add data source → Prometheus → URL 填 workspace 查询地址(不带 /api/v1 尾巴)→ 打开 SigV4 auth 开关 → Provider 选 Workspace IAM Role + region → Save & test
  3. 验证:Explore 查询 up 应返回 1
  4. 首块看板:Dashboards → Import → 输入社区看板 ID 1860(Node Exporter Full)→ 数据源选 AMP → Import。CPU/内存/磁盘/网络/温度全套即出,instance 下拉即车队设备选择器

⚠️ Save & test 报 403:建 workspace 时未勾 Prometheus 数据源权限——到 Managed Grafana 控制台该 workspace 的 IAM permission settings 补勾后重试。

14. 监控链路故障排查

现象 排查方向
remote_write 403(但安装时凭证自检通过) 凭证链被环境残留带偏:查 /etc/default/服务名 里的 AWS_PROFILE 与 AWS_SHARED_CREDENTIALS_FILE 残留——systemd 的 EnvironmentFile 会覆盖 drop-in 的 Environment。修法:yml 的 sigv4 里写死 profile: amp-edge(第一道保障)+ 清理残留(实案:设备残留旧静态 key 指向致 403)
SigV4 签名被拒 / 时间相关报错 设备时钟偏差过大:timedatectl 确认 NTP 同步(node_timex_sync_status 应为 1)
证书过期设备批量掉线 查 Trust Anchor 到期通知;用 device 子命令重签并下发新包;建议按到期日历提前 30 天轮换
helper 无法运行 Amzn2023 构建与极老 glibc 不兼容时从 GitHub 源码编译(Debian 13 实测可直接运行)
Grafana 查不到新设备 / 设备都叫 127.0.0.1:9100 instance 标签必须用抓取 job 的 relabel_configs(target_label: instance + replacement: 设备名)设置——external_labels 无法覆盖抓取自动生成的 instance 标签

15. 成本与日常运维

项目 计费 说明
IAM Roles Anywhere $0 服务本身零收费(无月费、无会话费)
设备证书 自建 CA / 企业 PKI:$0 openssl 签发不产生 AWS 费用。仅当改用 AWS Private CA 签发才有证书费:通用模式 $400/月 + $0.75/张(阶梯递减,1 万张后 $0.001);短周期模式 $50/月 + $0.058/张(证书 ≤7 天,本方案已排除)
AMP 按摄入样本 + 存储 + 查询计费 主要成本项,正比于时间序列数量——务必用 node_exporter 白名单控系列数;精确估算用 AWS Pricing Calculator
Amazon Managed Grafana 按活跃用户/月 编辑者与查看者价格不同;小团队也可自建 Grafana 连 AMP
日常操作 方法
新增设备 CloudShell 跑 device 子命令 → 包传设备 → 一键安装(裸机全自动)
证书轮换(年度) 重跑 device 子命令签新证 → 替换设备上的 crt/key → 重启服务;Trust Anchor 到期通知提前 30 天提醒
吊销单台设备 Roles Anywhere 控制台导入 CRL,或直接更换 CA 全量换证(小车队更省事)
控制系列数与费用 node_exporter 用 --collector.systemd.unit-include 白名单;关闭不需要的 collector
监控"监控本身" up 序列即设备在线状态;Grafana 告警 up==0 超过阈值

Edge-Cloud Bastion 边缘设备统一接入平台 | 第一部分:JumpServer v4.x + frp v0.71.x + CloudFront VPC Origins | 第二部分:node_exporter + IAM Roles Anywhere + AMP + Grafana | 全流程经真实环境验证 第一部分脚本位于 jumpserver/frp/ 子目录 | 第二部分脚本位于 amp-edge-monitoring/ 子目录

单页离线版:edge-cloud-bastion.html(内容与本 README 等价,可直接发给客户离线阅读)

About

基于 AWS 的边缘设备统一接入平台,两大能力:远程开发运维(JumpServer 堡垒机隐藏于 CloudFront 之后,车载/办公室边缘设备经 frp 隧道反向接入,一个 Web 控制台纳管全部资产)+ 远程监控运维(node_exporter 指标经 IAM Roles Anywhere + SigV4 直写 Amazon Managed Prometheus,Grafana 统一看板)。设备端零长期凭证,全流程经真实环境验证。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages