Change configuration format for duration (#263)

This commit is contained in:
Dimitri Herzog 2021-09-12 21:17:32 +02:00
parent ee8f041938
commit 91b975b0dc
12 changed files with 311 additions and 50 deletions

View File

@ -10,9 +10,9 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/0xERR0R/blocky/log"
"gopkg.in/yaml.v2"
)
@ -33,6 +33,8 @@ type NetProtocol uint16
// )
type QueryLogType int16
type Duration time.Duration
const (
validUpstream = `(?P<Host>(?:\[[^\]]+\])|[^\s/:]+):?(?P<Port>[^\s/:]*)?(?P<Path>/[^\s]*)?`
)
@ -127,6 +129,29 @@ func (c *CustomDNSMapping) UnmarshalYAML(unmarshal func(interface{}) error) erro
return nil
}
// UnmarshalYAML creates Duration from YAML. If no unit is used, uses minutes
func (c *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var input string
if err := unmarshal(&input); err != nil {
return err
}
if minutes, err := strconv.Atoi(input); err == nil {
// duration is defined as number without unit
// use minutes to ensure back compatibility
*c = Duration(time.Duration(minutes) * time.Minute)
return nil
}
duration, err := time.ParseDuration(input)
if err == nil {
*c = Duration(duration)
return nil
}
return err
}
// ParseUpstream creates new Upstream from passed string in format [net]:host[:port][/path]
func ParseUpstream(upstream string) (result Upstream, err error) {
if strings.TrimSpace(upstream) == "" {
@ -262,8 +287,8 @@ type BlockingConfig struct {
WhiteLists map[string][]string `yaml:"whiteLists"`
ClientGroupsBlock map[string][]string `yaml:"clientGroupsBlock"`
BlockType string `yaml:"blockType"`
BlockTimeSec int `yaml:"blockTTL"`
RefreshPeriod int `yaml:"refreshPeriod"`
BlockTTL Duration `yaml:"blockTTL"`
RefreshPeriod Duration `yaml:"refreshPeriod"`
}
// ClientLookupConfig configuration for the client lookup
@ -275,13 +300,13 @@ type ClientLookupConfig struct {
// CachingConfig configuration for domain caching
type CachingConfig struct {
MinCachingTime int `yaml:"minTime"`
MaxCachingTime int `yaml:"maxTime"`
MaxItemsCount int `yaml:"maxItemsCount"`
Prefetching bool `yaml:"prefetching"`
PrefetchExpires int `yaml:"prefetchExpires"`
PrefetchThreshold int `yaml:"prefetchThreshold"`
PrefetchMaxItemsCount int `yaml:"prefetchMaxItemsCount"`
MinCachingTime Duration `yaml:"minTime"`
MaxCachingTime Duration `yaml:"maxTime"`
MaxItemsCount int `yaml:"maxItemsCount"`
Prefetching bool `yaml:"prefetching"`
PrefetchExpires Duration `yaml:"prefetchExpires"`
PrefetchThreshold int `yaml:"prefetchThreshold"`
PrefetchMaxItemsCount int `yaml:"prefetchMaxItemsCount"`
}
// QueryLogConfig configuration for the query logging
@ -316,7 +341,11 @@ func LoadConfig(path string, mandatory bool) {
log.Log().Fatal("Can't read config file: ", err)
}
err = yaml.UnmarshalStrict(data, &cfg)
unmarshalConfig(data, cfg)
}
func unmarshalConfig(data []byte, cfg Config) {
err := yaml.UnmarshalStrict(data, &cfg)
if err != nil {
log.Log().Fatal("wrong file structure: ", err)
}

View File

@ -4,6 +4,7 @@ import (
"io/ioutil"
"net"
"os"
"time"
"github.com/0xERR0R/blocky/helpertest"
@ -41,9 +42,11 @@ var _ = Describe("Config", func() {
Expect(config.Blocking.BlackLists).Should(HaveLen(2))
Expect(config.Blocking.WhiteLists).Should(HaveLen(1))
Expect(config.Blocking.ClientGroupsBlock).Should(HaveLen(2))
Expect(config.Blocking.BlockTTL).Should(Equal(Duration(time.Minute)))
Expect(config.Blocking.RefreshPeriod).Should(Equal(Duration(2 * time.Hour)))
Expect(config.Caching.MaxCachingTime).Should(Equal(0))
Expect(config.Caching.MinCachingTime).Should(Equal(0))
Expect(config.Caching.MaxCachingTime).Should(Equal(Duration(0)))
Expect(config.Caching.MinCachingTime).Should(Equal(Duration(0)))
Expect(GetConfig()).Should(Not(BeNil()))
@ -65,6 +68,66 @@ var _ = Describe("Config", func() {
})
})
})
When("duration is in wrong format", func() {
It("should log with fatal and exit", func() {
cfg := Config{}
data :=
`blocking:
refreshPeriod: wrongduration`
helpertest.ShouldLogFatal(func() {
unmarshalConfig([]byte(data), cfg)
})
})
})
When("CustomDNS hast wrong IP defined", func() {
It("should log with fatal and exit", func() {
cfg := Config{}
data :=
`customDNS:
mapping:
someDomain: 192.168.178.WRONG`
helpertest.ShouldLogFatal(func() {
unmarshalConfig([]byte(data), cfg)
})
})
})
When("Conditional mapping hast wrong defined upstreams", func() {
It("should log with fatal and exit", func() {
cfg := Config{}
data :=
`conditional:
mapping:
multiple.resolvers: udp:192.168.178.1,wongprotocol:4.4.4.4:53`
helpertest.ShouldLogFatal(func() {
unmarshalConfig([]byte(data), cfg)
})
})
})
When("Wrong upstreams are defined", func() {
It("should log with fatal and exit", func() {
cfg := Config{}
data :=
`upstream:
default:
- udp:8.8.8.8
- wrongprotocol:8.8.4.4
- udp:1.1.1.1`
helpertest.ShouldLogFatal(func() {
unmarshalConfig([]byte(data), cfg)
})
})
})
When("config is not YAML", func() {
It("should log with fatal and exit", func() {
cfg := Config{}
data :=
`///`
helpertest.ShouldLogFatal(func() {
unmarshalConfig([]byte(data), cfg)
})
})
})
When("deprecated querylog.dir parameter is used", func() {
It("should be mapped to csv writer", func() {
@ -94,6 +157,7 @@ var _ = Describe("Config", func() {
})
})
When("config directory does not exist", func() {
It("should log with fatal and exit if config is mandatory", func() {
err := os.Chdir("../..")

View File

@ -70,18 +70,21 @@ blocking:
# nxDomain: return NXDOMAIN as return code
# comma separated list of destination IP adresses (for example: 192.100.100.15, 2001:0db8:85a3:08d3:1319:8a2e:0370:7344). Should contain ipv4 and ipv6 to cover all query types. Useful with running web server on this address to display the "blocked" page.
blockType: zeroIp
# optional: automatically list refresh period in minutes. Default: 4h.
# optional: TTL for answers to blocked domains
# default: 6h
blockTTL: 1m
# optional: automatically list refresh period (in duration format). Default: 4h.
# Negative value -> deactivate automatically refresh.
# 0 value -> use default
refreshPeriod: 0
refreshPeriod: 4h
# optional: configuration for caching of DNS responses
caching:
# amount in minutes, how long a response must be cached (min value).
# duration how long a response must be cached (min value).
# If <=0, use response's TTL, if >0 use this value, if TTL is smaller
# Default: 0
minTime: 5
# amount in minutes, how long a response must be cached (max value).
minTime: 5m
# duration how long a response must be cached (max value).
# If <0, do not cache responses
# If 0, use TTL
# If > 0, use this value, if TTL is greater
@ -94,9 +97,9 @@ caching:
# this improves the response time for often used queries, but significantly increases external traffic
# default: false
prefetching: true
# amount in minutes, prefetch track time window
# prefetch track time window (in duration format)
# default: 120
prefetchExpires: 120
prefetchExpires: 2h
# name queries threshold for prefetch
# default: 5
prefetchThreshold: 5

View File

@ -3,6 +3,163 @@
This chapter describes all configuration options in `config.yaml`. You can download a reference file with all
configuration properties as [JSON](config.yml).
??? example "reference configuration file"
```yaml upstream:
# these external DNS resolvers will be used. Blocky picks 2 random resolvers from the list for each query # format for
resolver: [net:]host:[port][/path]. net could be empty (default, shortcut for tcp+udp), tcp+udp, tcp, udp, tcp-tls or
https (DoH). If port is empty, default port will be used (53 for udp and tcp, 853 for tcp-tls, 443 for https (Doh))
# this configuration is mandatory, please define at least one external DNS resolver default:
- 46.182.19.48 - 80.241.218.68 - tcp-tls:fdns1.dismail.de:853 - https://dns.digitale-gesellschaft.ch/dns-query
# optional: use client name (with wildcard support: * - sequence of any characters, [0-9] - range)
# or single ip address / client subnet as CIDR notation laptop*:
- 123.123.123.123
# optional: custom IP address(es) for domain name (with all sub-domains). Multiple addresses must be separated by a comma
# example: query "printer.lan" or "my.printer.lan" will return 192.168.178.3
customDNS:
mapping:
printer.lan: 192.168.178.3,2001:0db8:85a3:08d3:1319:8a2e:0370:7344
# optional: definition, which DNS resolver(s) should be used for queries to the domain (with all sub-domains). Multiple resolvers must be separated by a comma
# Example: Query client.fritz.box will ask DNS server 192.168.178.1. This is necessary for local network, to resolve clients by host name
conditional:
# optional: replace domain in the query with other domain before resover lookup in the mapping
rewrite:
example.com: fritz.box
mapping:
fritz.box: udp:192.168.178.1
lan.net: udp:192.168.178.1,udp:192.168.178.2
# optional: use black and white lists to block queries (for example ads, trackers, adult pages etc.)
blocking:
# definition of blacklist groups. Can be external link (http/https) or local file
blackLists:
ads:
- https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
- https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
- https://mirror1.malwaredomains.com/files/justdomains
- http://sysctl.org/cameleon/hosts
- https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist
- https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
- |
# inline definition with YAML literal block scalar style
# hosts format
someadsdomain.com
special:
- https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews/hosts
# definition of whitelist groups. Attention: if the same group has black and whitelists, whitelists will be used to disable particular blacklist entries. If a group has only whitelist entries -> this means only domains from this list are allowed, all other domains will be blocked
whiteLists:
ads:
- whitelist.txt
- |
# inline definition with YAML literal block scalar style
# hosts format
whitelistdomain.com
# definition: which groups should be applied for which client
clientGroupsBlock:
# default will be used, if no special definition for a client name exists
default:
- ads
- special
# use client name (with wildcard support: * - sequence of any characters, [0-9] - range)
# or single ip address / client subnet as CIDR notation
laptop*:
- ads
192.168.178.1/24:
- special
# which response will be sent, if query is blocked:
# zeroIp: 0.0.0.0 will be returned (default)
# nxDomain: return NXDOMAIN as return code
# comma separated list of destination IP adresses (for example: 192.100.100.15, 2001:0db8:85a3:08d3:1319:8a2e:0370:7344). Should contain ipv4 and ipv6 to cover all query types. Useful with running web server on this address to display the "blocked" page.
blockType: zeroIp
# optional: TTL for answers to blocked domains
# default: 6h
blockTTL: 1m
# optional: automatically list refresh period (in duration format). Default: 4h.
# Negative value -> deactivate automatically refresh.
# 0 value -> use default
refreshPeriod: 4h
# optional: configuration for caching of DNS responses
caching:
# duration how long a response must be cached (min value).
# If <=0, use response's TTL, if >0 use this value, if TTL is smaller
# Default: 0
minTime: 5m
# duration how long a response must be cached (max value).
# If <0, do not cache responses
# If 0, use TTL
# If > 0, use this value, if TTL is greater
# Default: 0
maxTime: -1
# Max number of cache entries (responses) to be kept in cache (soft limit). Useful on systems with limited amount of RAM.
# Default (0): unlimited
maxItemsCount: 0
# if true, will preload DNS results for often used queries (default: names queried more than 5 times in a 2 hour time window)
# this improves the response time for often used queries, but significantly increases external traffic
# default: false
prefetching: true
# prefetch track time window (in duration format)
# default: 120
prefetchExpires: 2h
# name queries threshold for prefetch
# default: 5
prefetchThreshold: 5
# Max number of domains to be kept in cache for prefetching (soft limit). Useful on systems with limited amount of RAM.
# Default (0): unlimited
prefetchMaxItemsCount: 0
# optional: configuration of client name resolution
clientLookup:
# optional: this DNS resolver will be used to perform reverse DNS lookup (typically local router)
upstream: udp:192.168.178.1
# optional: some routers return multiple names for client (host name and user defined name). Define which single name should be used.
# Example: take second name if present, if not take first name
singleNameOrder:
- 2
- 1
# optional: custom mapping of client name to IP addresses. Useful if reverse DNS does not work properly or just to have custom client names.
clients:
laptop:
- 192.168.178.29
# optional: configuration for prometheus metrics endpoint
prometheus:
# enabled if true
enable: true
# url path, optional (default '/metrics')
path: /metrics
# optional: write query information (question, answer, client, duration etc) to daily csv file
queryLog:
# optional one of: mysql, csv, csv-client. If empty, log to console
type: mysql
# directory (should be mounted as volume in docker) for csv, db connection string for mysql
target: db_user:db_password@tcp(db_host_or_ip:3306)/db_user?charset=utf8mb4&parseTime=True&loc=Local
# if > 0, deletes log files which are older than ... days
logRetentionDays: 7
# optional: DNS listener port and bind ip address, default 53 (UDP and TCP). Example: 53, :53, 127.0.0.1:53
port: 53
# optional: HTTPS listener port and bind ip address, default empty = no http listener. If > 0, will be used for prometheus metrics, pprof, REST API, DoH... Example: 443, :443, 127.0.0.1:443
httpPort: 4000
#httpsPort: 443
# mandatory, if https port > 0: path to cert and key file for SSL encryption
#httpsCertFile: server.crt
#httpsKeyFile: server.key
# optional: use this DNS server to resolve blacklist urls and upstream DNS servers. Useful if no DNS resolver is configured and blocky needs to resolve a host name. Format net:IP:port, net must be udp or tcp
bootstrapDns: tcp:1.1.1.1
# optional: Drop all AAAA query if set to true. Default: false
disableIPv6: false
# optional: Log level (one from debug, info, warn, error). Default: info
logLevel: info
# optional: Log format (text or json). Default: text
logFormat: text
# optional: log timestamps. Default: true
logTimestamp: true
# optional: obfuscate log output (replace all alphanumeric characters with *) for user sensitive data like request domains or responses to increase privacy. Default: false
logPrivacy: false
```
## Basic configuration
| Parameter | Mandatory | Default value | Description |
@ -270,28 +427,29 @@ default** block type. Server returns 0.0.0.0 (or :: for IPv6) as result for A an
### Block TTL
TTL for answers to blocked domains can be set to customize the time clients ask for those domains again.
This setting only makes sense when `blockType` is set to `nxDomain` or `zeroIP`, and will affect how much time it could take for a client to be able to see the real IP address for a domain after receiving the custom value.
TTL for answers to blocked domains can be set to customize the time (in **duration format**) clients ask for those
domains again. This setting only makes sense when `blockType` is set to `nxDomain` or `zeroIP`, and will affect how much
time it could take for a client to be able to see the real IP address for a domain after receiving the custom value.
!!! example
```yaml
blocking:
blockType: 192.100.100.15, 2001:0db8:85a3:08d3:1319:8a2e:0370:7344
blockTTL: 10
blockTTL: 10s
```
### List refresh period
To keep the list cache up-to-date, blocky will periodically download and reload all external lists. Default period is **
4 hours**. You can configure this by setting the `blocking.refreshPeriod` parameter to a value in **minutes**. Negative
value will deactivate automatically refresh.
4 hours**. You can configure this by setting the `blocking.refreshPeriod` parameter to a value in **duration format**.
Negative value will deactivate automatically refresh.
!!! example
```yaml
blocking:
refreshPeriod: 60
refreshPeriod: 60m
```
Refresh every hour.
@ -311,11 +469,13 @@ With following parameters you can tune the caching behavior:
| Parameter | Mandatory | Default value | Description |
| ----------------------------- | --------- | -------------------| ------------------------------------------------- |
| caching.minTime | no | 0 (use TTL) | Amount in minutes, how long a response must be cached (min value). If <=0, use response's TTL, if >0 use this value, if TTL is smaller |
| caching.maxTime | no | 0 (use TTL) | Amount in minutes, how long a response must be cached (max value). If <0, do not cache responses. If 0, use TTL. If > 0, use this value, if TTL is greater |
| caching.minTime | no | 0 (use TTL) | How long (in **duration
format**) a response must be cached (min value). If <=0, use response's TTL, if >0 use this value, if TTL is smaller |
| caching.maxTime | no | 0 (use TTL) | How long (in **duration
format**) a response must be cached (max value). If <0, do not cache responses. If 0, use TTL. If > 0, use this value, if TTL is greater |
| caching.maxItemsCount | no | 0 (unlimited) | Max number of cache entries (responses) to be kept in cache (soft limit). Default (0): unlimited. Useful on systems with limited amount of RAM. |
| caching.prefetching | no | false | if true, blocky will preload DNS results for often used queries (default: names queried more than 5 times in a 2 hour time window). Results in cache will be loaded again on their expire (TTL). This improves the response time for often used queries, but significantly increases external traffic. It is recommended to increase "minTime" to reduce the number of prefetch queries to external resolvers. |
| caching.prefetchExpires | no | 120 | Amount in minutes, prefetch track time window
| caching.prefetchExpires | no | 2h | Prefetch track time window (in **duration format**)
| caching.prefetchThreshold | no | 5 | Name queries threshold for prefetch
| caching.prefetchMaxItemsCount | no | 0 (unlimited) | Max number of domains to be kept in cache for prefetching (soft limit). Default (0): unlimited. Useful on systems with limited amount of RAM. |
@ -323,8 +483,8 @@ With following parameters you can tune the caching behavior:
```yaml
caching:
minTime: 5
maxTime: 30
minTime: 5m
maxTime: 30m
prefetching: true
```

View File

@ -21,4 +21,6 @@
*[SSL]: Secure Sockets Layer
*[CSV]: Comma-separated values
*[SAMBA]: Server Message Block Protocol (Windows Network File System)
*[DHCP]: Dynamic Host Configuration Protocol
*[DHCP]: Dynamic Host Configuration Protocol
*[duration format]: Example: "300ms", "1.5h" or "2h45m". Valid time units are "ns", "us", "ms", "s", "m", "h". Number
without unit treats as minutes.

View File

@ -86,7 +86,7 @@ type ListCache struct {
// Configuration returns current configuration and stats
func (b *ListCache) Configuration() (result []string) {
if b.refreshPeriod > 0 {
result = append(result, fmt.Sprintf("refresh period: %d minutes", b.refreshPeriod/time.Minute))
result = append(result, fmt.Sprintf("refresh period: %s", b.refreshPeriod))
} else {
result = append(result, "refresh: disabled")
}
@ -114,10 +114,10 @@ func (b *ListCache) Configuration() (result []string) {
}
// NewListCache creates new list instance
func NewListCache(t ListCacheType, groupToLinks map[string][]string, refreshPeriod int) *ListCache {
func NewListCache(t ListCacheType, groupToLinks map[string][]string, refreshPeriod time.Duration) *ListCache {
groupCaches := make(map[string]stringCache)
p := time.Duration(refreshPeriod) * time.Minute
p := refreshPeriod
if refreshPeriod == 0 {
p = defaultRefreshPeriod
}

View File

@ -23,6 +23,7 @@ markdown_extensions:
- pymdownx.highlight
- pymdownx.superfences
- admonition
- pymdownx.details
nav:
- 'Welcome': 'index.md'

View File

@ -82,8 +82,8 @@ type BlockingResolver struct {
// NewBlockingResolver returns a new configured instance of the resolver
func NewBlockingResolver(cfg config.BlockingConfig) ChainedResolver {
blockHandler := createBlockHandler(cfg)
blacklistMatcher := lists.NewListCache(lists.ListCacheTypeBlacklist, cfg.BlackLists, cfg.RefreshPeriod)
whitelistMatcher := lists.NewListCache(lists.ListCacheTypeWhitelist, cfg.WhiteLists, cfg.RefreshPeriod)
blacklistMatcher := lists.NewListCache(lists.ListCacheTypeBlacklist, cfg.BlackLists, time.Duration(cfg.RefreshPeriod))
whitelistMatcher := lists.NewListCache(lists.ListCacheTypeWhitelist, cfg.WhiteLists, time.Duration(cfg.RefreshPeriod))
whitelistOnlyGroups := determineWhitelistOnlyGroups(&cfg)
res := &BlockingResolver{
@ -454,11 +454,11 @@ func (b ipBlockHandler) handleBlock(question dns.Question, response *dns.Msg) {
}
func blockTTLFromConfig(cfg config.BlockingConfig) uint32 {
if cfg.BlockTimeSec <= 0 {
if cfg.BlockTTL <= 0 {
return defaultBlockTTL
}
return uint32(cfg.BlockTimeSec)
return uint32(time.Duration(cfg.BlockTTL).Seconds())
}
func blockTypeFromConfig(cfg config.BlockingConfig) string {

View File

@ -246,7 +246,7 @@ badcnamedomain.com`)
})
})
When("BlockTimeSec is set", func() {
When("BlockTTL is set", func() {
BeforeEach(func() {
sutConfig = config.BlockingConfig{
BlackLists: map[string][]string{
@ -255,7 +255,7 @@ badcnamedomain.com`)
ClientGroupsBlock: map[string][]string{
"default": {"defaultGroup"},
},
BlockTimeSec: 1234,
BlockTTL: config.Duration(time.Second * 1234),
}
})

View File

@ -40,8 +40,8 @@ const (
// NewCachingResolver creates a new resolver instance
func NewCachingResolver(cfg config.CachingConfig) ChainedResolver {
c := &CachingResolver{
minCacheTimeSec: 60 * cfg.MinCachingTime,
maxCacheTimeSec: 60 * cfg.MaxCachingTime,
minCacheTimeSec: int(time.Duration(cfg.MinCachingTime).Seconds()),
maxCacheTimeSec: int(time.Duration(cfg.MaxCachingTime).Seconds()),
resultCache: createQueryResultCache(&cfg),
}
@ -59,7 +59,7 @@ func createQueryResultCache(cfg *config.CachingConfig) *cache.Cache {
func configurePrefetching(c *CachingResolver, cfg *config.CachingConfig) {
c.prefetchExpires = prefetchingNameCacheExpiration
if cfg.PrefetchExpires > 0 {
c.prefetchExpires = time.Duration(cfg.PrefetchExpires) * time.Minute
c.prefetchExpires = time.Duration(cfg.PrefetchExpires)
}
c.prefetchThreshold = prefetchingNameCountThreshold

View File

@ -49,7 +49,7 @@ var _ = Describe("CachingResolver", func() {
BeforeEach(func() {
sutConfig = config.CachingConfig{
Prefetching: true,
PrefetchExpires: 120,
PrefetchExpires: config.Duration(time.Minute * 120),
PrefetchThreshold: 5,
}
})
@ -102,7 +102,7 @@ var _ = Describe("CachingResolver", func() {
When("min caching time is defined", func() {
BeforeEach(func() {
sutConfig = config.CachingConfig{
MinCachingTime: 5,
MinCachingTime: config.Duration(time.Minute * 5),
}
})
Context("response TTL is bigger than defined min caching time", func() {
@ -232,7 +232,7 @@ var _ = Describe("CachingResolver", func() {
Context("max caching time is negative -> caching is disabled", func() {
BeforeEach(func() {
sutConfig = config.CachingConfig{
MaxCachingTime: -1,
MaxCachingTime: config.Duration(time.Minute * -1),
}
})
@ -265,7 +265,7 @@ var _ = Describe("CachingResolver", func() {
Context("max caching time is positive", func() {
BeforeEach(func() {
sutConfig = config.CachingConfig{
MaxCachingTime: 4,
MaxCachingTime: config.Duration(time.Minute * 4),
}
})
It("should cache response and use max caching time as TTL if response TTL is bigger", func() {
@ -369,7 +369,7 @@ var _ = Describe("CachingResolver", func() {
When("resolver is disabled", func() {
BeforeEach(func() {
sutConfig = config.CachingConfig{
MaxCachingTime: -1,
MaxCachingTime: config.Duration(time.Minute * -1),
}
})
It("should return 'disabled''", func() {

4
testdata/config.yml vendored
View File

@ -31,7 +31,9 @@ blocking:
- special
Laptop-D.fritz.box:
- ads
#blockMode: zeroIP
blockTTL: 1m
# without unit -> use minutes
refreshPeriod: 120
clientLookup:
upstream: udp:192.168.178.1
singleNameOrder: