plugin/acl : add support for Extended DNS Errors (#5532)

* plugin/acl : add support for Extended DNS Errors

Signed-off-by: Ondřej Benkovský <ondrej.benkovsky@jamf.com>

* fixup! plugin/acl : add support for Extended DNS Errors

Signed-off-by: Ondřej Benkovský <ondrej.benkovsky@jamf.com>
This commit is contained in:
Ondřej Benkovský 2022-07-25 16:44:13 +02:00 committed by GitHub
parent 50beb677ad
commit 2fe5273cd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 247 additions and 235 deletions

View file

@ -69,8 +69,11 @@ RulesCheckLoop:
switch action { switch action {
case actionBlock: case actionBlock:
{ {
m := new(dns.Msg) m := new(dns.Msg).
m.SetRcode(r, dns.RcodeRefused) 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) w.WriteMsg(m)
RequestBlockCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc() RequestBlockCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc()
return dns.RcodeSuccess, nil return dns.RcodeSuccess, nil
@ -81,8 +84,11 @@ RulesCheckLoop:
} }
case actionFilter: case actionFilter:
{ {
m := new(dns.Msg) m := new(dns.Msg).
m.SetRcode(r, dns.RcodeSuccess) 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) w.WriteMsg(m)
RequestFilterCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc() RequestFilterCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc()
return dns.RcodeSuccess, nil return dns.RcodeSuccess, nil

View file

@ -13,6 +13,7 @@ import (
type testResponseWriter struct { type testResponseWriter struct {
test.ResponseWriter test.ResponseWriter
Rcode int Rcode int
Msg *dns.Msg
} }
func (t *testResponseWriter) setRemoteIP(ip string) { func (t *testResponseWriter) setRemoteIP(ip string) {
@ -26,6 +27,7 @@ func (t *testResponseWriter) setZone(zone string) {
// WriteMsg implement dns.ResponseWriter interface. // WriteMsg implement dns.ResponseWriter interface.
func (t *testResponseWriter) WriteMsg(m *dns.Msg) error { func (t *testResponseWriter) WriteMsg(m *dns.Msg) error {
t.Rcode = m.Rcode t.Rcode = m.Rcode
t.Msg = m
return nil return nil
} }
@ -48,367 +50,357 @@ func TestACLServeDNS(t *testing.T) {
args args args args
wantRcode int wantRcode int
wantErr bool wantErr bool
wantExtendedErrorCode uint16
}{ }{
// IPv4 tests. // IPv4 tests.
{ {
"Blacklist 1 BLOCKED", name: "Blacklist 1 BLOCKED",
`acl example.org { config: `acl example.org {
block type A net 192.168.0.0/16 block type A net 192.168.0.0/16
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.0.2", sourceIP: "192.168.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 1 ALLOWED", name: "Blacklist 1 ALLOWED",
`acl example.org { config: `acl example.org {
block type A net 192.168.0.0/16 block type A net 192.168.0.0/16
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.167.0.2", sourceIP: "192.167.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Blacklist 2 BLOCKED", name: "Blacklist 2 BLOCKED",
` config: `
acl example.org { acl example.org {
block type * net 192.168.0.0/16 block type * net 192.168.0.0/16
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.0.2", sourceIP: "192.168.0.2",
dns.TypeAAAA, qtype: dns.TypeAAAA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 3 BLOCKED", name: "Blacklist 3 BLOCKED",
`acl example.org { config: `acl example.org {
block type A block type A
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"10.1.0.2", sourceIP: "10.1.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 3 ALLOWED", name: "Blacklist 3 ALLOWED",
`acl example.org { config: `acl example.org {
block type A block type A
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"10.1.0.2", sourceIP: "10.1.0.2",
dns.TypeAAAA, qtype: dns.TypeAAAA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Blacklist 4 Single IP BLOCKED", name: "Blacklist 4 Single IP BLOCKED",
`acl example.org { config: `acl example.org {
block type A net 192.168.1.2 block type A net 192.168.1.2
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 4 Single IP ALLOWED", name: "Blacklist 4 Single IP ALLOWED",
`acl example.org { config: `acl example.org {
block type A net 192.168.1.2 block type A net 192.168.1.2
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.1.3", sourceIP: "192.168.1.3",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Filter 1 FILTERED", name: "Filter 1 FILTERED",
`acl example.org { config: `acl example.org {
filter type A net 192.168.0.0/16 filter type A net 192.168.0.0/16
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.0.2", sourceIP: "192.168.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeFiltered,
}, },
{ {
"Filter 1 ALLOWED", name: "Filter 1 ALLOWED",
`acl example.org { config: `acl example.org {
filter type A net 192.168.0.0/16 filter type A net 192.168.0.0/16
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.167.0.2", sourceIP: "192.167.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Whitelist 1 ALLOWED", name: "Whitelist 1 ALLOWED",
`acl example.org { config: `acl example.org {
allow net 192.168.0.0/16 allow net 192.168.0.0/16
block block
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.0.2", sourceIP: "192.168.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Whitelist 1 REFUSED", name: "Whitelist 1 REFUSED",
`acl example.org { config: `acl example.org {
allow type * net 192.168.0.0/16 allow type * net 192.168.0.0/16
block block
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"10.1.0.2", sourceIP: "10.1.0.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Fine-Grained 1 REFUSED", name: "Fine-Grained 1 REFUSED",
`acl a.example.org { config: `acl a.example.org {
block type * net 192.168.1.0/24 block type * net 192.168.1.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"a.example.org.", domain: "a.example.org.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Fine-Grained 1 ALLOWED", name: "Fine-Grained 1 ALLOWED",
`acl a.example.org { config: `acl a.example.org {
block net 192.168.1.0/24 block net 192.168.1.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Fine-Grained 2 REFUSED", name: "Fine-Grained 2 REFUSED",
`acl example.org { config: `acl example.org {
block net 192.168.1.0/24 block net 192.168.1.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"a.example.org.", domain: "a.example.org.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Fine-Grained 2 ALLOWED", name: "Fine-Grained 2 ALLOWED",
`acl { config: `acl {
block net 192.168.1.0/24 block net 192.168.1.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"a.example.com.", domain: "a.example.com.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Fine-Grained 3 REFUSED", name: "Fine-Grained 3 REFUSED",
`acl a.example.org { config: `acl a.example.org {
block net 192.168.1.0/24 block net 192.168.1.0/24
} }
acl b.example.org { acl b.example.org {
block type * net 192.168.2.0/24 block type * net 192.168.2.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"b.example.org.", domain: "b.example.org.",
"192.168.2.2", sourceIP: "192.168.2.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Fine-Grained 3 ALLOWED", name: "Fine-Grained 3 ALLOWED",
`acl a.example.org { config: `acl a.example.org {
block net 192.168.1.0/24 block net 192.168.1.0/24
} }
acl b.example.org { acl b.example.org {
block net 192.168.2.0/24 block net 192.168.2.0/24
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"b.example.org.", domain: "b.example.org.",
"192.168.1.2", sourceIP: "192.168.1.2",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
// IPv6 tests. // IPv6 tests.
{ {
"Blacklist 1 BLOCKED IPv6", name: "Blacklist 1 BLOCKED IPv6",
`acl example.org { config: `acl example.org {
block type A net 2001:db8:abcd:0012::0/64 block type A net 2001:db8:abcd:0012::0/64
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:db8:abcd:0012::1230", sourceIP: "2001:db8:abcd:0012::1230",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 1 ALLOWED IPv6", name: "Blacklist 1 ALLOWED IPv6",
`acl example.org { config: `acl example.org {
block type A net 2001:db8:abcd:0012::0/64 block type A net 2001:db8:abcd:0012::0/64
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:db8:abcd:0013::0", sourceIP: "2001:db8:abcd:0013::0",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Blacklist 2 BLOCKED IPv6", name: "Blacklist 2 BLOCKED IPv6",
`acl example.org { config: `acl example.org {
block type A block type A
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334", sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 3 Single IP BLOCKED IPv6", name: "Blacklist 3 Single IP BLOCKED IPv6",
`acl example.org { config: `acl example.org {
block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334", sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Blacklist 3 Single IP ALLOWED IPv6", name: "Blacklist 3 Single IP ALLOWED IPv6",
`acl example.org { config: `acl example.org {
block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 block type A net 2001:0db8:85a3:0000:0000:8a2e:0370:7334
}`, }`,
[]string{}, zones: []string{},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:0db8:85a3:0000:0000:8a2e:0370:7335", sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7335",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Fine-Grained 1 REFUSED IPv6", name: "Fine-Grained 1 REFUSED IPv6",
`acl a.example.org { config: `acl a.example.org {
block type * net 2001:db8:abcd:0012::0/64 block type * net 2001:db8:abcd:0012::0/64
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"a.example.org.", domain: "a.example.org.",
"2001:db8:abcd:0012:2019::0", sourceIP: "2001:db8:abcd:0012:2019::0",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
{ {
"Fine-Grained 1 ALLOWED IPv6", name: "Fine-Grained 1 ALLOWED IPv6",
`acl a.example.org { config: `acl a.example.org {
block net 2001:db8:abcd:0012::0/64 block net 2001:db8:abcd:0012::0/64
}`, }`,
[]string{"example.org"}, zones: []string{"example.org"},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:db8:abcd:0012:2019::0", sourceIP: "2001:db8:abcd:0012:2019::0",
dns.TypeA, qtype: dns.TypeA,
}, },
dns.RcodeSuccess, wantRcode: dns.RcodeSuccess,
false,
}, },
{ {
"Blacklist Address%ifname", name: "Blacklist Address%ifname",
`acl example.org { config: `acl example.org {
block type AAAA net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 block type AAAA net 2001:0db8:85a3:0000:0000:8a2e:0370:7334
}`, }`,
[]string{"eth0"}, zones: []string{"eth0"},
args{ args: args{
"www.example.org.", domain: "www.example.org.",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334", sourceIP: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
dns.TypeAAAA, qtype: dns.TypeAAAA,
}, },
dns.RcodeRefused, wantRcode: dns.RcodeRefused,
false, wantExtendedErrorCode: dns.ExtendedErrorCodeBlocked,
}, },
} }
@ -438,6 +430,20 @@ func TestACLServeDNS(t *testing.T) {
if w.Rcode != tt.wantRcode { if w.Rcode != tt.wantRcode {
t.Errorf("Error: acl.ServeDNS() Rcode = %v, want %v", 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")
}
}
}) })
} }
} }