apnic-skills

APNIC 工作流封装(Workflows)

基于 apnic CLI 的多命令组合编排。每个工作流解决一个真实运维/审计问题,可逐条复制运行,或用 --json + jq 串成脚本。 前置:已构建 CLI(go build -o bin/apnic ./cmd/apnic)或用 go run ./cmd/apnic 替代下文 apnic。


工作流 1:国家资源审计

目的:盘点某国(如 CN)在 APNIC 拥有的全部 IPv4/IPv6/ASN 资源,并定位其持有组织。

# 1) CN 的 IPv4 分配(allocated + assigned)
apnic filter --source delegated --country CN --type ipv4

# 2) CN 的 IPv6 分配
apnic filter --source delegated --country CN --type ipv6

# 3) CN 的 ASN
apnic filter --source delegated --country CN --type asn

# 4) 定位持有组织(扩展版按 opaque-id 聚合)
apnic filter --source extended --country CN --json | jq -r '.Entries[] | "\(.OpaqueID)\t\(.Start)"' | sort -u

预期输出:步骤 1–3 输出 TSV 行(CN ipv4 <start> <value> <status> <date>);步骤 4 列出每个 opaque-id 对应的资源起点。

变体:

一键脚本(输出 CN 全资源计数) ```bash #!/usr/bin/env bash set -e for t in ipv4 ipv6 asn; do n=$(apnic filter --source delegated --country CN --type $t --json | jq '.Entries | length') echo "CN $t: $n 条" done ```

工作流 2:IP 全景调查

目的:给定一个 IP(如 1.1.1.1),汇聚其 RDAP 注册信息、whois 详情、反向 DNS,形成完整画像。

IP=1.1.1.1

# 1) RDAP:归属网络、CIDR、国家、持有实体、注册时间
apnic rdap ip $IP --json

# 2) whois:解析后的 Network/CIDR/Country/Org/Parent/Created/Updated
apnic whois ip $IP --json

# 3) 反向 DNS:PTR 记录
apnic reverse-dns $IP

# 4) 点对点历史:看该 IP 在某历史时刻的归属(RFC3339)
apnic rdap ip $IP --date 2020-06-01T00:00:00Z --json

预期输出:

延伸:用 RDAP 拿到的实体 handle 深挖持有人:

apnic rdap entity AIC3-AP --json

跨 RIR 归集:REx 可把同一持有组织在所有 RIR 的资源一次性聚齐(APNIC RDAP 只覆盖本区域)。先用 rex resources 找到该 IP 所属持有者的 opaqueId 与 rir,再聚合:

# 自定位当前出口网络(覆盖前缀/ASN/经济体)
apnic rex network

# 取最近委派资源,定位目标持有者的 opaqueId 与 rir
apnic rex resources ipv4

# 聚合该持有组织在对应 RIR 的全部 ASN/前缀与规模
apnic rex holder <opaqueId> <rir> --json
一键脚本(合并为单 JSON 报告) ```bash #!/usr/bin/env bash IP="${1:?usage: $0 }" jq -n --arg ip "$IP" \ --argjson rdap "$(apnic rdap ip $IP --json)" \ --argjson whois "$(apnic whois ip $IP --json)" \ --argjson ptr "$(apnic reverse-dns $IP --json)" \ '{ip:$ip, rdap:$rdap, whois:$whois, ptr:$ptr}' ``` </details> --- ## 工作流 3:转移与变更追踪 **目的**:审计 IP/ASN 在 RIR 间或组织间的转移,以及资源状态变更,定位异常迁移。 ```bash # 1) 最新转移记录(含跨 RIR 与 APNIC 内部转移) apnic transfers --json | jq '.Transfers[] | {date:.transfer_date, type, from:.source_organization.name, to:.recipient_organization.name, ip4:.ip4nets, ip6:.ip6nets, asn:.asns}' # 2) 按年度取 JCR 转移日志 apnic transfers --year 2023 --json # 3) 最新资源变更(JSON Lines) apnic changes --json | jq -c '.Changes[] | {cc, custodian, resources, status, type, timestamp}' # 4) 指定日期变更快照 apnic changes --date 20240101 --json ``` **预期输出**: - 转移:每条含转移日期、类型(`RESOURCE_TRANSFER`)、源/目的组织名与国家、转移的 IPv4/IPv6/ASN 集合。 - 变更:每条含国家、custodian(opaque-id)、资源 CIDR/ASN 列表、状态、类型(`delegated`/`cc-changed`/`status-changed`)、时间戳。 **变体**: - 仅看跨 RIR 转移:`jq 'select(.source_rir != .recipient_rir)'`。 - 仅看某国家:`jq 'select(.recipient_organization.country_code == "CN")'`。 - 监控某组织变更:`jq -c 'select(.custodian == "A92E1062")'`。
一键脚本(导出近期待转移 IPv4 段) ```bash #!/usr/bin/env bash apnic transfers --json | jq -r ' .Transfers[] | select(.ip4nets != null) | .ip4nets.transfer_set[] | "\(.start_address) - \(.end_address)" ' | sort -u ```
--- ## 工作流 4:数据完整性校验 **目的**:下载 APNIC 统计文件并验证其未被篡改——MD5 与官方校验值比对,PGP 签名与公钥核验。 ```bash # 1) 端到端 MD5 完整性校验(下载数据 + 旁挂 .md5,本地计算比对) apnic verify integrity --type delegated apnic verify integrity --type delegated-extended apnic verify integrity --type assigned apnic verify integrity --type delegated-ipv6-assigned apnic verify integrity --type legacy # 2) 取原始 MD5 校验值(BSD 风格 MD5 (file) = ) apnic verify md5 --type delegated # 3) 取 PGP 签名 apnic verify asc --type delegated # 4) 取 APNIC 签名公钥(用于离线验签 .asc) apnic verify pubkey ``` **预期输出**: - `integrity` 成功:`OK: delegated (date=latest) MD5 verified`,退出码 `0`。 - 失败(内容被改/网络错误):非零退出码 + 错误信息。 **变体**: - 校验历史快照:`apnic verify integrity --type delegated --date 20240101`。 - 批量校验所有类型: ```bash for t in delegated delegated-extended assigned delegated-ipv6-assigned legacy; do apnic verify integrity --type $t || echo "FAIL: $t" done ```
一键脚本(全部类型完整性巡检,任一失败即报警) ```bash #!/usr/bin/env bash set -e types=(delegated delegated-extended assigned delegated-ipv6-assigned legacy) fail=0 for t in "${types[@]}"; do if apnic verify integrity --type "$t"; then echo "✓ $t" else echo "✗ $t"; fail=1 fi done exit $fail ```
--- ## 工作流 5:RPKI/BGP 路由可信性核查 > 场景:审计某前缀的路由宣告是否与 RPKI 授权一致,并对照 thyme 实际 BGP 表。
步骤 ```bash # 1) RRDP notification:当前 RPKI 仓库序号与 snapshot 引用 apnic rpki notification --json | jq '{session: .SessionID, serial: .Serial, snapshot: .Snapshot.URI}' # 2) 流式解析当前 snapshot:发布/撤回的对象计数 apnic rpki snapshot # 3) thyme BGP 概览:路由条目数、ROA 覆盖(valid/invalid/none) apnic bgp summary --json | jq '.Entries[] | select(.Key|test("ROA|routing table entries"))' # 4) 某前缀的实际 origin ASN(raw table 过滤) apnic bgp raw-table --json | jq -r '.Routes[] | select(.Prefix=="1.1.1.0/24") | .ASN' # 5) 对照 IRR:该 ASN 的 aut-num 对象与宣告的路由对象 apnic irr aut-num --json | jq '.Objects[] | select(.PrimaryKey|test("13335"))' apnic irr route --json | jq '.Objects[] | select(.Attributes.route[0]=="1.1.1.0/24")' ``` ```bash #!/usr/bin/env bash set -euo pipefail PREFIX="${1:-1.1.1.0/24}" echo "== RRDP serial =="; apnic rpki notification --json | jq '.Serial' echo "== BGP summary =="; apnic bgp summary --json | jq '.Entries[] | select(.Key|test("ROA"))' echo "== origin ASN for $PREFIX =="; apnic bgp raw-table --json | jq -r --arg p "$PREFIX" '.Routes[]|select(.Prefix==$p)|.ASN' ```
--- ## 通用编排技巧 - **管道化**:所有命令支持 `--json`,可用 `jq`/`grep`/`awk` 二次处理;默认 TSV 适合直接 `sort | uniq -c`。 - **缓存**:SDK 默认 30 分钟缓存,批量工作流内重复请求同源数据只命中一次网络。调 `--cache-ttl 0` 可禁用。`irr` 等大转储走 `Get*` 缓存路径,TTL 内复用。 - **离线/自托管**:`--stats-base-url`/`--rdap-base-url`/`--whois-server`/`--ftp-base-url`/`--rrdp-base-url`/`--thyme-base-url`/`--rex-base-url` 可指向镜像或本地 mock,便于 CI 与隔离测试。 - **大文件下载**:APNIC FTP 对单连接限速 ~8-22 KB/s(delegated 4.3MB、extended、IRR `apnic.db.inetnum.gz` 50MB+ 等)。SDK 默认 `--max-concurrent-downloads 4` 自动分块:探测支持 Range 后切成 ~2MB 小块以 4 并发轮转下载,总吞吐提升 3-4 倍,50MB IRR dump 约 5-8 分钟完成。遇单块超时调小 `--chunk-size 1MB` 或调大 `--download-timeout 300s`。每个分块请求仍继承 stealth 伪装头与限速。 - **反爬**:默认 `--stealth`(浏览器伪装头 + 抖动 + 限速)。高频自动化设 `--rate-limit 2 --jitter 500ms-1500ms` 进一步降频;确需高速可 `--stealth=false --jitter 0-0`(仅发 UA+Accept,向后兼容旧 SDK 行为)。 - **退出码**:脚本中用 `set -e` 或显式 `||` 捕获失败,便于流水线门禁。