Skip to content

Commit

Permalink
Support inverting the behavior of response-blocklist-ip / -name (#344)
Browse files Browse the repository at this point in the history
* Support inverting the behavior of response-blocklist-ip / -name

* undo test code

* undo test code

* Fix nil ptr panic
  • Loading branch information
folbricht committed Sep 26, 2023
1 parent d193dcf commit 2e0f426
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 7 deletions.
14 changes: 14 additions & 0 deletions blocklistdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,17 @@ type BlocklistMatch struct {
List string // Identifier or name of the blocklist
Rule string // Identifier for the rule that matched
}

func (m *BlocklistMatch) GetList() string {
if m == nil {
return "none"
}
return m.List
}

func (m *BlocklistMatch) GetRule() string {
if m == nil {
return "none"
}
return m.Rule
}
1 change: 1 addition & 0 deletions cmd/routedns/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ type group struct {
AllowlistSource []list `toml:"allowlist-source"`
AllowlistRefresh int `toml:"allowlist-refresh"`
LocationDB string `toml:"location-db"` // GeoIP database file for response blocklist. Default "/usr/share/GeoIP/GeoLite2-City.mmdb"
Inverted bool // Only allow IPs on the blocklist. Supported in response-blocklist-ip and response-blocklist-name

// Static responder options
Answer []string
Expand Down
2 changes: 2 additions & 0 deletions cmd/routedns/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
BlocklistDB: blocklistDB,
BlocklistRefresh: time.Duration(g.BlocklistRefresh) * time.Second,
Filter: g.Filter,
Inverted: g.Inverted,
}
resolvers[id], err = rdns.NewResponseBlocklistIP(id, gr[0], opt)
if err != nil {
Expand Down Expand Up @@ -698,6 +699,7 @@ func instantiateGroup(id string, g group, resolvers map[string]rdns.Resolver) er
BlocklistResolver: resolvers[g.BlockListResolver],
BlocklistDB: blocklistDB,
BlocklistRefresh: time.Duration(g.BlocklistRefresh) * time.Second,
Inverted: g.Inverted,
}
resolvers[id], err = rdns.NewResponseBlocklistName(id, gr[0], opt)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ Rather than filtering queries, response blocklists evaluate the response to a qu

The configuration options of response blocklists are very similar to that of [query blocklists](#Query-Blocklist) with the exception of the `allowlists-*` options which are not supported in response blocklists.

Query blocklists are instantiated with `type = "response-blocklist-ip"` or `type = "response-blocklist-name"` in the groups section of the configuration.
Response blocklists are instantiated with `type = "response-blocklist-ip"` or `type = "response-blocklist-name"` in the groups section of the configuration.

Options:

Expand All @@ -715,6 +715,7 @@ Options:
- `blocklist-refresh` - Time interval (in seconds) in which external (remote or local) blocklists are reloaded. Optional.
- `blocklist-source` - An array of blocklists, each with `format`, `source` and optionally `cache-dir` (see notes for [Query Blockists](#Query-Blocklist)) as well as `name` which assigns a name to the list used in logs (defaults to `source`).
- `filter` - If set to `true` in `response-blocklist-ip`, matching records will be removed from responses rather than the whole response. If there is no answer record left after applying the filter, NXDOMAIN will be returned unless an alternative `blocklist-resolver` is defined.
- `inverted` - Inverts the behavior of the blocklist. If set to `true`, only IPs that are on the blocklist are allowed and responses containing an IP not on the blocklist are blocked. Can be combined with `filter` to remove any IPs not on the blocklist from the response.
- `location-db` - If location-based IP blocking is used, this specifies the GeoIP data file to load. Optional. Defaults to /usr/share/GeoIP/GeoLite2-City.mmdb

Location-based blocking requires a list of GeoName IDs of geographical entities (Continent, Country, City or Subdivision) and the GeoName ID, like `2750405` for Netherlands. The GeoName ID can be looked up in [https://www.geonames.org/](https://www.geonames.org/). Locations are read from a MAXMIND GeoIP2 database that either has to be present in `/usr/share/GeoIP/GeoLite2-City.mmdb` or is configured with the `location-db` option.
Expand Down
11 changes: 7 additions & 4 deletions response-blocklist-ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type ResponseBlocklistIPOptions struct {
// If true, removes matching records from the response rather than replying with NXDOMAIN. Can
// not be combined with alternative blockist-resolver
Filter bool

// Inverted behavior, only allow responses that can be found on at least one list.
Inverted bool
}

// NewResponseBlocklistIP returns a new instance of a response blocklist resolver.
Expand Down Expand Up @@ -103,8 +106,8 @@ func (r *ResponseBlocklistIP) blockIfMatch(query, answer *dns.Msg, ci ClientInfo
default:
continue
}
if match, ok := r.BlocklistDB.Match(ip); ok {
log := logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.List, "rule": match.Rule, "ip": ip})
if match, ok := r.BlocklistDB.Match(ip); ok != r.Inverted {
log := logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.GetList(), "rule": match.GetRule(), "ip": ip})
if r.BlocklistResolver != nil {
log.WithField("resolver", r.BlocklistResolver).Debug("blocklist match, forwarding to blocklist-resolver")
return r.BlocklistResolver.Resolve(query, ci)
Expand Down Expand Up @@ -147,8 +150,8 @@ func (r *ResponseBlocklistIP) filterRR(query *dns.Msg, ci ClientInfo, rrs []dns.
newRRs = append(newRRs, rr)
continue
}
if match, ok := r.BlocklistDB.Match(ip); ok {
logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.List, "rule": match.Rule, "ip": ip}).Debug("filtering response")
if match, ok := r.BlocklistDB.Match(ip); ok != r.Inverted {
logger(r.id, query, ci).WithFields(logrus.Fields{"list": match.GetList(), "rule": match.GetRule(), "ip": ip}).Debug("filtering response")
continue
}
newRRs = append(newRRs, rr)
Expand Down
7 changes: 5 additions & 2 deletions response-blocklist-name.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type ResponseBlocklistNameOptions struct {

// Refresh period for the blocklist. Disabled if 0.
BlocklistRefresh time.Duration

// Inverted behavior, only allow responses that can be found on at least one list.
Inverted bool
}

// NewResponseBlocklistName returns a new instance of a response blocklist resolver.
Expand Down Expand Up @@ -87,8 +90,8 @@ func (r *ResponseBlocklistName) blockIfMatch(query, answer *dns.Msg, ci ClientIn
default:
continue
}
if _, _, rule, ok := r.BlocklistDB.Match(dns.Question{Name: name}); ok {
log := logger(r.id, query, ci).WithField("rule", rule.Rule)
if _, _, rule, ok := r.BlocklistDB.Match(dns.Question{Name: name}); ok != r.Inverted {
log := logger(r.id, query, ci).WithField("rule", rule.GetRule())
if r.BlocklistResolver != nil {
log.WithField("resolver", r.BlocklistResolver).Debug("blocklist match, forwarding to blocklist-resolver")
return r.BlocklistResolver.Resolve(query, ci)
Expand Down

0 comments on commit 2e0f426

Please sign in to comment.