commit 25fcdf070158e490dc46e679feaac5f396bce110 Author: George Berkshire <41768719+ageorge156@users.noreply.github.com> Date: Wed Aug 19 16:46:26 2026 +0800 feat: vpn 全局直连规则真源(rule-provider)+ Clash Verge 脚本生成器 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..fc5fecb --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,30 @@ +# Gitea Actions CI 模板(统一极简合规门禁) +# 复制为 /.gitea/workflows/ci.yml 后即可用;无需调整。 +# 说明:runner 容器无外网且无 pip(Gitea 实例限制),CI 只做本地可完成的合规检查; +# 依赖安装与测试一律在本地运行(开发机上完成)。 +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Gitea 合规校验 + run: | + if git ls-tree -r HEAD | awk '$1==160000' | grep -q .; then echo "HEAD 含 gitlink"; exit 1; fi + if git ls-files .DS_Store | grep -q .; then echo "跟踪 .DS_Store"; exit 1; fi + if git grep -n -I -E 'sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|xox[baprs]-[A-Za-z0-9-]{10,}' HEAD | grep -q .; then echo "检出疑似凭证"; exit 1; fi + echo "Gitea 合规校验通过" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b503197 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# OS +.DS_Store +Thumbs.db + +# Secrets +.env +.env.* +!.env.example +secrets/ +*.pem +*.key diff --git a/README.md b/README.md new file mode 100644 index 0000000..cfb35c7 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# vpn-rules + +「哪些连接必须**直连、绝不进代理**」的规则真源(single source of truth),供电脑端 +Clash Verge 和华硕路由器上的 mihomo(Magic Catling2)等消费端同步引用,一处维护、处处生效。 + +## 目录结构 + +``` +vpn-rules/ +├── direct.yaml # 规则真源(mihomo rule-provider 格式) +└── scripts/ + └── gen_clash_verge_script.py # 真源 → Clash Verge Script.js 生成器 +``` + +## 为什么需要它 + +- 香港代理节点有流量配额(如 BygCloud 100GiB 用满),国内站点/内网流量被隧道进代理会白白烧配额。 +- 必须直连的对象:FRP 控制面(mTLS)、B 站全系、DeepSeek/MiniMax 等国内 LLM API、 + 内网/私有 IP 段、国区 iCloud、UGREEN 绿联 NAS 云。 +- **踩坑**:Clash Verge 全局 `Merge.yaml` 的 `prepend-rules` 会被内核忽略(不生效), + 全局直连规则的唯一正确机制是 `profiles/Script.js`(`main(config)` 把规则 prepend 进 `config.rules`)。 + +## 消费端接入 + +### Clash Verge(macOS) + +```bash +# 生成并写入 Clash Verge 数据目录的 Script.js +python3 scripts/gen_clash_verge_script.py +# 然后在 Clash Verge 界面点「重启内核」生效 +``` + +对当前及以后新增/切换的所有订阅生效;订阅更新不会覆盖它。 + +### 路由器 mihomo(Magic Catling2 / OpenWrt) + +rule-provider 引用(policy 在外层指定),定期自动拉取真源: + +```yaml +rule-providers: + vpn-direct: + type: http + behavior: classical + url: https://gitea.puyuanfh.cn/ageorge156/vpn-rules/raw/branch/main/direct.yaml + interval: 86400 + path: ./ruleset/vpn-direct.yaml + +rules: + - RULE-SET,vpn-direct,DIRECT +``` + +> 路由器内网自建 Gitea(192.168.50.2:3000)也可直连:`http://192.168.50.2:3000/ageorge156/vpn-rules/raw/branch/main/direct.yaml`。 + +## 维护流程 + +1. 改 `direct.yaml`(rule-provider 条目,**不含**策略字段)。 +2. `git commit` + push 到本仓库 `main`。 +3. Clash Verge 端重跑 `python3 scripts/gen_clash_verge_script.py` + 重启内核; + 路由器端等 rule-provider 到期自动拉新或手动 reload。 diff --git a/direct.yaml b/direct.yaml new file mode 100644 index 0000000..932121e --- /dev/null +++ b/direct.yaml @@ -0,0 +1,65 @@ +# 全局直连(DIRECT)规则真源 — mihomo rule-provider 格式 +# +# 用法:本文件是「哪些连接必须直连、绝不进代理」的唯一真源。 +# 消费端(Clash Verge / 路由器 mihomo)用 rule-provider 引用它,或由 +# scripts/gen_clash_verge_script.py 生成为 Clash Verge 的 Script.js。 +# +# rule-provider 引用示例(policy 在外层规则里指定): +# rule-providers: +# vpn-direct: +# type: http +# behavior: classical +# url: https://gitea.puyuanfh.cn/ageorge156/vpn-rules/raw/branch/main/direct.yaml +# interval: 86400 +# path: ./ruleset/vpn-direct.yaml +# rules: +# - RULE-SET,vpn-direct,DIRECT +# +# 修改后:直接 commit 到本仓库 main;Clash Verge 端重跑 +# python3 scripts/gen_clash_verge_script.py (默认写入 Clash Verge 数据目录并提示重启内核) +# 路由器端则等 rule-provider 的 interval 到期或手动 reload。 + +payload: + # FRP 控制面(mTLS)保持直连 + - DOMAIN,frp.puyuanfh.cn + - IP-CIDR,8.133.177.3/32,no-resolve + + # 国内视频(B 站全系)直连 + - DOMAIN-SUFFIX,bilibili.com + - DOMAIN-SUFFIX,bilibili.cn + - DOMAIN-SUFFIX,bilivideo.com + - DOMAIN-SUFFIX,bili2233.cn + - DOMAIN-SUFFIX,hdslb.com + + # 国内 IP 兜底:任何其它国内站点不再被隧道到 HK 代理 + - GEOIP,CN,no-resolve + + # 国内 LLM API 直连(DeepSeek / MiniMax 含海螺 AI) + - DOMAIN-SUFFIX,deepseek.com + - DOMAIN-SUFFIX,minimaxi.com + - DOMAIN-SUFFIX,minimax.chat + - DOMAIN-SUFFIX,hailuoai.com + - DOMAIN,minimax-public-cdn.oss-cn-wulanchabu.aliyuncs.com + + # 内网 / 私有段绝不上代理 + - IP-CIDR,127.0.0.0/8,no-resolve + - IP-CIDR,10.0.0.0/8,no-resolve + - IP-CIDR,172.16.0.0/12,no-resolve + - IP-CIDR,192.168.0.0/16,no-resolve + - IP-CIDR,100.64.0.0/10,no-resolve + - DOMAIN-SUFFIX,.local + + # 国区 iCloud 保持国内 + - DOMAIN-SUFFIX,icloud.com.cn + + # UGREEN 绿联 NAS / SyncSpace 云保持国内 + - DOMAIN-SUFFIX,ug.link + - DOMAIN-SUFFIX,ugreen.com + - DOMAIN-SUFFIX,ugnas.cloud + - DOMAIN-SUFFIX,ugos.cn + - DOMAIN-KEYWORD,ugreen + - DOMAIN-KEYWORD,ugnas + - DOMAIN-KEYWORD,ugreengroup + - IP-CIDR,110.42.0.0/16,no-resolve + - IP-CIDR,43.248.128.0/17,no-resolve + - IP-CIDR,111.170.0.0/16,no-resolve diff --git a/scripts/gen_clash_verge_script.py b/scripts/gen_clash_verge_script.py new file mode 100644 index 0000000..122853f --- /dev/null +++ b/scripts/gen_clash_verge_script.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""把 direct.yaml(真源)生成为 Clash Verge 的全局增强脚本 Script.js。 + +用法: + python3 scripts/gen_clash_verge_script.py [--out 目标路径] + +默认输出到 Clash Verge 数据目录 profiles/Script.js(全局直连规则的唯一正确机制, +见 clash-verge-config 记忆)。生成后需在 Clash Verge 界面点「重启内核」生效。 +""" +import argparse +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_OUT = Path.home() / "Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/profiles/Script.js" + + +def load_payload(path: Path) -> list[str]: + payload: list[str] = [] + with path.open(encoding="utf-8") as f: + for line in f: + line = line.strip() + if line.startswith("- "): + payload.append(line[2:].strip()) + if not payload: + raise SystemExit(f"❌ direct.yaml 里没有 payload 条目: {path}") + return payload + + +def to_rule(entry: str) -> str: + """rule-provider 条目(无策略)→ 完整规则(补 DIRECT)。""" + if entry.endswith(",no-resolve"): + return f"{entry[:-len(',no-resolve')]},DIRECT,no-resolve" + return f"{entry},DIRECT" + + +def render_script(payload: list[str]) -> str: + rules = [f" {to_rule(e)!r}," for e in payload] + lines = "\n".join(rules) + return f"""// 由 vpn-rules/direct.yaml 生成 — 勿手改,改真源后重跑 gen_clash_verge_script.py。 +// 全局直连规则:FRP 控制面、B站、国内 LLM、内网段、国区 iCloud、UGREEN 一律 DIRECT。 +const prependRules = [ +{lines} +]; + +function main(config, profileName) {{ + const existingRules = Array.isArray(config.rules) ? config.rules : []; + const existingRuleSet = new Set(existingRules); + + config.rules = [ + ...prependRules.filter((rule) => !existingRuleSet.has(rule)), + ...existingRules, + ]; + + return config; +}} +""" + + +def main() -> None: + ap = argparse.ArgumentParser(description="direct.yaml → Clash Verge Script.js") + ap.add_argument("--out", default=str(DEFAULT_OUT), help="输出路径") + args = ap.parse_args() + + payload = load_payload(REPO_ROOT / "direct.yaml") + script = render_script(payload) + out = Path(args.out) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(script, encoding="utf-8") + print(f"✅ 已生成 {out}({len(payload)} 条规则)") + print(" 请在 Clash Verge 界面点「重启内核」生效。") + + +if __name__ == "__main__": + main()