NetTrust: Dynamic Firewall
NetTrust is a Dynamic Firewall Authorizer. It uses a DNS as a source of truth to allow/deny outbund requests
Overview
The 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.
To increase security or privacy, we usually want to block outbound traffic to:
- Blocked DNS Queries
- Direct Network Communication (Static IP, no Query made)
For 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.
Firewalls normally allow outbound access to all hosts but restrict inbound access to a selected few
_________ _____________________
| | Direct Communication IPv4: x.x.x.x | |
| Process |=--------------------------------------> | OUTBOUND: Allow All |
|_________| |_____________________|
|
| _____________________________
| | |
|<--------------------------------------------=| INBOUND: Few or Established |
|_____________________________|
The 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
But 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)
The problem here is is that it is hard to filter all good hosts due to:
- 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
- hosts usually change IPs, which makes the management of a whitelist even harder
To work around this (up to a way, because as always everything has its weaknesses) is to use DNS for traffic authorization.
DNS Authorizer
DNS 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
With DNS Authorizer we can:
- Use them to filter our unwanted domains (most common, needs a list of bad hosts)
- Use them to filter in only wanted domains (most secure, needs a lot more work )
By 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 allow only the traffic that DNS answers in the queries
This is how NetTrust works. It is small dns proxy with Netfilter management capabilities.
________________ ____________ ____________
| | Query: x.x.x.x | | Forward | |
| Host Process A |=--------------------> | NetTrust |=--------------> | DNS Server |
|________________| |____________| |____________|
In 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.
________________ ____________ ____________
| | Query: x.x.x.x | | Reply OK | |
| Host Process A |=--------------------> | NetTrust | <--------------=| DNS Server |
|________________| |____________| |____________|
| |
| |
| |
| |
| | _____________
| | | |
| |=-----> | Netfilter |
| | |
| -------------
______________ |
| | x.x.x.x is whitelisted |
| OUTPUT HOOK | <--------------------------------------------=|
|______________|
Once NetTrust receives a query, 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).
Build
To build NetTrust, simply issue:
go build -o nettrust
Run NetTrust
Note: NetTrust needs to make changes on Netfilter, for that, it requires root access
To run NetTrust, issue ./nettrust -fwd-addr "someIP:53" -listen-addr "127.0.0.1:53"
- listen-addr is the listening address that NetTrust will listen and forward dns queries
- fwd-addr is the address of the DNS Server that NetTrust will use to resolve queries
NetTrust options
NetTrust accepts the follwoing options
-firewall-type string
NetTrust firewall type (nftables is only supported for now) (default "nftables")
-fwd-addr string
NetTrust forward dns address
-listen-addr string
NetTrust listen dns address
-whitelist-loopback
Loopback network space 127.0.0.0/8 will be whitelisted (default true)
-whitelist-private
If 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)
-whitelist-ttl int
Number of seconds a whitelisted host will be active before NetTrust expires it and expect a DNS query again (-1 do not expire) (default -1)
NetTrust ENV/Config whitelist / blacklist
Note: Whitelisting, blacklisting should be done automatically via DNS proxy. This option should be used if you want to add custom entries
We can add hosts or networks to whitelist or blacklist by using
- Environmental variables
- config file
config.json
Note: Networks are evaluated first in the chain managed by NetTrust (See NFTables Overview for additional info)
Using Environmental variables
Whitelist a host by exporting NET_TRUST_WHITELIST_HOSTS_<someHost>=someIP
export NET_TRUST_WHITELIST_HOSTS_CUSTOM=192.168.1.1
Whitelist a network by exporting NET_TRUST_WHITELIST_NETWORK_<someNetworkName>=someNetwork
export NET_TRUST_WHITELIST_NETWORK_HOME=192.168.1.0/24
Using Config file
Use config.json
to whitelist or blacklist hosts and networks
Note: This is expected to be in the root of the directory hosts NetTrust binary
{
"whitelist": {
"networks": [],
"hosts": []
},
"blacklist": {
"networks": [],
"hosts": []
}
}
NFTables chain overview
NetTrust creates a table called net-trust
and a chain called authorized
. Inside the chain it also creates two sets
- 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)
- 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)
Example 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
table ip net-trust {
set whitelist {
type ipv4_addr
elements = { 127.0.0.1, 192.168.178.21 }
}
set authorized {
type ipv4_addr
}
chain authorized-output {
type filter hook output priority filter; policy drop;
ip daddr 127.0.0.0/8 counter packets 2389 bytes 469802 accept
ip daddr 10.0.0.0/8 counter packets 0 bytes 0 accept
ip daddr 172.16.0.0/12 counter packets 0 bytes 0 accept
ip daddr 192.168.0.0/16 counter packets 807 bytes 66255 accept
ip daddr 100.64.0.0/10 counter packets 0 bytes 0 accept
ip daddr @whitelist accept
ip daddr @authorized accept
counter packets 14 bytes 1370 reject with icmp type net-unreachable
}
}
As 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