aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'plugin')
-rw-r--r--plugin/acl/README.md14
-rw-r--r--plugin/acl/acl.go7
-rw-r--r--plugin/acl/acl_test.go150
-rw-r--r--plugin/acl/metrics.go7
-rw-r--r--plugin/acl/setup.go4
-rw-r--r--plugin/acl/setup_test.go14
6 files changed, 194 insertions, 2 deletions
diff --git a/plugin/acl/README.md b/plugin/acl/README.md
index 6e5b827e1..d957d24ec 100644
--- a/plugin/acl/README.md
+++ b/plugin/acl/README.md
@@ -25,7 +25,7 @@ acl [ZONES...] {
```
- **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block are used.
-- **ACTION** (*allow*, *block*, or *filter*) defines the way to deal with DNS queries matched by this rule. The default action is *allow*, which means a DNS query not matched by any rules will be allowed to recurse. The difference between *block* and *filter* is that block returns status code of *REFUSED* while filter returns an empty set *NOERROR*
+- **ACTION** (*allow*, *block*, *filter*, or *drop*) defines the way to deal with DNS queries matched by this rule. The default action is *allow*, which means a DNS query not matched by any rules will be allowed to recurse. The difference between *block* and *filter* is that block returns status code of *REFUSED* while filter returns an empty set *NOERROR*. *drop* however returns no response to the client.
- **QTYPE** is the query type to match for the requests to be allowed or blocked. Common resource record types are supported. `*` stands for all record types. The default behavior for an omitted `type QTYPE...` is to match all kinds of DNS queries (same as `type *`).
- **SOURCE** is the source IP address to match for the requests to be allowed or blocked. Typical CIDR notation and single IP address are supported. `*` stands for all possible source IP addresses.
@@ -85,6 +85,16 @@ example.org {
}
~~~
+Drop all DNS queries from 192.0.2.0/24:
+
+~~~ corefile
+. {
+ acl {
+ drop net 192.0.2.0/24
+ }
+}
+~~~
+
## Metrics
If monitoring is enabled (via the _prometheus_ plugin) then the following metrics are exported:
@@ -95,4 +105,6 @@ If monitoring is enabled (via the _prometheus_ plugin) then the following metric
- `coredns_acl_allowed_requests_total{server, view}` - counter of DNS requests being allowed.
+- `coredns_acl_dropped_requests_total{server, zone, view}` - counter of DNS requests being dropped.
+
The `server` and `zone` labels are explained in the _metrics_ plugin documentation.
diff --git a/plugin/acl/acl.go b/plugin/acl/acl.go
index 7d7b9d600..263232632 100644
--- a/plugin/acl/acl.go
+++ b/plugin/acl/acl.go
@@ -49,6 +49,8 @@ const (
actionBlock
// actionFilter returns empty sets for queries towards protected DNS zones.
actionFilter
+ // actionDrop does not respond for queries towards the protected DNS zones.
+ actionDrop
)
var log = clog.NewWithPlugin("acl")
@@ -67,6 +69,11 @@ RulesCheckLoop:
action := matchWithPolicies(rule.policies, w, r)
switch action {
+ case actionDrop:
+ {
+ RequestDropCount.WithLabelValues(metrics.WithServer(ctx), zone, metrics.WithView(ctx)).Inc()
+ return dns.RcodeSuccess, nil
+ }
case actionBlock:
{
m := new(dns.Msg).
diff --git a/plugin/acl/acl_test.go b/plugin/acl/acl_test.go
index d947879c3..f867d1f52 100644
--- a/plugin/acl/acl_test.go
+++ b/plugin/acl/acl_test.go
@@ -51,6 +51,7 @@ func TestACLServeDNS(t *testing.T) {
wantRcode int
wantErr bool
wantExtendedErrorCode uint16
+ expectNoResponse bool
}{
// IPv4 tests.
{
@@ -206,6 +207,79 @@ func TestACLServeDNS(t *testing.T) {
wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
},
{
+ name: "Drop 1 DROPPED",
+ config: `acl example.org {
+ drop net 192.168.0.0/16
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "192.168.0.2",
+ qtype: dns.TypeA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Subnet-Order 1 REFUSED",
+ config: `acl example.org {
+ block net 192.168.1.0/24
+ drop net 192.168.0.0/16
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "192.168.1.2",
+ qtype: dns.TypeA,
+ },
+ wantRcode: dns.RcodeRefused,
+ wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
+ },
+ {
+ name: "Subnet-Order 2 DROPPED",
+ config: `acl example.org {
+ drop net 192.168.0.0/16
+ block net 192.168.1.0/24
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "192.168.1.1",
+ qtype: dns.TypeA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Drop-Type 1 DROPPED",
+ config: `acl example.org {
+ drop type A
+ allow net 192.168.0.0/16
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "192.168.1.1",
+ qtype: dns.TypeA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Drop-Type 2 ALLOWED",
+ config: `acl example.org {
+ drop type A
+ allow net 192.168.0.0/16
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "192.168.1.1",
+ qtype: dns.TypeAAAA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ },
+ {
name: "Fine-Grained 1 REFUSED",
config: `acl a.example.org {
block type * net 192.168.1.0/24
@@ -402,6 +476,79 @@ func TestACLServeDNS(t *testing.T) {
wantRcode: dns.RcodeRefused,
wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
},
+ {
+ name: "Drop 1 DROPPED IPV6",
+ config: `acl example.org {
+ drop net 2001:0db8:85a3:0000:0000:8a2e:0370:7334
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ qtype: dns.TypeAAAA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Subnet-Order 1 REFUSED IPv6",
+ config: `acl example.org {
+ block net 2001:db8:abcd:0012:8000::/66
+ drop net 2001:db8:abcd:0012::0/64
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "2001:db8:abcd:0012:8000::1",
+ qtype: dns.TypeAAAA,
+ },
+ wantRcode: dns.RcodeRefused,
+ wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
+ },
+ {
+ name: "Subnet-Order 2 DROPPED IPv6",
+ config: `acl example.org {
+ drop net 2001:db8:abcd:0012::0/64
+ block net 2001:db8:abcd:0012:8000::/66
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "2001:db8:abcd:0012:8000::1",
+ qtype: dns.TypeAAAA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Drop-Type 1 DROPPED IPv6",
+ config: `acl example.org {
+ drop type A
+ allow net 2001:db8:85a3:0000::0/64
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ qtype: dns.TypeA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ expectNoResponse: true,
+ },
+ {
+ name: "Drop-Type 2 ALLOWED IPv6",
+ config: `acl example.org {
+ drop type A
+ allow net 2001:db8:85a3:0000::0/64
+ }`,
+ zones: []string{},
+ args: args{
+ domain: "www.example.org.",
+ sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ qtype: dns.TypeAAAA,
+ },
+ wantRcode: dns.RcodeSuccess,
+ },
}
ctx := context.Background()
@@ -430,6 +577,9 @@ func TestACLServeDNS(t *testing.T) {
if w.Rcode != tt.wantRcode {
t.Errorf("Error: acl.ServeDNS() Rcode = %v, want %v", w.Rcode, tt.wantRcode)
}
+ if tt.expectNoResponse && w.Msg != nil {
+ t.Errorf("Error: acl.ServeDNS() responded to client when not expected")
+ }
if tt.wantExtendedErrorCode != 0 {
matched := false
for _, opt := range w.Msg.IsEdns0().Option {
diff --git a/plugin/acl/metrics.go b/plugin/acl/metrics.go
index 04d728bcd..a8d823254 100644
--- a/plugin/acl/metrics.go
+++ b/plugin/acl/metrics.go
@@ -29,4 +29,11 @@ var (
Name: "allowed_requests_total",
Help: "Counter of DNS requests being allowed.",
}, []string{"server", "view"})
+ // RequestDropCount is the number of DNS requests being dropped.
+ RequestDropCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Namespace: plugin.Namespace,
+ Subsystem: pluginName,
+ Name: "dropped_requests_total",
+ Help: "Counter of DNS requests being dropped.",
+ }, []string{"server", "zone", "view"})
)
diff --git a/plugin/acl/setup.go b/plugin/acl/setup.go
index 3adde0aec..189acc6c4 100644
--- a/plugin/acl/setup.go
+++ b/plugin/acl/setup.go
@@ -56,8 +56,10 @@ func parse(c *caddy.Controller) (ACL, error) {
p.action = actionBlock
} else if action == "filter" {
p.action = actionFilter
+ } else if action == "drop" {
+ p.action = actionDrop
} else {
- return a, c.Errf("unexpected token %q; expect 'allow', 'block', or 'filter'", c.Val())
+ return a, c.Errf("unexpected token %q; expect 'allow', 'block', 'filter' or 'drop'", c.Val())
}
p.qtypes = make(map[uint16]struct{})
diff --git a/plugin/acl/setup_test.go b/plugin/acl/setup_test.go
index 1d25dd7af..5cd51bb3a 100644
--- a/plugin/acl/setup_test.go
+++ b/plugin/acl/setup_test.go
@@ -58,6 +58,13 @@ func TestSetup(t *testing.T) {
false,
},
{
+ "Drop 1",
+ `acl {
+ drop type * net 192.168.0.0/16
+ }`,
+ false,
+ },
+ {
"fine-grained 1",
`acl a.example.org {
block type * net 192.168.1.0/24
@@ -176,6 +183,13 @@ func TestSetup(t *testing.T) {
false,
},
{
+ "Drop 1 IPv6",
+ `acl {
+ drop net 2001:db8:abcd:0012::0/64
+ }`,
+ false,
+ },
+ {
"fine-grained 1 IPv6",
`acl a.example.org {
block net 2001:db8:abcd:0012::0/64