{"id":50416887,"url":"https://github.com/localvoid/resolved-nftset","last_synced_at":"2026-05-31T06:30:27.683Z","repository":{"id":360866534,"uuid":"1252046966","full_name":"localvoid/resolved-nftset","owner":"localvoid","description":"A Linux service that monitors DNS queries resolved via `systemd-resolved` and adds IPs matching specific hostnames to `nftables` sets","archived":false,"fork":false,"pushed_at":"2026-05-28T06:56:33.000Z","size":25,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T08:16:43.693Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/localvoid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T06:26:32.000Z","updated_at":"2026-05-28T06:55:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/localvoid/resolved-nftset","commit_stats":null,"previous_names":["localvoid/resolved-nftset"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/localvoid/resolved-nftset","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localvoid%2Fresolved-nftset","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localvoid%2Fresolved-nftset/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localvoid%2Fresolved-nftset/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localvoid%2Fresolved-nftset/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/localvoid","download_url":"https://codeload.github.com/localvoid/resolved-nftset/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/localvoid%2Fresolved-nftset/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33722156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T06:30:24.583Z","updated_at":"2026-05-31T06:30:27.678Z","avatar_url":"https://github.com/localvoid.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# resolved-nftset\n\nA Linux service that monitors DNS queries resolved via `systemd-resolved` and adds IPs matching specific hostnames to `nftables` sets. This enables policy-based routing — for example, routing traffic to certain domains through a WireGuard VPN.\n\n```\nsystemd-resolved  ──varlink──▶  resolved-nftset  ──netlink──▶  nftables kernel sets\n```\n\n## How it works\n\n1. Connects to `systemd-resolved` via the `io.systemd.Resolve.Monitor` varlink interface.\n2. Listens for all DNS responses in real time.\n3. Matches each resolved hostname against user-defined rules (FQDN prefix matching).\n4. On a match, adds the resolved IPv4/IPv6 addresses to the corresponding nftables set.\n5. Your nftables ruleset marks packets destined for those IPs, and routing policy sends them to your desired interface.\n\n## Requirements\n\n- **systemd ≥ 246** (for `io.systemd.Resolve.Monitor`)\n- **nftables**\n\n## Installation\n\n### Build from source\n\n```bash\ncargo build --release\nsudo install -m 755 target/release/resolved-nftset /usr/local/bin/\n```\n\n### Install the systemd service\n\n```bash\nsudo useradd -r -s /sbin/nologin resolved-nftset\nsudo install -m 644 resolved-nftset.service /etc/systemd/system/\nsudo systemctl daemon-reload\nsudo systemctl enable --now resolved-nftset\n```\n\n## Configuration\n\nConfiguration lives under `/etc/resolved-nftset/`. The directory structure maps to the nftables table and set names:\n\n```\n/etc/resolved-nftset/\n└── \u003ctable_name\u003e/           # nftables table name\n    └── \u003cset_name\u003e/         # set name (without _v4, _v6 suffix)\n        └── rules.conf      # hostnames to match, one per line\n```\n\n### Example: route VPN traffic\n\nSuppose you want traffic to `google.com` and `youtube.com` to go through a WireGuard interface (`wg0`).\n\n#### 1. Create the rule file\n\n```bash\nsudo mkdir -p /etc/resolved-nftset/resolved_route/vpn\nprintf 'google.com\\nyoutube.com\\n' | sudo tee /etc/resolved-nftset/resolved_route/vpn/rules.conf\n```\n\n#### 2. Load the nftables ruleset\n\nCreate `/etc/nftables/resolved-mark.nft`:\n\n```nft\ntable inet resolved_mark {\n    set vpn_v4 {\n        type ipv4_addr\n        flags timeout\n        timeout 1h\n        gc-interval 5m\n    }\n\n    set vpn_v6 {\n        type ipv6_addr\n        flags timeout\n        timeout 1h\n        gc-interval 5m\n    }\n\n    chain prerouting {\n        type filter hook prerouting priority mangle; policy accept;\n\n        ip  daddr @vpn_v4 meta mark set 0xFF\n        ip6 daddr @vpn_v6 meta mark set 0xFF\n    }\n}\n```\n\nLoad it:\n\n```bash\nsudo install -m 640 resolved-mark.nft /etc/nftables/resolved-mark.nft\nsudo nft -f /etc/nftables/resolved-mark.nft\n```\n\nAdd `include \"/etc/nftables/resolved-mark.nft\"` to your main nftables config.\n\n#### 3. Add policy routing rules\n\nMark `0xFF` (255) needs a routing rule that directs marked packets to the VPN:\n\n```bash\n# Create a routing table for VPN traffic (table number 100 is arbitrary)\necho \"100 vpn\" | sudo tee -a /etc/iproute2/rt_tables\n\n# Packets marked 0xFF go through table \"vpn\"\nsudo ip rule add fwmark 0xFF table vpn\n\n# All routes in the \"vpn\" table go out via wg0\nsudo ip route add default dev wg0 table vpn\n```\n\n#### 4. Start the service\n\n```bash\nsudo systemctl restart resolved-nftset\n```\n\nNow any DNS resolution for `google.com` or `youtube.com` will cause the daemon to add the resolved IPs to the `vpn_v4`/`vpn_v6` sets. Packets to those IPs get marked `0xFF` in prerouting, and the policy routing rule sends them through `wg0`.\n\n### Multiple sets\n\nYou can define multiple rule directories for different routing policies:\n\n```\n/etc/resolved-nftset/\n├── resolved_route/\n│   ├── vpn/\n│   │   └── rules.conf      # → vpn_v4, vpn_v6 sets → route via wg0\n│   └── tor/\n│       └── rules.conf      # → tor_v4, tor_v6 sets → route via tor\n└── resolved_block/\n    └── ads/\n        └── rules.conf      # → ads_v4, ads_v6 sets → drop in filter chain\n```\n\n### Rules file format\n\n- One hostname per line\n- Lines starting with `#` are comments\n- Blank lines are ignored\n- Matching is prefix-based (e.g., `google.com` matches `www.google.com`, `mail.google.com`, etc.)\n\n## Security notes\n\n- The service requires `CAP_NET_ADMIN` to write to nftables via Netlink.\n- The systemd unit uses `AmbientCapabilities=CAP_NET_ADMIN` so it can run as an unprivileged user.\n- All other systemd sandboxing options (`ProtectSystem=strict`, `MemoryDenyWriteExecute`, etc.) are enabled by default.\n\n## License\n\nMIT OR Apache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocalvoid%2Fresolved-nftset","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flocalvoid%2Fresolved-nftset","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flocalvoid%2Fresolved-nftset/lists"}