# Implementing Bgp Security With Rpki

> 使用路由来源授权（ROA）、RPKI-to-Router 协议和 Cisco、Juniper 路由器上的 ROV 策略，实施 BGP 路由来源验证（RPKI），以防止路由劫持。

- Skill: `killvxk/implementing-bgp-security-with-rpki` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/implementing-bgp-security-with-rpki`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/implementing-bgp-security-with-rpki/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/implementing-bgp-security-with-rpki

---


# 使用 RPKI 实施 BGP 安全

## 概述

资源公钥基础设施（RPKI，Resource Public Key Infrastructure）为 BGP 路由来源提供密码学验证，以防止路由劫持（route hijacking）和意外路由泄漏。RPKI 使网络运营商能够创建路由来源授权（ROA，Route Origin Authorization），声明哪些自治系统（AS，Autonomous System）被授权通告特定 IP 前缀。BGP 路由器通过路由来源验证（ROV，Route Origin Validation）将收到的路由通告与 RPKI 数据进行验证，拒绝来源无效的路由。本技能涵盖通过地区互联网注册机构（RIR，Regional Internet Registry）创建 ROA、部署 RPKI 验证器软件、在 Cisco IOS-XE 和 Juniper Junos 路由器上配置 ROV，以及基于 RPKI 验证状态实施 BGP 过滤策略。

## 前置条件

- 从 RIR（ARIN、RIPE、APNIC、AFRINIC、LACNIC）分配的 IP 地址空间
- 用于创建 ROA 的 RIR 会员门户访问权限
- BGP 路由器（Cisco IOS-XE 16.x+、Juniper Junos 12.2+ 或同等产品）
- 用于 RPKI 验证器/缓存的 Linux 服务器（Routinator、FORT 或 OctoRPKI）
- 了解 BGP 路由和 AS 路径概念

## 核心概念

### RPKI 架构

```
┌──────────────────────────────────────────────┐
│           地区互联网注册机构                   │
│    (ARIN, RIPE, APNIC, AFRINIC, LACNIC)      │
│                                               │
│  ┌─────────────────────────────────────────┐  │
│  │  信任锚点（根 CA 证书）                   │  │
│  │  ├── CA 证书（ISP/组织）                  │  │
│  │  │   ├── ROA: AS64512 → 198.51.100.0/24 │  │
│  │  │   └── ROA: AS64512 → 2001:db8::/32   │  │
│  │  └── CA 证书（另一组织）                  │  │
│  │      └── ROA: AS64513 → 203.0.113.0/24  │  │
│  └─────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘
                     │ rsync/RRDP
                     ▼
         ┌──────────────────────┐
         │  RPKI 验证器/缓存     │  (Routinator, FORT, OctoRPKI)
         │  验证 ROA             │
         │  通过 RTR 提供 VRP    │
         └──────────────────────┘
                     │ RTR 协议（TCP 8323）
                     ▼
         ┌──────────────────────┐
         │  BGP 路由器           │
         │  执行 ROV              │
         │  应用策略：           │
         │   Valid → Accept      │
         │   Invalid → Reject    │
         │   NotFound → Accept   │
         └──────────────────────┘
```

### RPKI 验证状态

| 状态 | 含义 | 建议操作 |
|-------|---------|-------------------|
| **Valid（有效）** | ROA 存在，来源 AS 和前缀匹配 | 接受路由（优先） |
| **Invalid（无效）** | ROA 存在，但来源 AS 或前缀长度不匹配 | 拒绝路由 |
| **NotFound（未找到）** | 没有 ROA 覆盖此前缀 | 接受（但降低优先级） |

### 路由来源授权（ROA）

ROA 是一个签名对象，声明：
- **前缀**：IP 地址范围（例如 198.51.100.0/24）
- **来源 AS**：被授权通告此前缀的 AS（例如 AS64512）
- **最大长度**：可通告的最大前缀长度（例如 /24）

## 步骤

### 步骤 1：在您的 RIR 创建 ROA

**ARIN（北美）：**
1. 登录 ARIN Online 门户
2. 导航至 Routing Security > Route Origin Authorizations
3. 创建 ROA：
   - 前缀：198.51.100.0/24
   - 来源 AS：AS64512
   - 最大长度：/24（设置等于前缀长度，防止子前缀劫持）
4. 签名并提交

**RIPE NCC（欧洲）：**
1. 登录 RIPE NCC LIR 门户
2. 导航至 Certification（RPKI）> ROAs
3. 使用前缀、来源 AS 和最大前缀长度创建 ROA

### 步骤 2：部署 RPKI 验证器（Routinator）

```bash
# 在 Ubuntu 上安装 Routinator
sudo apt install -y routinator

# 或通过 Cargo（Rust）安装
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install routinator

# 初始化 Routinator（接受 TAL）
routinator init --accept-arin-rpa

# 以 RTR 服务器模式启动 Routinator
routinator server \
  --rtr 0.0.0.0:8323 \
  --http 0.0.0.0:8080 \
  --refresh 600 \
  --retry 60 \
  --expire 7200

# 以 systemd 服务运行
cat > /etc/systemd/system/routinator.service << 'SYSTEMD'
[Unit]
Description=Routinator RPKI Validator
After=network.target

[Service]
Type=simple
User=routinator
ExecStart=/usr/bin/routinator server --rtr 0.0.0.0:8323 --http 0.0.0.0:8080
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
SYSTEMD

sudo systemctl enable routinator
sudo systemctl start routinator

# 验证 Routinator 正在提供数据
curl http://localhost:8080/api/v1/status
curl http://localhost:8080/api/v1/validity/AS64512/198.51.100.0/24

# 查看验证后的 ROA 载荷（VRP）
routinator vrps --format json | head -50
```

### 步骤 3：在 Cisco IOS-XE 上配置 ROV

```
! 配置 RPKI 缓存服务器连接
router bgp 64512
 bgp rpki server tcp 10.0.5.50 port 8323 refresh 600

! 验证 RPKI 会话
show bgp rpki server
show bgp rpki table

! 创建基于 RPKI 的过滤路由映射
route-map RPKI-FILTER permit 10
 match rpki valid
 set local-preference 200

route-map RPKI-FILTER permit 20
 match rpki not-found
 set local-preference 100

route-map RPKI-FILTER deny 30
 match rpki invalid

! 应用到 BGP 邻居
router bgp 64512
 address-family ipv4 unicast
  neighbor 198.51.100.1 route-map RPKI-FILTER in
  neighbor 203.0.113.1 route-map RPKI-FILTER in

 address-family ipv6 unicast
  neighbor 2001:db8::1 route-map RPKI-FILTER in

! 验证 ROV 操作
show bgp ipv4 unicast rpki validation
show bgp ipv4 unicast 198.51.100.0/24
show ip bgp rpki table
show ip bgp neighbors 198.51.100.1 rpki state
```

### 步骤 4：在 Juniper Junos 上配置 ROV

```
# 配置 RPKI 缓存连接
set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 port 8323
set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 refresh-time 600
set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 hold-time 7200
set routing-options validation group RPKI-VALIDATORS session 10.0.5.50 record-lifetime 7200

# 创建验证策略
set policy-options policy-statement RPKI-POLICY term valid from validation-database valid
set policy-options policy-statement RPKI-POLICY term valid then validation-state valid
set policy-options policy-statement RPKI-POLICY term valid then local-preference 200
set policy-options policy-statement RPKI-POLICY term valid then accept

set policy-options policy-statement RPKI-POLICY term invalid from validation-database invalid
set policy-options policy-statement RPKI-POLICY term invalid then validation-state invalid
set policy-options policy-statement RPKI-POLICY term invalid then reject

set policy-options policy-statement RPKI-POLICY term unknown from validation-database unknown
set policy-options policy-statement RPKI-POLICY term unknown then validation-state unknown
set policy-options policy-statement RPKI-POLICY term unknown then local-preference 100
set policy-options policy-statement RPKI-POLICY term unknown then accept

# 应用到 BGP 对等体
set protocols bgp group TRANSIT import RPKI-POLICY
set protocols bgp group PEERS import RPKI-POLICY

# 验证
show validation session
show validation database
show validation statistics
show route validation-state invalid
```

### 步骤 5：监控 RPKI 部署

```python
#!/usr/bin/env python3
"""监控 RPKI ROV 部署健康状况和覆盖率统计。"""

import json
import sys
import urllib.request


class RPKIMonitor:
    def __init__(self, routinator_url: str = "http://localhost:8080"):
        self.routinator_url = routinator_url

    def get_status(self) -> dict:
        """获取 Routinator 服务器状态。"""
        url = f"{self.routinator_url}/api/v1/status"
        try:
            with urllib.request.urlopen(url) as resp:
                return json.loads(resp.read())
        except Exception as e:
            print(f"连接 Routinator 时出错：{e}")
            return {}

    def check_validity(self, asn: int, prefix: str) -> dict:
        """检查前缀/来源对的 RPKI 有效性。"""
        url = f"{self.routinator_url}/api/v1/validity/AS{asn}/{prefix}"
        try:
            with urllib.request.urlopen(url) as resp:
                return json.loads(resp.read())
        except Exception as e:
            return {"error": str(e)}

    def get_vrp_count(self) -> int:
        """获取经验证的 ROA 载荷（VRP）总数。"""
        status = self.get_status()
        return status.get("vrpsCount", 0)

    def report(self, prefixes_to_check: list):
        """生成 RPKI 监控报告。"""
        status = self.get_status()

        print(f"\n{'='*60}")
        print("RPKI 监控报告")
        print(f"{'='*60}")
        print(f"\nRoutinator 状态：")
        print(f"  版本：{status.get('version', '未知')}")
        print(f"  VRP 总数：{status.get('vrpsCount', 'N/A')}")
        print(f"  上次更新：{status.get('lastUpdateDone', 'N/A')}")

        if prefixes_to_check:
            print(f"\n前缀有效性检查：")
            for asn, prefix in prefixes_to_check:
                result = self.check_validity(asn, prefix)
                validity = result.get("validated_route", {}).get(
                    "validity", {}).get("state", "error")
                print(f"  AS{asn} -> {prefix}: {validity.upper()}")


if __name__ == "__main__":
    monitor = RPKIMonitor()

    # 检查自己的前缀
    own_prefixes = [
        (64512, "198.51.100.0/24"),
    ]

    monitor.report(own_prefixes)
```

## 最佳实践

- **为所有前缀创建 ROA**——为您的组织通告的每个前缀签署 ROA
- **最大长度 = 前缀长度**——将 max-length 设置等于通告的前缀长度，防止子前缀劫持
- **双验证器**——运行两个独立的 RPKI 验证器以实现冗余
- **先软策略**——在丢弃 RPKI 无效路由之前，先从记录日志开始
- **监控 ROA 到期**——为即将到期的 ROA 证书设置警报
- **与上游协调**——通知运营商关于您的 RPKI 部署情况
- **使用 Looking Glass 测试**——使用公共 RPKI 验证器验证您的 ROA 是否可见

## 参考资料

- [RPKI 文档](https://rpki.readthedocs.io/en/latest/)
- [Cloudflare RPKI 博客](https://blog.cloudflare.com/rpki/)
- [NLnet Labs Routinator](https://www.nlnetlabs.nl/projects/rpki/routinator/)
- [RIPE NCC RPKI 仪表板](https://rpki.ripe.net/)
- [RFC 6480 - RPKI 架构](https://www.rfc-editor.org/rfc/rfc6480)
- [RFC 6811 - BGP 来源 AS 验证](https://www.rfc-editor.org/rfc/rfc6811)

