Skip to content

Commit

Permalink
feat: ability to disable "Special Use Domain Name" blocking (#1618)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: mapl <[email protected]>
  • Loading branch information
mapl and mapl authored Dec 8, 2024
1 parent aff2998 commit 51db8db
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 40 deletions.
4 changes: 2 additions & 2 deletions config/sudn.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ type SUDN struct {
// upstream or custom DNS, which come before SUDN in the resolver chain.
// Thus defaulting to `true` and returning NXDOMAIN here should not conflict.
RFC6762AppendixG bool `yaml:"rfc6762-appendixG" default:"true"`
Enable bool `yaml:"enable" default:"true"`
}

// IsEnabled implements `config.Configurable`.
func (c *SUDN) IsEnabled() bool {
// The Special Use RFCs are always active
return true
return c.Enable
}

// LogConfig implements `config.Configurable`.
Expand Down
20 changes: 19 additions & 1 deletion config/sudn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,27 @@ var _ = Describe("SUDNConfig", func() {
})

Describe("IsEnabled", func() {
It("is true", func() {
It("should be true by default", func() {
Expect(cfg.IsEnabled()).Should(BeTrue())
})

When("enabled", func() {
It("should be true", func() {
cfg := SUDN{
Enable: true,
}
Expect(cfg.IsEnabled()).Should(BeTrue())
})
})

When("disabled", func() {
It("should be false", func() {
cfg := SUDN{
Enable: false,
}
Expect(cfg.IsEnabled()).Should(BeFalse())
})
})
})

Describe("LogConfig", func() {
Expand Down
1 change: 1 addition & 0 deletions docs/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ specialUseDomains:
# optional: block recomended private TLDs
# default: true
rfc6762-appendixG: true
enable: true

# optional: configure extended client subnet (ECS) support
ecs:
Expand Down
82 changes: 45 additions & 37 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ configuration properties as [JSON](config.yml).

## Basic configuration

| Parameter | Type | Mandatory | Default value | Description |
| ------------------- | ------------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------- |
| certFile | path | no | | Path to cert and key file for SSL encryption (DoH and DoT); if empty, self-signed certificate is generated |
| keyFile | path | no | | Path to cert and key file for SSL encryption (DoH and DoT); if empty, self-signed certificate is generated |
| minTlsServeVersion | string | no | 1.2 | Minimum TLS version that the DoT and DoH server use to serve those encrypted DNS requests |
| connectIPVersion | enum (dual, v4, v6) | no | dual | IP version to use for outgoing connections (dual, v4, v6) |
| Parameter | Type | Mandatory | Default value | Description |
| ------------------ | ------------------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------- |
| certFile | path | no | | Path to cert and key file for SSL encryption (DoH and DoT); if empty, self-signed certificate is generated |
| keyFile | path | no | | Path to cert and key file for SSL encryption (DoH and DoT); if empty, self-signed certificate is generated |
| minTlsServeVersion | string | no | 1.2 | Minimum TLS version that the DoT and DoH server use to serve those encrypted DNS requests |
| connectIPVersion | enum (dual, v4, v6) | no | dual | IP version to use for outgoing connections (dual, v4, v6) |

!!! example

Expand All @@ -29,12 +29,12 @@ configuration properties as [JSON](config.yml).

All logging port are optional.

| Parameter | Type | Default value | Description |
| ----------- | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ports.dns | [IP]:port[,[IP]:port]* | 53 | Port(s) and optional bind ip address(es) to serve DNS endpoint (TCP and UDP). If you wish to specify a specific IP, you can do so such as `192.168.0.1:53`. Example: `53`, `:53`, `127.0.0.1:53,[::1]:53` |
| ports.tls | [IP]:port[,[IP]:port]* | | Port(s) and optional bind ip address(es) to serve DoT DNS endpoint (DNS-over-TLS). If you wish to specify a specific IP, you can do so such as `192.168.0.1:853`. Example: `83`, `:853`, `127.0.0.1:853,[::1]:853` |
| ports.http | [IP]:port[,[IP]:port]* | | Port(s) and optional bind ip address(es) to serve HTTP used for prometheus metrics, pprof, REST API, DoH... If you wish to specify a specific IP, you can do so such as `192.168.0.1:4000`. Example: `4000`, `:4000`, `127.0.0.1:4000,[::1]:4000` |
| ports.https | [IP]:port[,[IP]:port]* | | Port(s) and optional bind ip address(es) to serve HTTPS used for prometheus metrics, pprof, REST API, DoH... If you wish to specify a specific IP, you can do so such as `192.168.0.1:443`. Example: `443`, `:443`, `127.0.0.1:443,[::1]:443` |
| Parameter | Type | Default value | Description |
| ----------- | ----------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ports.dns | [IP]:port[,[IP]:port]\* | 53 | Port(s) and optional bind ip address(es) to serve DNS endpoint (TCP and UDP). If you wish to specify a specific IP, you can do so such as `192.168.0.1:53`. Example: `53`, `:53`, `127.0.0.1:53,[::1]:53` |
| ports.tls | [IP]:port[,[IP]:port]\* | | Port(s) and optional bind ip address(es) to serve DoT DNS endpoint (DNS-over-TLS). If you wish to specify a specific IP, you can do so such as `192.168.0.1:853`. Example: `83`, `:853`, `127.0.0.1:853,[::1]:853` |
| ports.http | [IP]:port[,[IP]:port]\* | | Port(s) and optional bind ip address(es) to serve HTTP used for prometheus metrics, pprof, REST API, DoH... If you wish to specify a specific IP, you can do so such as `192.168.0.1:4000`. Example: `4000`, `:4000`, `127.0.0.1:4000,[::1]:4000` |
| ports.https | [IP]:port[,[IP]:port]\* | | Port(s) and optional bind ip address(es) to serve HTTPS used for prometheus metrics, pprof, REST API, DoH... If you wish to specify a specific IP, you can do so such as `192.168.0.1:443`. Example: `443`, `:443`, `127.0.0.1:443,[::1]:443` |

!!! example

Expand All @@ -49,12 +49,12 @@ All logging port are optional.

All logging options are optional.

| Parameter | Type | Default value | Description |
| ------------- | -------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| log.level | enum (trace, debug, info, warn, error) | info | Log level |
| log.format | enum (text, json) | text | Log format (text or json). |
| log.timestamp | bool | true | Log timestamps (true or false). |
| log.privacy | bool | false | Obfuscate log output (replace all alphanumeric characters with *) for user sensitive data like request domains or responses to increase privacy. |
| Parameter | Type | Default value | Description |
| ------------- | -------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| log.level | enum (trace, debug, info, warn, error) | info | Log level |
| log.format | enum (text, json) | text | Log format (text or json). |
| log.timestamp | bool | true | Log timestamps (true or false). |
| log.privacy | bool | false | Obfuscate log output (replace all alphanumeric characters with \*) for user sensitive data like request domains or responses to increase privacy. |

!!! example

Expand Down Expand Up @@ -89,7 +89,6 @@ This applies to all of them. The default strategy is blocking.

For `init.strategy`, the "init" is testing the given resolvers for each group. The potentially fatal error, depending on the strategy, is if a group has no functional resolvers.


### Upstream Groups

To resolve a DNS query, blocky needs external public or private DNS resolvers. Blocky supports DNS resolvers with
Expand All @@ -101,9 +100,9 @@ following network protocols (net part of the resolver URL):

!!! hint

You can (and should!) configure multiple DNS resolvers.
You can (and should!) configure multiple DNS resolvers.
Per default blocky uses the `parallel_best` upstream strategy where blocky picks 2 random resolvers from the list for each query and
returns the answer from the fastest one.
returns the answer from the fastest one.

Each resolver must be defined as a string in following format: `[net:]host:[port][/path][#commonName]`.

Expand Down Expand Up @@ -198,7 +197,6 @@ Currently available strategies:
- 9.8.7.6
```


## Bootstrap DNS configuration

These DNS servers are used to resolve upstream DoH and DoT servers that are specified as host names, and list domains.
Expand Down Expand Up @@ -291,10 +289,11 @@ This configuration will also resolve any subdomain of the defined domain, recurs
CNAME records are supported by utilizing the `zone` parameter. The zone file is a multiline string containing a [DNS Zone File](https://en.wikipedia.org/wiki/Zone_file#Example_file).
For records defined using the `zone` parameter, the `customTTL` parameter is unused. Instead, the TTL is defined in the zone directly.
The following directives are supported in the zone file:
* `$ORIGIN` - sets the origin for relative domain names
* `$TTL` - sets the default TTL for records in the zone
* `$INCLUDE` - includes another zone file relative to the blocky executable
* `$GENERATE` - generates a range of records

- `$ORIGIN` - sets the origin for relative domain names
- `$TTL` - sets the default TTL for records in the zone
- `$INCLUDE` - includes another zone file relative to the blocky executable
- `$GENERATE` - generates a range of records

With the optional parameter `rewrite` you can replace domain part of the query with the defined part **before** the
resolver lookup is performed.
Expand Down Expand Up @@ -412,7 +411,7 @@ Each list in a group is a "source" and can be downloaded, read from a file, or i

The supported list formats are:

1. the well-known [Hosts format](https://en.wikipedia.org/wiki/Hosts_(file))
1. the well-known [Hosts format](<https://en.wikipedia.org/wiki/Hosts_(file)>)
2. one domain per line (plain domain list)
3. one wildcard per line
4. one regex per line
Expand Down Expand Up @@ -692,20 +691,20 @@ Configuration parameters:
To connect to a database, you must provide a URL like value for `target`. The exact format and supported parameters depends on the DB type.
Parsing is handled not by Blocky, but third-party libraries, therefore the full documentation is external.

| Database | Full docs | Format | Example |
| ---------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| MySQL | [Go MySQL driver Data Source Name](https://github.com/go-sql-driver/mysql#dsn-data-source-name) | `[username[:password]@][protocol[(host[:port])]]/dbname[?param1=value1[&paramN=valueN]]`| `username:password@tcp(localhost:3306)/blocky_query_log?timeout=15s`|
| PostgreSQL | [pgx.ParseConfig](https://pkg.go.dev/github.com/jackc/pgx/v5/pgconn#ParseConfig) | `postgres://[username[:password]@][host[:port]]/dbname[?param1=value1[&paramN=valueN]]` | `postgres://username@localhost:5432/blocky_query_log` |
| Timescale | See PostgreSQL above | | |
| Database | Full docs | Format | Example |
| ---------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| MySQL | [Go MySQL driver Data Source Name](https://github.com/go-sql-driver/mysql#dsn-data-source-name) | `[username[:password]@][protocol[(host[:port])]]/dbname[?param1=value1[&paramN=valueN]]` | `username:password@tcp(localhost:3306)/blocky_query_log?timeout=15s` |
| PostgreSQL | [pgx.ParseConfig](https://pkg.go.dev/github.com/jackc/pgx/v5/pgconn#ParseConfig) | `postgres://[username[:password]@][host[:port]]/dbname[?param1=value1[&paramN=valueN]]` | `postgres://username@localhost:5432/blocky_query_log` |
| Timescale | See PostgreSQL above | | |

!!! note

For increased security, it is recommended to configure the password for a PostgreSQL/Timescale connection via the `PGPASSFILE` environment variable.
For increased security, it is recommended to configure the password for a PostgreSQL/Timescale connection via the `PGPASSFILE` environment variable.

### Examples

!!! example
**CSV format with limited logging information**
**CSV format with limited logging information**

```yaml
queryLog:
Expand Down Expand Up @@ -753,7 +752,6 @@ Configuration parameters:
strategy: fast
```


## Deliver EDE codes as EDNS0 option

DNS responses can be extended with EDE codes according to [RFC8914](https://datatracker.ietf.org/doc/rfc8914/).
Expand Down Expand Up @@ -792,14 +790,17 @@ EDNS Client Subnet (ECS) configuration parameters:

## Special Use Domain Names

SUDN (Special Use Domain Names) are always enabled as they are required by various RFCs.
SUDN (Special Use Domain Names) are always enabled by default as they are required by various RFCs.
Some RFCs have optional recommendations, which are configurable as described below.
However, you can completely deactivate the blocking of SUDN by setting enable to false.
Warning! You should only disable this if your upstream DNS server is local, as it shouldn't be disabled for remote upstreams.

Configuration parameters:

| Parameter | Type | Mandatory | Default value | Description |
| ----------------------------------- | ---- | --------- | ------------- | --------------------------------------------------------------------------------------------- |
| specialUseDomains.rfc6762-appendixG | bool | no | true | Block TLDs listed in [RFC 6762 Appendix G](https://www.rfc-editor.org/rfc/rfc6762#appendix-G) |
| enable | bool | no | true | completely disable or enable SUDN blocking |

!!! example

Expand All @@ -808,6 +809,13 @@ Configuration parameters:
rfc6762-appendixG: true
```

!!! example

```yaml
specialUseDomains:
enable: false
```

## SSL certificate configuration (DoH / TLS listener)

See [Wiki - Configuration of HTTPS](https://github.com/0xERR0R/blocky/wiki/Configuration-of-HTTPS-for-DoH-and-Rest-API)
Expand Down Expand Up @@ -932,5 +940,5 @@ Default value is 4.
!!! note

As with other settings under `loading`, the limit applies to the blocking and hosts file resolvers separately.
The total number of concurrent sources concurrently processed can reach the sum of both values.
The total number of concurrent sources concurrently processed can reach the sum of both values.
For example if blocking has a limit set to 8 and hosts file's is 4, there could be up to 12 concurrent jobs.
4 changes: 4 additions & 0 deletions resolver/sudn_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ func NewSpecialUseDomainNamesResolver(cfg config.SUDN) *SpecialUseDomainNamesRes
}

func (r *SpecialUseDomainNamesResolver) Resolve(ctx context.Context, request *model.Request) (*model.Response, error) {
if !r.cfg.Enable {
return r.next.Resolve(ctx, request)
}

handler := r.handler(request)
if handler != nil {
resp := handler(request, r.cfg)
Expand Down
18 changes: 18 additions & 0 deletions resolver/sudn_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ var _ = Describe("SudnResolver", Label("sudnResolver"), func() {
return Entry(description, args...)
}

It("should be true by default", func() {
Expect(sutConfig.IsEnabled()).Should(BeTrue())
})

When("enabled", func() {
It("should be true", func() {
sutConfig.Enable = true
Expect(sutConfig.IsEnabled()).Should(BeTrue())
})
})

When("disabled", func() {
It("should be false", func() {
sutConfig.Enable = false
Expect(sutConfig.IsEnabled()).Should(BeFalse())
})
})

DescribeTable("handled domains",
func(qType dns.Type, qName string, expectedRCode int, extraMatchers ...types.GomegaMatcher) {
resp, err := sut.Resolve(ctx, newRequest(qName, qType))
Expand Down

0 comments on commit 51db8db

Please sign in to comment.