基于 Rust + Pingora 的 HTTP/HTTPS 反向代理,支持精确域名的基本转发与 OpenStack 全自动实例发现两种站点模式,配置热重载、按 SNI 的多站点 TLS(含 HTTP/2)与进程内防火墙。
状态:已实现并验证(P0–P8 分阶段 TDD,含一次全量 code-review 与修复;P14 增加独立 OpenStack 环境文件 + forward 按实例名/UUID 后端)。
cargo test360 passed,8 个端到端脚本全部通过。详见下方「验证证据」。
- 两种站点模式
- forward:精确域名匹配 → 后端列表(可加权)→ 负载均衡
(
round_robin/weighted_round_robin/random/ketama),支持 TCP/HTTP 健康检查,宕机后端自动剔除。 后端可为字面IP:port(addr),也可为 OpenStack 实例名或 UUID(instance+ 显式port,站点级openstack_env引用一个环境),二者可在同一站点内混用;实例后端复用完整发现链路(解析、缓存、熔断、fail-open;相同实例的并发请求只向上游查询一次)。 - openstack:全自动发现。域名形如
{instance}-{port}.base_domain(如INS001-80.rent.do.top), 经 Keystone v3 认证 + Nova 2.1 全局查询实例的 floating/fixed IP,反向代理到IP:port;结果缓存约 1 天, 相同实例的并发访问合并为一次上游查询;另带熔断与 fail-open(上游故障时用最近一次成功结果继续服务)。
- forward:精确域名匹配 → 后端列表(可加权)→ 负载均衡
(
- 独立 OpenStack 环境:控制面配置从站点文件独立为
<config-dir>/openstack/<env-name>.yaml,站点按名引用(openstack_env);每环境一个 resolver + 一份缓存,被任意多个 openstack 站点与 forward 实例池共享。 - HTTPS / SNI:共享
0.0.0.0:443单监听,按 SNI 选择证书(forward 精确匹配;openstack 通配*.base_domain), ALPN 协商 HTTP/2;证书可随热重载无停机轮换。 - 防火墙:可选
firewall_dir下的INSTANCE.role规则(开放端口 + IP 白/黑名单,支持 CIDR),默认拒绝;403 不泄露细节。 - 热重载:
iris reload(SIGHUP,进程内原子快照换装)、URL 与请求零中断;配置校验失败保持旧配置。 - 可观测:Prometheus
/metrics(config_reload_total、firewall_denied_total)、/healthz、/version(含配置指纹config_hash);JSON 结构化日志。 - 借鉴并对照
xrays-tech/Iris_ssh的 OpenStack 发现行为(地址选择、鉴权、缓存、熔断、fail-open)。
需要 Rust 工具链(1.75+)。
cargo build --release
# 配置根目录:<root>/sites/ 下每站点一个 .yaml;<root>/openstack/ 下每环境一个 .yaml
cargo run --bin iris -- run --config-dir ./config默认监听:HTTP 0.0.0.0:80、HTTPS 0.0.0.0:443、指标 127.0.0.1:9091
(可在 main 装配层用环境变量 IRIS_HTTP_LISTEN_ADDR / IRIS_HTTP_LISTEN_TLS_ADDR / IRIS_HTTP_METRICS_ADDR 覆盖,便于测试)。
sites/www-example.yaml:
name: www-example
mode: forward
domain: www.example.com
http: true
https: true
cert_file: /etc/iris/certs/www.example.com.crt
key_file: /etc/iris/certs/www.example.com.key
backends:
- addr: 10.0.0.5:8080
weight: 3
- addr: 10.0.0.6:8080 # weight 默认 1
load_balancer: weighted_round_robin
health_check:
kind: http
http_path: /healthz
interval_secs: 5
timeout_secs: 2sites/apps.yaml——后端可引用 OpenStack 实例(名称或 UUID),此时站点级 openstack_env 必填;addr 与 instance 可在同一站点混用:
name: apps-example
mode: forward
domain: apps.example.com
http: true
https: false
openstack_env: prod # 引用 <config-root>/openstack/prod.yaml
backends:
- instance: INS001 # 实例名,或 8-4-4-4-12 UUID
port: 8080 # instance 后端必填的显式端口
weight: 2
- addr: 10.0.0.6:8080
weight: 1
load_balancer: weighted_round_robin
health_check:
kind: tcp
interval_secs: 5
timeout_secs: 2openstack/prod.yaml——控制面配置独立成文件(文件 stem = 环境名),被站点按名引用:
timeout_secs: 30 # 1..=600
cache_ttl_secs: 86400 # 默认 1 天;校验范围 60..604800
auth:
api_url: https://controller.example.com:5000/v3
approach: password # 或 application_credential
username: proxy
password: ${OS_PASSWORD} # 凭据推荐走环境变量
project_name: admin
project_domain_name: Default
user_domain_name: Default
nova:
all_tenants: truesites/rent.yaml——站点仅引用环境名,不再内嵌 openstack: 块:
name: rent
mode: openstack
base_domain: rent.do.top
http: true
https: true
cert_file: /etc/iris/certs/rent.do.top.crt # 需覆盖 *.rent.do.top 的通配证书
key_file: /etc/iris/certs/rent.do.top.key
firewall_dir: /etc/iris/firewall/rent # 可选;实例名.role 规则文件
openstack_env: prod # 引用 <config-root>/openstack/prod.yaml(必填)firewall_dir/INS001.role(文件名 = 实例名):
allowed_ports: [80, 8080] # 开放本地端口;不列出 = 全拒
whitelist: # 可选;非空时仅放行名单内客户端
- 192.168.1.0/24
- 10.0.0.7
blacklist: # 可选;命中即拒(优先级高于白名单)
- 203.0.113.9iris reload --pidfile /run/iris/iris.pid # 发 SIGHUP;校验失败保持旧配置| 文档 | 内容 |
|---|---|
dev-docs/README.md |
文档索引 + 运行说明 + 最新验证证据 |
dev-docs/design.md |
总体架构、组件、请求生命周期、设计决策 |
dev-docs/config.md |
配置 schema、校验、域名匹配、secret 展开 |
dev-docs/discovery.md |
OpenStack 发现:认证/查询/地址选择/缓存/熔断/fail-open |
dev-docs/firewall.md |
防火墙判定顺序与白/黑名单语义 |
dev-docs/reload.md |
热重载机制、信号、优雅关闭/升级 |
dev-docs/ops-deploy.md |
部署与运维:systemd unit、证书零停机轮换、排障 |
开发计划与门禁见 dev-docs/plan/(P0–P8 分步、跨阶段接口契约、验收门禁)。
src/
main.rs # 装配:监听、TLS、后台服务、CLI 分发
config/ # 站点 + 独立 OpenStack 环境文件解析、校验、${ENV} 展开(redacted Debug)
routing.rs # 精确/通配域名路由(纯函数)
lb.rs # LbEngine:RR/加权/随机/ketama + 共享健康注册表
proxy.rs # ProxyHttp:forward/openstack 双分支、防火墙门禁、421/404/502、floating→fixed 回退
tls.rs # SniCertMap/SniCertSource:按 SNI 选证书、H2
state.rs # 单一原子 Snapshot(热重载一次切换)
firewall.rs # INSTANCE.role 解析 + default-deny 判定
reload.rs # apply_new_config + SIGHUP ReloadService + 指标
openstack/ # mod/auth/nova/cache:Keystone、Nova 2.1(名称/UUID show-by-id)、缓存/请求合并/熔断/fail-open;build_env_resolvers(键=环境名)
scripts/
e2e/*.sh # 8 个可复现端到端(forward/tls/openstack/reload/health-reload/tls-reload/new-site-health/forward-instance)
mock_openstack.py # Keystone/Nova 契约 mock
gen-test-certs.sh # 自签测试证书生成
tests/
stubs/upstream.py # 可配置 HTTP 上游桩
验证证据(HEAD bc7dcd4,2026-10-18):cargo build 0 warnings;cargo test 360 passed / 0 failed(lib 254、bin 7、config_load 71、firewall 11、openstack 17);scripts/e2e/ 8/8 通过(含新增 12-forward-instance.sh);生产代码零 unwrap/expect/panic。
OpenStack 自动模式的解析器行为(Keystone v3 / Nova 2.1 查询、地址选择、缓存/熔断/fail-open)对齐并借鉴了 Cloudflare Pingora 生态与 xrays-tech/Iris_ssh 的实现思路。