时间:入职第 4 天天气:多云转晴状态:如履薄冰,但逐渐掌控局面
前三天,我费了九牛二虎之力才把这个以太坊全节点拉起来,解决了同步死循环和时间漂移的问题。 我本以为今天可以摸个鱼,研究一下公司的代码库。 结果刚上班,安全组(SecOps)的一封邮件就把我炸醒了。
🚨 1. 上午 09:15:一份“刺眼”的漏洞扫描报告
邮件标题是鲜红的:《高危:基础设施组件未授权访问风险通知》。 附件里的 PDF 直接点名了我那台刚上线的节点服务器:
目标 IP:172.31.20.100 (Eth-Mainnet-Node)漏洞描述:TCP 8545 端口监听在
0.0.0.0,且未配置鉴权。风险等级:High整改建议:立即收敛监听地址,或增加访问控制层。
组长路过我工位,敲了敲桌子:“Alen,虽然这是在 VPC 内网,但按照金融合规要求,内网也是‘不可信’的。万一内网某台测试机中了毒,黑客扫到你的 8545 端口,发一个恶意的eth_getLogs请求,你的节点瞬间就会 CPU 100% 宕机。今天下班前,给它穿上‘防弹衣’,另外把监控告警配好,我不希望下次是用户告诉我节点挂了。”
我看着那行0.0.0.0,确实是大意了。在 Web3,JSON-RPC 接口就是金库的大门,我居然让它敞开着。
🛠️ 2. 上午 10:30:第一道防线 —— 部署 Nginx 反向代理
Scanner 服务确实需要访问我的节点,但我不能让所有内网机器都能访问。 Geth 自带的 HTTP 配置功能太弱,不支持 IP 白名单,也不支持速率限制(Rate Limit)。解决方案:在节点前面挡一层Nginx。
步骤一:修改 Geth 启动脚本首先,我必须把 Geth 那个“危险”的耳朵收起来,只让它听本机的话。
# 修改 /usr/local/bin/start_geth.sh # ... (前略) --http \ # [Day 4 修改] 从 0.0.0.0 改为 127.0.0.1 # 强制外部流量必须走 Nginx,绕过 Nginx 谁也别想连 --http.addr 127.0.0.1 \ --http.port 8545 \ --http.api "eth,net,web3,txpool" \ --http.vhosts "*" \ # ... (后略)步骤二:配置 Nginx 访问控制安装完 Nginx 后,我写了一个专门的配置文件。这里不仅做了IP 白名单,还加了Rate Limit(速率限制),防止 Scanner 业务代码死循环把节点打挂。
# /etc/nginx/sites-available/ethereum-rpc.conf # 定义限流区域:每个 IP 每秒最多 1000 个请求,突发不超过 500 limit_req_zone $binary_remote_addr zone=rpc_limit:10m rate=1000r/s; upstream geth_backend { server 127.0.0.1:8545; } server { listen 8080; # 换个端口,增加一点隐蔽性 access_log /var/log/nginx/rpc_access.log; # 开启审计日志 # --- 核心安全规则 --- # 仅允许 Scanner 业务组的 CIDR (网段) allow 172.31.50.0/24; # 允许监控服务器 allow 172.31.90.10; # 拒绝其他所有内网 IP deny all; location / { proxy_pass http://geth_backend; # 启用限流 limit_req zone=rpc_limit burst=500 nodelay; # 传递真实 IP 给后端 (虽然 Geth 不看,但为了扩展性) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }重启 Nginx 和 Geth 后,我自己用内网另一台机器测试了一把curl。 结果:403 Forbidden。爽。这就叫安全感。
📊 3. 下午 2:00:让黑盒变透明 —— Prometheus 监控
加固完接口,接下来是“看见”。 如果不知道当前 Peer 有多少,不知道区块高度差多少,那我就像在开一辆没有仪表盘的赛车。
步骤一:开启 Geth Metrics我又改了一次启动脚本,加入了 metrics 参数。
# 修改 /usr/local/bin/start_geth.sh # ... --metrics \ --metrics.addr 127.0.0.1 \ --metrics.port 6060 \ # ...(注:同样需要在 Nginx 里配置一下 6060 的转发,或者只允许监控机访问)
步骤二:配置 Prometheus 抓取 Job登录到公司的监控服务器,修改prometheus.yml:
scrape_configs: - job_name: 'eth_mainnet_node_01' scrape_interval: 15s metrics_path: /debug/metrics/prometheus static_configs: - targets: ['172.31.20.100:6060'] # 这里的端口走的是 Nginx 转发或者直连 labels: env: 'prod' role: 'full_node'配置生效后,我在 Grafana 里导入了一个 Dashboard。看着上面跳动的Block Height和Peer Count (50),我终于觉得自己是在“运维”它,而不是在“供着”它了。
🔔 4. 下午 4:30:告警闭环 —— 飞书 (Lark) 通知
监控不是给人看的,是给机器看的。我不能 24 小时盯着大屏。 我需要配置一套规则:只要节点有问题,手机必须立刻响。
步骤一:编写告警规则 (Alert Rules)我定义了三条“生死线”:
# rules/ethereum_alerts.yml groups: - name: EthereumNodeAlerts rules: # 1. 宕机告警:连不上端口或者进程挂了 - alert: GethInstanceDown expr: up{job="eth_mainnet_node_01"} == 0 for: 1m labels: severity: critical annotations: summary: "🚨 紧急:以太坊节点宕机" # 2. 同步落后告警:这是业务最关心的 # 逻辑:全网最高高度 - 本地高度 > 20 (约 4 分钟) - alert: BlockHeightLagging expr: (chain_head_block - ethereum_blockchain_height) > 20 for: 2m labels: severity: warning annotations: summary: "⚠️ 警告:节点同步滞后" description: "节点落后主网超过 20 个块,Scanner 可能漏单!" # 3. 没朋友告警:Peer 数量太少 - alert: LowPeerCount expr: p2p_peer_count < 8 for: 5m labels: severity: warning annotations: summary: "⚠️ 警告:P2P 连接数过低"步骤二:配置 Alertmanager 发送给飞书修改alertmanager.yml,把 Webhook 指向公司的飞书机器人:
receivers: - name: 'lark-webhook' webhook_configs: - url: 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx-xxxx-xxxx' send_resolved: true # 恢复正常也发一条通知测试:我手动停止了 Geth 进程。 1 分钟后,手机“叮”的一声。飞书弹窗:[🚨 紧急:以太坊节点宕机]。 我又启动进程。 2 分钟后,手机又是“叮”的一声:[✅ 恢复:以太坊节点宕机]。
✅ 5. 下午 6:00:日结
下班前,我把 Nginx 的访问日志截图和 Grafana 的监控链接发给了安全组和 Scanner 组。 安全组回复:“Pass”。 组长回复:“干得不错。这才是生产级环境该有的样子。”
今日感悟:在 AWS 上起个 EC2 很容易,跑个 Geth 也很容易。 但要给它穿上Nginx 的防弹衣,装上Prometheus 的眼睛,配上Alertmanager 的嘴巴,这才是初级运维和高级运维的区别。
现在,这台节点已经是一座防御森严的堡垒了。 我可以安心下班了……大概吧?
📚 附录:Alen 的 Web3 运维错题本 (Day 4)
📖 第一部分:核心概念解析 (Glossary)
1. JSON-RPC (JSON Remote Procedure Call)
定义:区块链节点与外部世界(如 Scanner、钱包、DApp)交互的标准协议。
Alen 的理解:它就像是 Web2 的 REST API,但它是无状态的。
风险点:REST API 通常有鉴权(Token/Cookie),但以太坊原生的 JSON-RPC 设计之初是完全开放、无鉴权的。谁连上了端口,谁就能发号施令。这就是为什么要挂 Nginx 的根本原因。
2. Lateral Movement (横向移动)
定义:网络安全术语。指黑客攻陷了一台内网低权限服务器(如测试机)后,以此为跳板,扫描并攻击内网其他高价值服务器(如我的节点)。
防御:VPC 和安全组能防外网,但防不了内网。Nginx 的 IP 白名单就是为了切断这种横向攻击路径。
3. Expensive Query (昂贵查询 / 毒药请求)
定义:消耗极高计算资源的 RPC 请求。
例子:
eth_getLogs(查询大范围的历史日志)或debug_traceTransaction(重放交易)。危害:一个恶意的
eth_getLogs请求,可能需要节点扫描几百 GB 的硬盘数据。如果没有Rate Limit (速率限制),几个并发请求就能让 64核 CPU 瞬间瘫痪。
❓ 第二部分:关键技术问答 (Q&A)
Q1: 为什么 AWS 安全组 (Security Group) 不够用,必须上 Nginx?
AWS 安全组 (Layer 4):只管“门禁”。它只能判断来源 IP 是不是允许的,不管你带了什么东西进去。
Nginx (Layer 7):它是“安检员”。
限流:它能限制你每秒只能说 1000 句话(Rate Limit)。
日志:它能把你说的每句话都录音(Access Log)。
协议清洗:未来甚至可以配置只允许
eth_blockNumber,禁止debug_*等危险指令。
Q2: 监控里为什么要区分chain_head_block和local_block_height?
chain_head_block(全网高度):这是 Geth 通过 P2P 网络听到的“别人家的高度”。local_block_height(本地高度):这是我自己硬盘里实际存好的高度。Lag (滞后)= 全网 - 本地。
如果 Lag > 0 但很小(1-2),是正常的网络延迟。
如果 Lag > 10,说明同步出问题了(IO 瓶颈或网络断连)。
告警公式:
lag > 20持续2分钟->报警。
Q3: 为什么0.0.0.0是万恶之源?
现象:
--http.addr 0.0.0.0表示监听服务器上所有的网卡(公网 IP、内网 IP、Loopback)。后果:如果你忘了配置 AWS 安全组,或者安全组配置失误开放了
0.0.0.0/0,你的节点瞬间就会暴露在公网,成为全球黑客的肉鸡。规范:永远绑定
127.0.0.1,强迫流量走 Nginx 代理,或者只绑定特定的内网 IP。
📊 第三部分:Prometheus 关键监控指标清单
这是 Alen 在 Grafana 看板上配置的核心指标,也是 Geth 暴露出的 Metrics 中最重要的部分:
| 指标名称 (Metric Name) | 含义 | 正常范围 | 告警阈值 |
chain_head_block | 本地已同步的最新区块高度 | 随时间递增 | N/A |
p2p_peer_count | 当前连接的邻居节点数量 | 30 ~ 50 (主网) | < 10(说明网络可能被隔离) |
eth_sync_remaining | 剩余未同步的区块/状态数 | 0 (同步完成) | > 0且长时间不下降 |
system_cpu_usage | CPU 使用率 | 40% ~ 80% | > 90%(可能遭受 DoS 攻击) |
disk_usage_percent | 磁盘空间使用率 | < 70% | > 85%(需立即扩容) |
🛠️ 第四部分:常用验证命令 (Cheat Sheet)
Alen 留在记事本里的命令,用于以后每次变更配置后自测:
1. 检查端口监听状态 (确保 8545 只有 127.0.0.1)
Bash
sudo ss -tulnp | grep 'geth\|nginx' # 预期输出: # tcp LISTEN 0 0 127.0.0.1:8545 ... users:(("geth"...)) <-- 只监听本地 # tcp LISTEN 0 0 0.0.0.0:8080 ... users:(("nginx"...)) <-- Nginx 监听所有2. 模拟内网调用 (测试白名单)
Bash
# 在本机测试 (应该成功) curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' http://127.0.0.1:8080 # 在非白名单机器测试 (应该返回 403 Forbidden) curl -v http://172.31.20.100:80803. 查看实时审计日志 (看谁在查数据)
Bash
tail -f /var/log/nginx/rpc_access.log # 格式:[时间] [来源IP] [请求状态] [响应时间](Day 4 附录结束)