Skip to main content

ipgate — 基于 IP 证书的 HTTPS 端口映射网关

把本机任意 HTTP 服务(127.0.0.1:8080、0.0.0.0:3000 …)一键发布成公网 HTTPS 端口,证书是 Let's Encrypt 直接签给 IP 地址本身的短期证书,不需要域名。

https://115.190.165.92:8443   ->  127.0.0.1:8080
https://115.190.165.92:9444   ->  0.0.0.0:3000
https://115.190.165.92:10443  ->  /var/www/docs   (静态目录)

Caddy 负责 TLS 终止与反向代理,Python 负责证书生命周期、路由管理、Web 面板和 CLI。 纯 stdlib,无 pip 依赖。


架构

        公网
          │  https://IP:8443, :9444, :443 …
          ▼
   ┌──────────────┐   admin API (127.0.0.1:2019)   ┌────────────────┐
   │    Caddy     │ ◀───────────────────────────── │  ipgate (py)   │
   │ TLS + 反代    │                                │  面板 + 续期    │
   └──────┬───────┘                                └───────┬────────┘
          │ 读证书文件                                       │ 调用
          ▼                                                ▼
   <pyproxy>/tls/{fullchain,key}.pem              ◀──  acme.sh (tls-alpn-01)
                    ▲
                    └── 同一份证书,pyproxy 也在用

为什么不让 Caddy 自己签证书

Let's Encrypt 的 IP 证书只在 shortlived profile(约 6.5 天)下签发, Caddy 至今没有实现这条路径(caddyserver/caddy#7399),会直接报 subject '<ip>' cannot have public IP certificate。

所以签发继续交给 acme.sh,Caddy 只负责加载证书文件。配置里全程 automatic_https.disable = true,避免它徒劳地尝试再报错。

和 pyproxy 的共存:443 端口之争

同机的 pyproxy 用 --alpn(tls-alpn-01)续期,需要独占 443。 Caddy 常驻 443 会让它签不下来,而证书 6 天就过期 —— 直接把 VPN 的 HTTPS 弄挂。

ipgate 的做法是成为唯一真正动手续期的一方,且不改 pyproxy 一行代码: 抢在 acme.sh 自己计划的时刻之前把证书换掉,它跑到时就只会打印 Skipping. Next renewal time is ... 然后退出,全程不碰 443。

关键是怎么知道它计划什么时候动手。不能靠「剩余天数」猜 —— acme.sh 走 RFC 9773 ARI,续期时刻是在 Let's Encrypt 下发的 suggestedWindow 里 随机取点,命令行上的 --days 会被整个覆盖:

# acme.sh 内部
_ari_offset=$(_math "$(_time)" % "$_ari_window")
Le_NextRenewTime=$(_math "$_ari_start_t_new" + "$_ari_offset")

实测这台机器上 Le_NextRenewTime = notAfter − 3.35 天,而不是 --days 3 应该给出的 notAfter − 3 天。

所以 ipgate 直接读它写在共享账本 (<acme_home>/<IP>_ecc/<IP>.conf 里的 Le_NextRenewTime)中的计划时刻, 永远比它早 6 小时动手(renew_lead 可调)。ARI 随机到哪都成立, LE 以后改窗口形状也不用跟着调参数。读不到账本时才退回「剩余不足 4 天」的兜底判断。

即便万一让 pyproxy 抢先跑了一次,后果也只是它 bind 443 失败、在自己的 status.json 里记一条错误 —— 证书是共用的,ipgate 这边照常续上,不会真的断服务。

ipgate 调 acme.sh 时带 --force(按 acme.sh 自己的账本还没到期),这绕过了它 内建的节流,因此另有一道 min_issue_interval(默认 12 小时)兜底,防止逻辑 出错时反复冲击 Let's Encrypt 的配额。

续期发生时,如果确实有映射占着 443,ipgate 会通过 admin API 临时把这一个 监听器摘掉,签完立刻挂回来 —— 其余端口全程不受影响,443 的中断窗口约 10 秒, 约每 2.5 天一次。

安装

./install.sh                    # 幂等,可反复执行
ipgate totp                     # 强烈建议:绑定两步验证

不用 install.sh:pip 安装

pip install pyipgate

pip install 只装 Python 代码,不像 install.sh 一条命令包办一切,但装完之后 剩下的都能在网页 http://<IP>:9500 上点完:

  1. 跑一次 ipgate-server,打开面板。「日志」页如果显示 Caddy 未安装,会出现一张 "安装 Caddy"卡片——点一下会下载对应架构的二进制、注册 caddy.service 并启动, 跟 install.sh 那段是同一份逻辑,只是触发方式从命令行改成了网页; 命令行等价操作是 ipgate caddy install
  2. 「证书」页如果没有可用证书,会出现"初始化证书"卡片:先探测同机是否有 pyproxy 可复用,探测不到就下载 acme.sh 并签发第一张证书(正式/staging 二选一); 命令行等价操作是 ipgate cert init(--dry-run 先看会做什么,--staging 先在测试环境跑通流程,--acme-sh <路径> 复用已有的 acme.sh)
  3. 想要开机自启 / 崩溃自动重启 ipgate-server 本身(Caddy 那份已经由上面第 1 步 注册好了),照抄 install.sh 里 ipgate.service 那段 systemd 单元自己写一份—— 这是目前唯一还没搬上网页的部分,因为 ipgate 没法从自己进程里注册"重启自己"的 服务

状态目录(config.json / routes.json / caddy.json / cert-status.json / acme.sh 本体 / 签发出的证书)默认在 /etc/ipgate/(root 权限),可用 IPGATE_HOME 环境变量改到别处。

简单说:install.sh 是「自动挡」,一条命令连 Caddy 和 systemd 一起装好; pip install pyipgate 是「半自动挡」,Caddy 和证书这两步网页/CLI 都能触发, 只有 ipgate-server 自身的开机自启还需要手写一份 systemd 单元。

首次设置密码

服务启动时若还没有密码,会开一个首次设置窗口(setup_window,默认 12 小时), 直接打开面板就能在浏览器里设定,不必 SSH。窗口只存在内存里:

  • 设定完成即关闭;
  • 超时未设定则锁死,systemctl restart ipgate 可重新开窗;
  • 已经设过密码的实例重启不会再开窗。

不想用窗口的话,ipgate passwd 随时可以在命令行设定/修改。

⚠️ 窗口期内面板对全网是真的开放的:任何人扫到 :9500 都能抢先设定密码、 接管这台网关。窗口越长风险越大 —— 12 小时意味着装完就该尽快去把密码设掉, 别装完就撂着过夜。改短:config.json 里的 setup_window(秒)。

安装脚本会下载 Caddy、部署代码(路径由 IPGATE_DIR 决定)、自动探测同机 pyproxy 的证书目录、注册两个 systemd 单元(caddy.service / ipgate.service), 并把 CLI 链接为 /usr/local/bin/ipgate。

pyproxy 的安装路径各机器不一:已见过 /root/vpn/pyproxy 和 /root/main/vpn/pyproxy。探测按「证书文件最新」挑选,多个备份目录也不会认错。 探测不到时在 config.json 里手工填 cert_dir / acme_home / acme_sh 即可。

CLI

ipgate status                              # 总览:IP / 证书 / Caddy / 所有映射
ipgate ls

ipgate add web 8443 127.0.0.1:8080         # 新增映射
ipgate add api 9444 0.0.0.0:3000 --note "内部 API"
ipgate add sec 10443 127.0.0.1:9443 --tls  # 后端本身是 HTTPS(默认跳过证书校验)
ipgate add-static docs 11443 /var/www/docs # 直接发布一个目录

ipgate edit web --upstream 127.0.0.1:8081
ipgate disable web / enable web / rm web
ipgate test web                            # 探测后端连通性
ipgate reload                              # 重新生成配置并热加载

ipgate cert                                # 证书详情(JSON)
ipgate cert renew                          # 按需续期
ipgate cert renew --force                  # 强制重签(注意每周配额)
ipgate cert init                           # 首次初始化:探测同机 pyproxy,探测不到就下载 acme.sh 并签发
ipgate cert init --dry-run                 # 只打印会做什么,不实际下载/签发/写配置
ipgate cert init --staging                 # 先在 LE 测试环境跑通流程,不消耗正式配额
ipgate cert init --acme-sh /path/to/acme.sh  # 已有 acme.sh 就不用重新下载

ipgate passwd  |  ipgate totp  |  ipgate token
ipgate caddy start|stop|restart|status|config
ipgate caddy install                       # 下载二进制、注册 systemd 单元、启动(幂等)
ipgate caddy install --force               # 已装过也强制重新下载覆盖

ipgate bundle                              # 打包一份可拿去别处部署的 zip
ipgate bundle --with-caddy                 # 连 caddy 二进制一起打(约 17 MB)

部署到别的机器

管理界面「设置」页有下载按钮,命令行用 ipgate bundle。包里是纯代码: 管理密码、API token、两步验证密钥、端口映射都不在里面(用白名单挑文件, 不是排除法 —— 漏掉排除项就是泄密)。

目标机器上解压后 bash install.sh 即可,脚本会自动探测那台机器上 pyproxy 的 证书目录。

--with-caddy 版把 caddy 二进制一起打进去,install.sh 会直接用、不联网。 国内机器直连 GitHub 拉 caddy 常常只有几十 KB/s,这一步能省十几分钟。

所有写操作都会立刻通过 Caddy admin API 热加载,不断开已有连接。 Caddy 若拒绝新配置,改动会自动回滚。

管理面板

http://<IP>:9500 —— 端口映射的增删改查、证书状态与手动续期、acme.sh 实时输出、两步验证绑定(扫码)、systemd 日志查看。

登录需要密码 且(启用后)动态验证码,登录失败 5 分钟内 8 次即锁定。

面板按需求直接监听公网 HTTP 端口,密码在链路上是明文的。 想让面板自己也走 HTTPS,加一条指向它的映射即可: ipgate add panel 443 127.0.0.1:9500,之后用 https://<IP>/ 访问。

文件

路径 说明
config.json 面板端口、证书路径、续期阈值、首次设置窗口、密码散列、API token(0600)
routes.json 端口映射定义
caddy.json 生成的 Caddy 配置,供 systemd 冷启动使用
cert-status.json 最近一次续期的状态与 acme.sh 输出
totp.json 两步验证密钥(0600)

注意

  • 端口一对一:一个对外端口只属于一条映射,新增时会先试探 bind,端口被别的 进程占用会当场拒绝,而不是等 Caddy 起不来。
  • 0.0.0.0:port 会自动改写成 127.0.0.1:port —— 从 ss -tlnp 抄来的地址 可以直接粘。
  • WebSocket 无需额外配置,Caddy 的 reverse_proxy 原生支持协议升级。
  • 不要用别的工具再给这个 IP 签证书:同一 SAN 的重复证书 Let's Encrypt 每周只给 5 张,两个客户端各自续期很容易撞上限。

Metadata

Release files for pyipgate 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyipgate 1.0.0
File Size Uploaded Provenance
pyipgate-1.0.0.tar.gz 66.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pyipgate 1.0.0
File Interpreter ABI Platform
pyipgate-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 134.7 kB

Release files / pyipgate-1.0.0.tar.gz

Download URL pyipgate-1.0.0.tar.gz
Size 66.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e5fa9121cb8890f44bc3b9857d069815c162c49dec59d7f9212b9c005770fffd
BLAKE2b-256 checksum
How to use checksums
11114977f1b5eb2ebe3a696932cd72bdec92ef2e1cae15d2f2e545432423c5eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / pyipgate-1.0.0-py3-none-any.whl

Download URL pyipgate-1.0.0-py3-none-any.whl
Size 68.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0216da20aa0931426780a926394934bb83beddc9dc50f2284b2dbf1280b3a318
BLAKE2b-256 checksum
How to use checksums
7424d2f0793127342a7a6722bca3effdd50c2cefa89e690e00ff013bd11e8411
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Security for pyipgate 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page