From 2fe5273cd12562aca6939540f0a9e03b51d34aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Benkovsk=C3=BD?= Date: Mon, 25 Jul 2022 16:44:13 +0200 Subject: [PATCH] plugin/acl : add support for Extended DNS Errors (#5532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * plugin/acl : add support for Extended DNS Errors Signed-off-by: Ondřej Benkovský * fixup! plugin/acl : add support for Extended DNS Errors Signed-off-by: Ondřej Benkovský --- plugin/acl/acl.go | 14 +- plugin/acl/acl_test.go | 468 +++++++++++++++++++++-------------------- 2 files changed, 247 insertions(+), 235 deletions(-) diff --git a/plugin/acl/acl.go b/plugin/acl/acl.go index 95a5c65eb..017a8ac6b 100644 --- a/plugin/acl/acl.go +++ b/plugin/acl/acl.go @@ -69,8 +69,11 @@ RulesCheckLoop: switch action { case actionBlock: { - m := new(dns.Msg) - m.SetRcode(r, dns.RcodeRefused) + m := new(dns.Msg). + SetRcode(r, dns.RcodeRefused). + SetEdns0(4096, true) + ede := dns.EDNS0_EDE{InfoCode: dns.ExtendedErrorCodeBlocked} + m.IsEdns0().Option = append(m.IsEdns0().Option, &ede) w.WriteMsg(m) RequestBlockCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc() return dns.RcodeSuccess, nil @@ -81,8 +84,11 @@ RulesCheckLoop: } case actionFilter: { - m := new(dns.Msg) - m.SetRcode(r, dns.RcodeSuccess) + m := new(dns.Msg). + SetRcode(r, dns.RcodeSuccess). + SetEdns0(4096, true) + ede := dns.EDNS0_EDE{InfoCode: dns.ExtendedErrorCodeFiltered} + m.IsEdns0().Option = append(m.IsEdns0().Option, &ede) w.WriteMsg(m) RequestFilterCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc() return dns.RcodeSuccess, nil diff --git a/plugin/acl/acl_test.go b/plugin/acl/acl_test.go index 7b641cef5..d947879c3 100644 --- a/plugin/acl/acl_test.go +++ b/plugin/acl/acl_test.go @@ -13,6 +13,7 @@ import ( type testResponseWriter struct { test.ResponseWriter Rcode int + Msg *dns.Msg } func (t *testResponseWriter) setRemoteIP(ip string) { @@ -26,6 +27,7 @@ func (t *testResponseWriter) setZone(zone string) { // WriteMsg implement dns.ResponseWriter interface. func (t *testResponseWriter) WriteMsg(m *dns.Msg) error { t.Rcode = m.Rcode + t.Msg = m return nil } @@ -42,373 +44,363 @@ func TestACLServeDNS(t *testing.T) { qtype uint16 } tests := []struct { - name string - config string - zones []string - args args - wantRcode int - wantErr bool + name string + config string + zones []string + args args + wantRcode int + wantErr bool + wantExtendedErrorCode uint16 }{ // IPv4 tests. { - "Blacklist 1 BLOCKED", - `acl example.org { + name: "Blacklist 1 BLOCKED", + config: `acl example.org { block type A net 192.168.0.0/16 }`, - []string{}, - args{ - "www.example.org.", - "192.168.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.0.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 1 ALLOWED", - `acl example.org { + name: "Blacklist 1 ALLOWED", + config: `acl example.org { block type A net 192.168.0.0/16 }`, - []string{}, - args{ - "www.example.org.", - "192.167.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.167.0.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Blacklist 2 BLOCKED", - ` + name: "Blacklist 2 BLOCKED", + config: ` acl example.org { block type * net 192.168.0.0/16 }`, - []string{}, - args{ - "www.example.org.", - "192.168.0.2", - dns.TypeAAAA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.0.2", + qtype: dns.TypeAAAA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 3 BLOCKED", - `acl example.org { + name: "Blacklist 3 BLOCKED", + config: `acl example.org { block type A }`, - []string{}, - args{ - "www.example.org.", - "10.1.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "10.1.0.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 3 ALLOWED", - `acl example.org { + name: "Blacklist 3 ALLOWED", + config: `acl example.org { block type A }`, - []string{}, - args{ - "www.example.org.", - "10.1.0.2", - dns.TypeAAAA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "10.1.0.2", + qtype: dns.TypeAAAA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Blacklist 4 Single IP BLOCKED", - `acl example.org { + name: "Blacklist 4 Single IP BLOCKED", + config: `acl example.org { block type A net 192.168.1.2 }`, - []string{}, - args{ - "www.example.org.", - "192.168.1.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 4 Single IP ALLOWED", - `acl example.org { + name: "Blacklist 4 Single IP ALLOWED", + config: `acl example.org { block type A net 192.168.1.2 }`, - []string{}, - args{ - "www.example.org.", - "192.168.1.3", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.1.3", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Filter 1 FILTERED", - `acl example.org { + name: "Filter 1 FILTERED", + config: `acl example.org { filter type A net 192.168.0.0/16 }`, - []string{}, - args{ - "www.example.org.", - "192.168.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.0.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, + wantExtendedErrorCode: dns.ExtendedErrorCodeFiltered, }, { - "Filter 1 ALLOWED", - `acl example.org { + name: "Filter 1 ALLOWED", + config: `acl example.org { filter type A net 192.168.0.0/16 }`, - []string{}, - args{ - "www.example.org.", - "192.167.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.167.0.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Whitelist 1 ALLOWED", - `acl example.org { + name: "Whitelist 1 ALLOWED", + config: `acl example.org { allow net 192.168.0.0/16 block }`, - []string{}, - args{ - "www.example.org.", - "192.168.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.0.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Whitelist 1 REFUSED", - `acl example.org { + name: "Whitelist 1 REFUSED", + config: `acl example.org { allow type * net 192.168.0.0/16 block }`, - []string{}, - args{ - "www.example.org.", - "10.1.0.2", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "10.1.0.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Fine-Grained 1 REFUSED", - `acl a.example.org { + name: "Fine-Grained 1 REFUSED", + config: `acl a.example.org { block type * net 192.168.1.0/24 }`, - []string{"example.org"}, - args{ - "a.example.org.", - "192.168.1.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "a.example.org.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Fine-Grained 1 ALLOWED", - `acl a.example.org { + name: "Fine-Grained 1 ALLOWED", + config: `acl a.example.org { block net 192.168.1.0/24 }`, - []string{"example.org"}, - args{ - "www.example.org.", - "192.168.1.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "www.example.org.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Fine-Grained 2 REFUSED", - `acl example.org { + name: "Fine-Grained 2 REFUSED", + config: `acl example.org { block net 192.168.1.0/24 }`, - []string{"example.org"}, - args{ - "a.example.org.", - "192.168.1.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "a.example.org.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Fine-Grained 2 ALLOWED", - `acl { + name: "Fine-Grained 2 ALLOWED", + config: `acl { block net 192.168.1.0/24 }`, - []string{"example.org"}, - args{ - "a.example.com.", - "192.168.1.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "a.example.com.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Fine-Grained 3 REFUSED", - `acl a.example.org { + name: "Fine-Grained 3 REFUSED", + config: `acl a.example.org { block net 192.168.1.0/24 } acl b.example.org { block type * net 192.168.2.0/24 }`, - []string{"example.org"}, - args{ - "b.example.org.", - "192.168.2.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "b.example.org.", + sourceIP: "192.168.2.2", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Fine-Grained 3 ALLOWED", - `acl a.example.org { + name: "Fine-Grained 3 ALLOWED", + config: `acl a.example.org { block net 192.168.1.0/24 } acl b.example.org { block net 192.168.2.0/24 }`, - []string{"example.org"}, - args{ - "b.example.org.", - "192.168.1.2", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "b.example.org.", + sourceIP: "192.168.1.2", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, // IPv6 tests. { - "Blacklist 1 BLOCKED IPv6", - `acl example.org { + name: "Blacklist 1 BLOCKED IPv6", + config: `acl example.org { block type A net 2001:db8:abcd:0012::0/64 }`, - []string{}, - args{ - "www.example.org.", - "2001:db8:abcd:0012::1230", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:db8:abcd:0012::1230", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 1 ALLOWED IPv6", - `acl example.org { + name: "Blacklist 1 ALLOWED IPv6", + config: `acl example.org { block type A net 2001:db8:abcd:0012::0/64 }`, - []string{}, - args{ - "www.example.org.", - "2001:db8:abcd:0013::0", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:db8:abcd:0013::0", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Blacklist 2 BLOCKED IPv6", - `acl example.org { + name: "Blacklist 2 BLOCKED IPv6", + config: `acl example.org { block type A }`, - []string{}, - args{ - "www.example.org.", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 3 Single IP BLOCKED IPv6", - `acl example.org { + name: "Blacklist 3 Single IP BLOCKED IPv6", + config: `acl example.org { block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 }`, - []string{}, - args{ - "www.example.org.", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Blacklist 3 Single IP ALLOWED IPv6", - `acl example.org { + name: "Blacklist 3 Single IP ALLOWED IPv6", + config: `acl example.org { block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 }`, - []string{}, - args{ - "www.example.org.", - "2001:0db8:85a3:0000:0000:8a2e:0370:7335", - dns.TypeA, + zones: []string{}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7335", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Fine-Grained 1 REFUSED IPv6", - `acl a.example.org { + name: "Fine-Grained 1 REFUSED IPv6", + config: `acl a.example.org { block type * net 2001:db8:abcd:0012::0/64 }`, - []string{"example.org"}, - args{ - "a.example.org.", - "2001:db8:abcd:0012:2019::0", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "a.example.org.", + sourceIP: "2001:db8:abcd:0012:2019::0", + qtype: dns.TypeA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, { - "Fine-Grained 1 ALLOWED IPv6", - `acl a.example.org { + name: "Fine-Grained 1 ALLOWED IPv6", + config: `acl a.example.org { block net 2001:db8:abcd:0012::0/64 }`, - []string{"example.org"}, - args{ - "www.example.org.", - "2001:db8:abcd:0012:2019::0", - dns.TypeA, + zones: []string{"example.org"}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:db8:abcd:0012:2019::0", + qtype: dns.TypeA, }, - dns.RcodeSuccess, - false, + wantRcode: dns.RcodeSuccess, }, { - "Blacklist Address%ifname", - `acl example.org { + name: "Blacklist Address%ifname", + config: `acl example.org { block type AAAA net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 }`, - []string{"eth0"}, - args{ - "www.example.org.", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - dns.TypeAAAA, + zones: []string{"eth0"}, + args: args{ + domain: "www.example.org.", + sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + qtype: dns.TypeAAAA, }, - dns.RcodeRefused, - false, + wantRcode: dns.RcodeRefused, + wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked, }, } @@ -438,6 +430,20 @@ 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.wantExtendedErrorCode != 0 { + matched := false + for _, opt := range w.Msg.IsEdns0().Option { + if ede, ok := opt.(*dns.EDNS0_EDE); ok { + if ede.InfoCode != tt.wantExtendedErrorCode { + t.Errorf("Error: acl.ServeDNS() Extended DNS Error = %v, want %v", ede.InfoCode, tt.wantExtendedErrorCode) + } + matched = true + } + } + if !matched { + t.Error("Error: acl.ServeDNS() missing Extended DNS Error option") + } + } }) } }