{"id":20849795,"url":"https://github.com/ulfox/nettrust","last_synced_at":"2025-05-12T04:30:58.109Z","repository":{"id":43488604,"uuid":"457955946","full_name":"ulfox/nettrust","owner":"ulfox","description":"Dynamic Outbound Firewall Authorizer","archived":false,"fork":false,"pushed_at":"2022-04-14T18:08:40.000Z","size":256,"stargazers_count":22,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-10T08:54:46.408Z","etag":null,"topics":["dns-proxy","firewall","nftables","security"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ulfox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-10T21:38:49.000Z","updated_at":"2024-12-20T20:33:24.000Z","dependencies_parsed_at":"2022-08-23T10:01:42.184Z","dependency_job_id":null,"html_url":"https://github.com/ulfox/nettrust","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfox%2Fnettrust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfox%2Fnettrust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfox%2Fnettrust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ulfox%2Fnettrust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ulfox","download_url":"https://codeload.github.com/ulfox/nettrust/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253675157,"owners_count":21945907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["dns-proxy","firewall","nftables","security"],"created_at":"2024-11-18T03:06:48.480Z","updated_at":"2025-05-12T04:30:57.818Z","avatar_url":"https://github.com/ulfox.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NetTrust: Dynamic Outbound Firewall Authorizer\n\nNetTrust is a Dynamic Outbound Firewall Authorizer. It uses a DNS as a source of truth to allow/deny outbund requests\n\n## Overview\n\nThe ideas is that we want to grant network access only to networks or hosts that we trust. Trusted networks and hosts are whitelisted in Output Netfilter Hook, while all others are rejected.\n\nTo increase security or privacy, we usually want to block outbound traffic to:\n\n- Blocked DNS Queries\n- Direct Network Communication (Static IP, no Query made)\n\nFor the first item in the list, this is known as DNS Black hole and is a secure way to narrow down network communication only to trusted domains. However, not all processes (or javascript functions for example) use DNS Queries. There are many who use static IPs to communicate with the outside world. For example, a javascript function could dynamically fetch a list of hosts during render and forward traffic to them. For such case, DNS Blackholes are worethless.\n\nFirewalls normally allow outbound access to all hosts but restrict inbound access to a selected few\n\n```bash\n    _________                                           _____________________\n   |         |   Direct Communication IPv4: x.x.x.x    |                     |\n   | Process |=--------------------------------------\u003e | OUTBOUND: Allow All |\n   |_________|                                         |_____________________|\n        |\n        |                                               _____________________________\n        |                                              |                             |\n        |\u003c--------------------------------------------=| INBOUND: Few or Established |\n                                                       |_____________________________|\n\n```\n\nThe allow all to public but filter inbound works well with servers. There we trust the services that we run and we control components in a more strict way\n\nBut what happens when we want to filter and increase security on hosts that are not as restricted as servers or on hosts that may do many things, like personal computers. We install packages often, visit different websites which exposes us to different kind of tracking (telemetries, etc) and risks (hostile apps, bad javascript functions sending traffic to hosts we can not easilly stop)\n\nThe problem here is is that it is hard to filter all good hosts due to:\n\n- big number of public IPs. The total number of IPv4 addresses may be small for the world, but is really huge to filter it in a list\n- hosts usually change IPs, which makes the management of a whitelist even harder\n\nA work around this issue (up to a way, because as always everything has its weaknesses), is to use DNS for traffic authorization.\n\n### DNS Authorizer\n\nDNS Authorizers are normal DNS hosts that we trust a lot. For example a local DNS service that we have configured to blacklist certain domains, or block all except some domains\n\nWith DNS Authorizer we can:\n\n- Use them to filter our unwanted domains   (most common, needs a list of bad hosts)\n- Use them to filter in only wanted domains (most secure, needs a lot more work    )\n\nBy using a DNS Authorizer we have the pros of a DNS Blackhole + easy filtering of IPs. All we need, is to block all outbound traffic and then allow only the traffic that DNS answers in the queries\n\nThis is how NetTrust works. It is small dns proxy with Netfilter management capabilities.\n\n```bash\n\n    ________________                             ____________                   ____________\n   |                |     Query: example.com    |            |     Forward     |            |\n   | Host Process A |=------------------------\u003e |  NetTrust  |=--------------\u003e | DNS Server |\n   |________________|                           |____________|                 |____________|\n\n```\n\nIn the above diagram queries are sent to NetTrust, and from there NetTrust forwards them to the DNS Server that either knows the question or has been configured to forward queries.\n\n\n```bash\n\n    ________________                             ____________                   ____________\n   |                |     Query: example.com    |            |     Reply OK    |            |\n   | Host Process A |=------------------------\u003e |  NetTrust  | \u003c--------------=| DNS Server |\n   |________________|                           |____________|                 |____________|\n            |                                          |\n            |                                          |\n            |                                          |\n            |                                          |\n            |                                          |         _____________\n            |                                          |        |             |\n            |                                          |=-----\u003e |  Netfilter  |\n            |                                                   |             |\n            |                                                    -------------\n     ______________                                                    |\n    |              |          x.x.x.x is whitelisted                   |\n    | OUTPUT HOOK  | \u003c------------------------------------------------=|\n    |______________|\n```\n\nOnce NetTrust receives a query response, it checks if there are any answers (hosts resolved). If there are, it proceeds by updating firewall rules (e.g. nftables) in order to allow network access to the resolved hosts. If there is no answer, or if the answer is **0.0.0.0**, no action is taken. In all cases, the dns reply is sent back to the requestor process after a firewall decision has been made (if any).\n\n### Authorized hosts TTL\n\nNetTrust by default does not enable TTL on authorized hosts. The max authorized time a host can get is the time that NetTrust runs. Once NetTrust exits gracefully, it will clear the authorized hosts.\n\nWe can enable however TTL on authorized hosts. By adding a TTL, NetTrust will allow communication to that host for as long as TTL is set. Once a host is expired and no session is active (see Conntrack section below), it will be removed from the authorized list and will be expected by the process that wants to continue communication to resolve the host via the DNS again.\n\n#### Conntrack: Session liveness and TTL\n\nAll sessions that have TTL enabled will be checked against two rules. The first rule is the TTL itself. If the host has not expired, nothing happens, if it has expired, then conntrack will be checked to ensure that no connection with the specific host is active. If a tuple contains the host, either in the src or dst, then the TTL will be renewed and the host will be checked again in the next expiration. If the host is not part of any conntrack connection, then the host will be removed from the cache and the firewall's authorized hosts set\n\n```bash\n     _______                                   _____________\n    |       |        Get Expired Hosts        |             |\n    | Cache |=------------------------------\u003e | TTL Checker |\n    |_______|                                 |_____________|\n                                                     |\n                                                     |\n                                           __________|__________\n                                          |                     |\n                                          | Has host X Expired? |\n                                          |_____________________|\n                                                     |\n                                                     | Yes\n     _______________                       __________|___________                   ___________\n    |               |               No    |                      |      Yes        |           |\n    |  De-Authorize | \u003c------------------=| Is connection active |=--------------\u003e | Renew TTL |\n    |_______________|                     |______________________|                 |___________|\n                                                     |\n                                                     | \n                                                     |\n     ___________                                     |\n    |           |    Get Active Connections          |\n    | Conntrack |=---------------------------------\u003e |\n    |___________|\n```\n\n## Build\n\nTo build NetTrust, simply issue:\n\n```bash\n    go build -o nettrust cmd/nettrust.go\n```\n\n## Run NetTrust\n\nNote: NetTrust needs to interact Netfilter, for that, it requires root access\n\nTo run NetTrust, issue `./nettrust  -fwd-addr \"someIP:53\" -listen-addr \"127.0.0.1:53 -config config.json\"`\n\n- listen-addr is the listening address that NetTrust will listen and forward dns queries\n- fwd-addr is the address of the DNS Server that NetTrust will use to resolve queries\n\nExample output\n\n```bash\nINFO[2022-02-12T20:40:16+02:00] Starting UDP DNS Server                       Component=\"DNS Server\" Stage=Init\nINFO[2022-02-12T20:40:16+02:00] Starting TCP DNS Server                       Component=\"DNS Server\" Stage=Init\nINFO[2022-02-12T20:40:16+02:00] Starting                                      Component=\"[UDP] DNSServer\" Stage=Init\nINFO[2022-02-12T20:40:16+02:00] Starging                                      Component=\"[TCP] DNSServer\" Stage=Init\n# Some time later\nINFO[2022-02-12T20:41:02+02:00] [Blocked] Question: example.com.   Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:02+02:00] [Not Handled] Question: example.com.federation.local. - Is this local?  Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:14+02:00] [PTR] Question: 247.1.168.192.in-addr.arpa. resolved to arph.federation.local.  Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:36+02:00] [Blocked] Question: api.removedButWasSomeDomainHere.         Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:37+02:00] [Blocked] Question: api.removedButWasSomeDomainHere.         Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:38+02:00] [Blocked] Question: api.removedButWasSomeDomainHere.         Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:41+02:00] [Blocked] Question: api.removedButWasSomeDomainHere.         Component=Firewall Stage=Authorizer\nINFO[2022-02-12T20:41:49+02:00] [Blocked] Question: api.removedButWasSomeDomainHere.         Component=Firewall Stage=Authorizer\n# Some time later when I did a git push\nINFO[2022-02-12T21:19:30+02:00] [Authorized] Question: github.com. Hosts: [140.82.121.4]  Component=Firewall Stage=Authorizer\n# Some time later when I did a git fetch\nINFO[2022-02-12T21:41:54+02:00] [Already Authorized] Question: github.com. Host: 140.82.121.3  Component=Firewall Stage=Authorizer\n```\n\nThe nftables authorized hosts set now looks like this\n\n```bash\ntable ip net-trust {\n\tset whitelist {\n\t\ttype ipv4_addr\n\t\telements = { 127.0.0.1, 192.168.178.21 }\n\t}\n\n\tset authorized {\n\t\ttype ipv4_addr\n\t\telements = { xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, xyz.xyz.xyz.xyz,\n\t\t\t     xyz.xyz.xyz.xyz, 140.82.121.3 }\n\t}\n\n\tchain authorized-output {\n\t\ttype filter hook output priority filter; policy drop;\n\t\tip daddr 127.0.0.0/8 counter packets 563 bytes 48587 accept\n\t\tip daddr 10.0.0.0/8 counter packets 0 bytes 0 accept\n\t\tip daddr 172.16.0.0/12 counter packets 0 bytes 0 accept\n\t\tip daddr 192.168.0.0/16 counter packets 273 bytes 20402 accept\n\t\tip daddr 100.64.0.0/10 counter packets 0 bytes 0 accept\n\t\tip daddr @whitelist accept\n\t\tip daddr @authorized accept\n\t\tcounter packets 23 bytes 2637 reject with icmp type net-unreachable\n\t}\n}\n```\n\nCheck options below for additional configuration\n\n### NetTrust options\n\nNetTrust accepts the follwoing options\n\n```bash\nUsage of ./bin/nettrust:\n  -authorized-ttl int\n    \tNumber of seconds a authorized host will be active before NetTrust expires it and expect a DNS query again (-1 do not expire)\n  -config string\n    \tPath to config.json\n  -dns-ttl-cache int\n    \tNumber of seconds dns queries stay in cache (-1 to disable caching)\n  -do-not-flush-authorized-hosts\n    \tDo not clean up the authorized hosts list on exit. Use this together with do-not-flush-table to keep the NetTrust table as is on exit\n  -do-not-flush-table\n    \tDo not clean up tables when NetTrust exists. Use this flag if you want to continue to deny communication when NetTrust has exited\n  -firewall-backend string\n    \tNetTrust firewall backend [nftables/iptables/iptables-nft] that will be used to interact with Netfilter (nftables is only supported for now)\n  -firewall-drop-input\n    \tIf enabled, NetTrust will drop input. Adds [ct state established,related accept] \u0026 ['lo' accept]. Should be enabled only when NetTrust runs in host\n  -firewall-type string\n    \tNetTrust firewall type. Supported types: OUTPUT (default), FORWARD. The type essentially tells NetTrust on which hook the rules will be added\n  -fwd-addr string\n    \tNetTrust forward dns address\n  -fwd-proto string\n    \tNetTrust dns forward protocol\n  -fwd-tls\n    \tEnable DoT. This expects that forward dns address supports DoT and fwd-proto is tcp\n  -fwd-tls-cert string\n    \tpath to certificate that will be used to validate forward dns hostname. If you do not set this, the the host root CAs will be used\n  -listen-addr string\n    \tNetTrust listen dns address\n  -listen-cert string\n    \tpath to certificate that will be used by the TCP DNS Service to serve DoT\n  -listen-cert-key string\n    \tpath to the private key that will be used by the TCP DNS Service to serve DoT\n  -listen-tls\n    \tEnable tls listener, tls listener works only with the TCP DNS Service, UDP will continue to serve in plaintext mode\n  -ttl-check-ticker int\n    \tHow often NetTrust should check the cache for expired authorized hosts (Checking is blocking, do not put small numbers)\n  -whitelist-loopback\n    \tLoopback network space 127.0.0.0/8 will be whitelisted (default true)\n  -whitelist-private\n    \tIf 10.0.0.0/8, 172.16.0.0/16, 192.168.0.0/16, 100.64.0.0/10 will be whitelisted (default true)\n```\n\n#### Config options\n\nYou can also use a json config to set options.\n\n```json\n{\n    \"whitelist\": {\n        \"networks\": [],\n        \"hosts\": []\n    },\n    \"blacklist\": {\n        \"networks\": [],\n        \"hosts\": []\n    },\n    \"fwdAddr\": \"192.168.178.21:53\", // Example address of local dns server\n    \"fwdProto\": \"udp\",\n    \"fwdCaCert\": \"\",\n    \"fwdTLS\": false,\n\n    \"listenAddr\": \"127.0.0.1:53\",\n    \"listenTLS\": false,\n    \"listenCert\": \"\",\n    \"listenCertKey\": \"\",\n\n    \"firewallBackend\": \"nftables\",\n    \"firewallType\": \"OUTPUT\",\n    \"firewallDropInput\": false,\n\n    \"dnsTTLCache\": -1,\n\n    \"whitelistLoEnabled\": true,\n    \"whitelistPrivateEnabled\": true,\n    \"ttl\": -1,\n    \"ttlInterval\": 30,\n    \"doNotFlushTable\": false, // Set this to true if you want to keep the rules and the chain when NetTrust has stopped\n    \"doNotFlushAuthorizedHosts\": false\n}\n```\n\n**Note**: Config file options have lower priority from flag options. For example, if you start NetTrust with `-fwd-tls` and you set `fwdTLS: false` in the config, NetTrust will use tls since flags have the highest priority \n\n##### Do Not Flush Table on Exit\n\n**Note**: As you can imagine, with this option set to true, you will not be able to access any host that is not part of a whitelisted option (hosts, networks). If you enabled this option and you wish to revert back, simply start NetTrust again with this option set to false and then exit\n\nIf you wish to keep the table's content on NetTrust exit, then pass either `-do-not-flush-table` via flags or `doNotFlushTable: true` via config. NetTrust on exit will clear only the authorized set, that is the set that is populated via resolved hosts.\n\nIt will keep:\n\n- The chain with the default policy to drop\n- The whitelisted hosts\n- The whitelisted networks\n- Final reject verdict\n\n### NetTrust ENV/Config whitelist / blacklist\n\nNote: Whitelisting, blacklisting should be done automatically via DNS proxy. This option should be used if you want to add custom entries\n\nWe can add hosts or networks to whitelist or blacklist by using\n\n- Environmental variables\n- config file `config.json`\n\nNote: Networks are evaluated first in the chain managed by NetTrust (for additional info, see NFTables Overview section at the end of this Readme)\n\n#### Using Environmental variables\n\nWhitelist a host by exporting `NET_TRUST_WHITELIST_HOSTS_\u003csomeHost\u003e=someIP`\n\n```bash\nexport NET_TRUST_WHITELIST_HOSTS_CUSTOM=192.168.1.1\n```\n\nWhitelist a network by exporting `NET_TRUST_WHITELIST_NETWORK_\u003csomeNetworkName\u003e=someNetwork`\n\n```bash\nexport NET_TRUST_WHITELIST_NETWORK_HOME=192.168.1.0/24\n```\n\nNote: blacklisting via env is not yet supported. Check config section in the next section to add blacklists\n\n#### Using Config file\n\nUse `config.json` to whitelist or blacklist hosts and networks\n\n```bash\n{\n    \"whitelist\": {\n        \"networks\": [],\n        \"hosts\": []\n    },\n    \"blacklist\": {\n        \"networks\": [],\n        \"hosts\": []\n    }\n}\n```\n\nBlacklisting instructs NetTrust to skip hosts that match the hostlist or are part of the network. Skipping is essentially blackist since chain's tailing policy is reject and chain's default policy is drop\n\n\n### NFTables chain overview\n\nNetTrust creates a table called `net-trust` and a chain called `authorized`. Inside the chain it also creates two sets\n\n- whitelist: populated by whitelisted hosts during init of NetTrust. This set should stay static during the lifetime of NetTrust (or unless new hosts are whitelisted)\n- authorized: this set is used to add authorized hosts. If TTL is set to `-1` then this set should only grow during the lifetime of NetTrust and emptied on exit (we empty to ensure we do not forget whitelisted hosts behind)\n\nExample of a populated table and chain. Here 127.0.0.1 and 192.168.178.21 are redundant since we whitelisted the networks that contain them, but were added because 127.0.0.1 was the listening address of NetTrust dns proxy and 192.168.178.21 is the IP of a local DNS Black hole. We always whitelist listening address and forward address to ensure that NetTrust will work without issues for cases where NetTrust is started with `-whitelist-private=false`\n\n```bash\ntable ip net-trust {\n\tset whitelist {\n\t\ttype ipv4_addr\n\t\telements = { 127.0.0.1, 192.168.178.21 }\n\t}\n\n\tset authorized {\n\t\ttype ipv4_addr\n\t}\n\n\tchain authorized-output {\n\t\ttype filter hook output priority filter; policy drop;\n\t\tip daddr 127.0.0.0/8 counter packets 2389 bytes 469802 accept\n\t\tip daddr 10.0.0.0/8 counter packets 0 bytes 0 accept\n\t\tip daddr 172.16.0.0/12 counter packets 0 bytes 0 accept\n\t\tip daddr 192.168.0.0/16 counter packets 807 bytes 66255 accept\n\t\tip daddr 100.64.0.0/10 counter packets 0 bytes 0 accept\n\t\tip daddr @whitelist accept \n\t\tip daddr @authorized accept\n\t\tcounter packets 14 bytes 1370 reject with icmp type net-unreachable\n\t}\n}\n```\n\nAs you may have noticed, there is no blacklist entry in the chain or in any set. This is because NetTrust uses deny all except firewall implementation. Blacklists are all hosts that are not resolved by the DNS Authority and the hosts added manually via the config file or env vars. The blacklisting is taking place in the DNS Proxy handler, there we check any returned results by the DNS Authority and skip them if they match a blacklist rule\n\n#### NFTables clean ruleset manually\n\nIf you need to remove NetTrust rules and chains manually, then please follow this section\n\n##### Remove rule\n\nTo remove a rule, first get the rule's handle number\n\n```bash\nsudo nft list table net-trust -a\n```\n\n```bash\ntable ip net-trust { # handle 5\n\tset whitelist { # handle 7\n\t\ttype ipv4_addr\n\t\telements = { 127.0.0.1, 192.168.178.21 }\n\t}\n\n\tset authorized { # handle 9\n\t\ttype ipv4_addr\n\t\telements = { 140.82.121.4 }\n\t}\n\n\tchain authorized-output { # handle 129\n\t\ttype filter hook output priority filter; policy drop;\n\t\tip daddr 127.0.0.0/8 counter packets 2174 bytes 196333 accept # handle 130\n\t\tip daddr 10.0.0.0/8 counter packets 0 bytes 0 accept # handle 131\n\t\tip daddr 172.16.0.0/12 counter packets 0 bytes 0 accept # handle 132\n\t\tip daddr 192.168.0.0/16 counter packets 105 bytes 8793 accept # handle 133\n\t\tip daddr 100.64.0.0/10 counter packets 0 bytes 0 accept # handle 134\n\t\tip daddr @whitelist accept # handle 135\n\t\tip daddr @authorized accept # handle 136\n\t\tcounter packets 90 bytes 8380 reject with icmp type net-unreachable # handle 137\n\t}\n}\n```\n\nTo remove rule `ip daddr 100.64.0.0/10 counter packets 0 bytes 0 accept # handle 134` as an example, issue\n\n```bash\nsudo nft delete rule net-trust authorized-output handle 134\n```\n\n##### Remove all rules from all chains in the table\n\nTo remove all rules from all chains, issue\n\n```bash\nsudo nft 'flush table net-trust'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulfox%2Fnettrust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fulfox%2Fnettrust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fulfox%2Fnettrust/lists"}