From 441cbd37b327af38a119ab83305b9a682bd5762d Mon Sep 17 00:00:00 2001 From: Timofey Gelazoniya Date: Sun, 29 Jun 2025 13:13:17 +0300 Subject: [PATCH] docs: add readme and example of template.json --- .gitignore | 4 +- README.md | 91 ++++++++++++ config/template.json | 322 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 README.md create mode 100644 config/template.json diff --git a/.gitignore b/.gitignore index 26977b0..65e794a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target /rules -/config -template.json +config/* +!config/template.json default.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..55888c1 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# sbrs — sing-box rules sync + +A containerized Rust utility to synchronize and self-host remote rule sets for `sing-box`. + +## Core Workflow + +1. **Reads** a source JSON configuration (`config/template.json`) containing remote URLs (e.g., from GitHub). +2. **Downloads** all rule files specified in the source config to a local directory (for example, `./rules`). +3. **Cleans up** stale rule files from the local directory that are no longer present in the source config. +4. **Generates** a new, client-facing configuration (`config/default.json`) where all remote URLs are rewritten to point to a self-hosted domain. + +The original `template.json` is never modified by this application. + +Upon container start, the utility performs an initial synchronization immediately and then sets up a cron job for subsequent, scheduled runs. + +## Prerequisites + +- Docker +- Docker Compose + +## Usage via Docker + +**1. Example of possible directory structure** + +Create the following directory structure in your project root before the first run: + +``` +. +├── config/ +│ └── template.json # Your input config with original URLs +├── rules/ # Empty directory for downloaded rule files +├── docker/ +│ ├── Dockerfile +│ └── entrypoint.sh +└── docker-compose.yml +``` +*Note: `config/default.json` will be generated by the application on the first run.* + +**2. Configuration** + +Edit `docker-compose.yml` to set the required environment variables: + +- `PUID`/`PGID`: Set to your host user's ID (`id -u`) and group ID (`id -g`) to prevent volume permission issues. +- `DOMAIN`: The public domain that will serve the rule files (e.g., `rules.mydomain.com`). +- `RULE_PATH`: The path on the domain where the files are located (e.g., `/` or `/files`). +- `TZ`: The timezone for container logs (e.g., `Etc/UTC`). +- `CRON_SCHEDULE`: The schedule for the sync task. + +**3. Run the Service** + +```bash +# Build the image and start the service in detached mode +docker-compose up --build -d +``` + +The service will run the sync task immediately upon starting and then again on the schedule defined by `CRON_SCHEDULE`. + +### Common Commands + +```bash +# View live logs +docker-compose logs -f + +# Trigger a manual sync immediately (without restarting) +docker-compose exec sbrs entrypoint.sh manual + +# Stop and remove the container +docker-compose down +``` + +## Local Development + +Requires the [Rust toolchain](https://rustup.rs/). + +**1. Build** +```bash +cargo build --release +``` + +**2. Run** + +All arguments are required. + +```bash +./target/release/sbrs \ + --input-config ./config/template.json \ + --rules-dir ./rules \ + --output-config ./config/default.json \ + --domain my.server.com \ + --rule-path rules +``` \ No newline at end of file diff --git a/config/template.json b/config/template.json new file mode 100644 index 0000000..c6c1200 --- /dev/null +++ b/config/template.json @@ -0,0 +1,322 @@ +{ + "log": { + "level": "error", + "timestamp": true + }, + "dns": { + "cache_capacity": 16384, + "servers": [ + { + "tag": "dns-remote", + "address": "tls://208.67.222.222", + "client_subnet": "{{ ansible_host }}" + }, + { + "tag": "dns-local", + "address": "195.208.4.1" + }, + { + "tag": "dns-block", + "address": "rcode://success" + } + ], + "rules": [ + { + "rule_set": [ + "category-ads-all", + "adobe" + ], + "server": "dns-block", + "disable_cache": true + }, + { + "domain_suffix": [ + "habr.com" + ], + "rule_set": [ + "telegram", + "google" + ], + "server": "dns-remote" + }, + { + "domain_suffix": [ + ".ru", + ".su", + ".ru.com", + ".ru.net", + "{{ marzban_domain }}", + "wikipedia.org" + ], + "domain_keyword": [ + "xn--", + "ozon", + "wildberries", + "aliexpress" + ], + "rule_set": [ + "gov-ru", + "yandex", + "vk", + "mailru", + "duckduckgo", + "mozilla", + "category-android-app-download", + "gitlab", + "debian", + "canonical", + "torrent-clients" + ], + "server": "dns-local" + }, + { + "process_name": [ + "qbittorrent.exe", + "thunderbird.exe", + "Obsidian.exe", + "javaw.exe" + ], + "server": "dns-local" + }, + { + "inbound": [ + "tun-in" + ], + "server": "dns-remote" + } + ], + "final": "dns-local" + }, + "inbounds": [ + { + "type": "tun", + "tag": "tun-in", + "interface_name": "tun0", + "stack": "system", + "address": "172.19.0.1/28", + "auto_route": true, + "strict_route": true, + "sniff_override_destination": true + } + ], + "outbounds": [ + { + "type": "direct", + "tag": "direct" + }, + { + "type": "selector", + "tag": "proxy", + "outbounds": null + }, + { + "type": "urltest", + "tag": "Fastest", + "outbounds": null, + "url": "https://www.gstatic.com/generate_204", + "interval": "15m0s" + } + ], + "route": { + "rules": [ + { + "action": "sniff" + }, + { + "protocol": "dns", + "action": "hijack-dns" + }, + { + "ip_is_private": true, + "outbound": "direct" + }, + { + "rule_set": [ + "category-ads-all", + "adobe" + ], + "action": "reject", + "method": "drop" + }, + { + "domain_suffix": [ + "habr.com" + ], + "rule_set": [ + "telegram", + "google" + ], + "outbound": "proxy" + }, + { + "domain_suffix": [ + ".ru", + ".su", + ".ru.com", + ".ru.net", + "{{ marzban_domain }}", + "wikipedia.org" + ], + "domain_keyword": [ + "xn--", + "ozon", + "wildberries", + "aliexpress" + ], + "ip_cidr": [ + "{{ ansible_host }}" + ], + "rule_set": [ + "gov-ru", + "yandex", + "vk", + "mailru", + "duckduckgo", + "mozilla", + "category-android-app-download", + "gitlab", + "debian", + "canonical", + "torrent-clients" + ], + "outbound": "direct" + }, + { + "action": "resolve", + "strategy": "prefer_ipv4" + }, + { + "rule_set": [ + "geoip-ru" + ], + "outbound": "direct" + }, + { + "process_name": [ + "qbittorrent.exe", + "thunderbird.exe", + "Obsidian.exe", + "javaw.exe" + ], + "outbound": "direct" + }, + { + "domain_suffix": [ + "deb.home.net" + ], + "outbound": "direct" + }, + { + "inbound": [ + "tun-in" + ], + "outbound": "proxy" + } + ], + "rule_set": [ + { + "tag": "torrent-clients", + "type": "remote", + "format": "source", + "url": "https://raw.githubusercontent.com/FPPweb3/sb-rule-sets/main/torrent-clients.json" + }, + { + "tag": "geoip-ru", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geoip/raw/rule-set/geoip-ru.srs" + }, + { + "tag": "gov-ru", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-category-gov-ru.srs" + }, + { + "tag": "yandex", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-yandex.srs" + }, + { + "tag": "google", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-google.srs" + }, + { + "tag": "telegram", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-telegram.srs" + }, + { + "tag": "vk", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-vk.srs" + }, + { + "tag": "mailru", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-mailru.srs" + }, + { + "tag": "duckduckgo", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-duckduckgo.srs" + }, + { + "tag": "mozilla", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-mozilla.srs" + }, + { + "tag": "category-android-app-download", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-category-android-app-download.srs" + }, + { + "tag": "adobe", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-adobe.srs" + }, + { + "tag": "gitlab", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-gitlab.srs" + }, + { + "tag": "debian", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-debian.srs" + }, + { + "tag": "canonical", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-canonical.srs" + }, + { + "tag": "category-ads-all", + "type": "remote", + "format": "binary", + "url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-category-ads-all.srs" + } + ], + "auto_detect_interface": true, + "override_android_vpn": true + }, + "experimental": { + "cache_file": { + "enabled": true + } + } +}