mirror of https://github.com/0xERR0R/blocky.git
Bugfix in ECS forward (#1290)
* fixed override bug in forward * set prettier as default formatter for yaml * added ecs to example config
This commit is contained in:
parent
ac54110886
commit
11543356b6
|
@ -50,7 +50,7 @@
|
||||||
"[go]": {
|
"[go]": {
|
||||||
"editor.defaultFormatter": "golang.go"
|
"editor.defaultFormatter": "golang.go"
|
||||||
},
|
},
|
||||||
"[json][jsonc][github-actions-workflow]": {
|
"[yaml][json][jsonc][github-actions-workflow]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[markdown]": {
|
"[markdown]": {
|
||||||
|
|
|
@ -332,3 +332,10 @@ specialUseDomains:
|
||||||
# optional: block recomended private TLDs
|
# optional: block recomended private TLDs
|
||||||
# default: true
|
# default: true
|
||||||
rfc6762-appendixG: true
|
rfc6762-appendixG: true
|
||||||
|
|
||||||
|
# optional: configure extended client subnet (ECS) support
|
||||||
|
ecs:
|
||||||
|
# optional: if the request ecs option with a max sice mask the address will be used as client ip
|
||||||
|
useAsClient: true
|
||||||
|
# optional: if the request contains a ecs option it will be forwarded to the upstream resolver
|
||||||
|
forward: true
|
||||||
|
|
|
@ -53,17 +53,19 @@ func (r *ECSResolver) Resolve(ctx context.Context, request *model.Request) (*mod
|
||||||
// Set the client IP from the Edns0 subnet option if the option is enabled and the correct subnet mask is set
|
// Set the client IP from the Edns0 subnet option if the option is enabled and the correct subnet mask is set
|
||||||
if r.cfg.UseAsClient && so != nil && ((so.Family == ecsFamilyIPv4 && so.SourceNetmask == ecsMaskIPv4) ||
|
if r.cfg.UseAsClient && so != nil && ((so.Family == ecsFamilyIPv4 && so.SourceNetmask == ecsMaskIPv4) ||
|
||||||
(so.Family == ecsFamilyIPv6 && so.SourceNetmask == ecsMaskIPv6)) {
|
(so.Family == ecsFamilyIPv6 && so.SourceNetmask == ecsMaskIPv6)) {
|
||||||
|
request.Log.Debugf("using request's edns0 address as internal client IP: %s", so.Address)
|
||||||
request.ClientIP = so.Address
|
request.ClientIP = so.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the Edns0 subnet option if the client IP is IPv4 or IPv6 and the masks are set in the configuration
|
// Set the Edns0 subnet option if the client IP is IPv4 or IPv6 and the masks are set in the configuration
|
||||||
if r.cfg.IPv4Mask > 0 || r.cfg.IPv6Mask > 0 {
|
if r.cfg.IPv4Mask > 0 || r.cfg.IPv6Mask > 0 {
|
||||||
r.setSubnet(request)
|
r.setSubnet(so, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the Edns0 subnet option if the client IP is IPv4 or IPv6 and the corresponding mask is not set
|
// Remove the Edns0 subnet option if the client IP is IPv4 or IPv6 and the corresponding mask is not set
|
||||||
// and the forwardEcs option is not enabled
|
// and the forwardEcs option is not enabled
|
||||||
if r.cfg.IPv4Mask == 0 && r.cfg.IPv6Mask == 0 && so != nil && !r.cfg.Forward {
|
if r.cfg.IPv4Mask == 0 && r.cfg.IPv6Mask == 0 && so != nil && !r.cfg.Forward {
|
||||||
|
request.Log.Debug("remove edns0 subnet option")
|
||||||
util.RemoveEdns0Option[*dns.EDNS0_SUBNET](request.Req)
|
util.RemoveEdns0Option[*dns.EDNS0_SUBNET](request.Req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,28 +75,30 @@ func (r *ECSResolver) Resolve(ctx context.Context, request *model.Request) (*mod
|
||||||
|
|
||||||
// setSubnet appends the subnet information to the request as EDNS0 option
|
// setSubnet appends the subnet information to the request as EDNS0 option
|
||||||
// if the client IP is IPv4 or IPv6 and the corresponding mask is set in the configuration
|
// if the client IP is IPv4 or IPv6 and the corresponding mask is set in the configuration
|
||||||
func (r *ECSResolver) setSubnet(request *model.Request) {
|
func (r *ECSResolver) setSubnet(so *dns.EDNS0_SUBNET, request *model.Request) {
|
||||||
e := new(dns.EDNS0_SUBNET)
|
var subIP net.IP
|
||||||
e.Code = dns.EDNS0SUBNET
|
if so != nil && r.cfg.Forward && so.Address != nil {
|
||||||
e.SourceScope = ecsSourceScope
|
subIP = so.Address
|
||||||
|
} else {
|
||||||
|
subIP = request.ClientIP
|
||||||
|
}
|
||||||
|
|
||||||
if ip := request.ClientIP.To4(); ip != nil && r.cfg.IPv4Mask > 0 {
|
var edsOption *dns.EDNS0_SUBNET
|
||||||
mip, err := maskIP(ip, r.cfg.IPv4Mask)
|
|
||||||
if err == nil {
|
if ip := subIP.To4(); ip != nil && r.cfg.IPv4Mask > 0 {
|
||||||
e.Family = ecsFamilyIPv4
|
if mip, err := maskIP(ip, r.cfg.IPv4Mask); err == nil {
|
||||||
e.SourceNetmask = uint8(r.cfg.IPv4Mask)
|
edsOption = newEdnsSubnetOption(mip, ecsFamilyIPv4, r.cfg.IPv4Mask)
|
||||||
e.Address = mip
|
|
||||||
util.SetEdns0Option(request.Req, e)
|
|
||||||
}
|
}
|
||||||
} else if ip := request.ClientIP.To16(); ip != nil && r.cfg.IPv6Mask > 0 {
|
} else if ip := subIP.To16(); ip != nil && r.cfg.IPv6Mask > 0 {
|
||||||
mip, err := maskIP(ip, r.cfg.IPv6Mask)
|
if mip, err := maskIP(ip, r.cfg.IPv6Mask); err == nil {
|
||||||
if err == nil {
|
edsOption = newEdnsSubnetOption(mip, ecsFamilyIPv6, r.cfg.IPv6Mask)
|
||||||
e.Family = ecsFamilyIPv6
|
|
||||||
e.SourceNetmask = uint8(r.cfg.IPv6Mask)
|
|
||||||
e.Address = mip
|
|
||||||
util.SetEdns0Option(request.Req, e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if edsOption != nil {
|
||||||
|
request.Log.Debugf("set edns0 subnet option address: %s", edsOption.Address)
|
||||||
|
util.SetEdns0Option(request.Req, edsOption)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// maskIP masks the IP with the given mask and return an error if the mask is invalid
|
// maskIP masks the IP with the given mask and return an error if the mask is invalid
|
||||||
|
@ -103,3 +107,14 @@ func maskIP[maskType ECSMask](ip net.IP, mask maskType) (net.IP, error) {
|
||||||
|
|
||||||
return mip.IP, err
|
return mip.IP, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newEdnsSubnetOption( creates a new EDNS0 subnet option with the given IP, family and mask
|
||||||
|
func newEdnsSubnetOption[maskType ECSMask](ip net.IP, family uint16, mask maskType) *dns.EDNS0_SUBNET {
|
||||||
|
return &dns.EDNS0_SUBNET{
|
||||||
|
Code: dns.EDNS0SUBNET,
|
||||||
|
SourceScope: ecsSourceScope,
|
||||||
|
Family: family,
|
||||||
|
SourceNetmask: uint8(mask),
|
||||||
|
Address: ip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,9 +26,6 @@ var _ = Describe("EcsResolver", func() {
|
||||||
err error
|
err error
|
||||||
origIP net.IP
|
origIP net.IP
|
||||||
ecsIP net.IP
|
ecsIP net.IP
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
cancelFn context.CancelFunc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Describe("Type", func() {
|
Describe("Type", func() {
|
||||||
|
@ -38,15 +35,12 @@ var _ = Describe("EcsResolver", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ctx, cancelFn = context.WithCancel(context.Background())
|
|
||||||
DeferCleanup(cancelFn)
|
|
||||||
|
|
||||||
err = defaults.Set(&sutConfig)
|
err = defaults.Set(&sutConfig)
|
||||||
Expect(err).Should(Succeed())
|
Expect(err).Should(Succeed())
|
||||||
|
|
||||||
mockAnswer = new(dns.Msg)
|
mockAnswer = new(dns.Msg)
|
||||||
origIP = net.ParseIP("1.2.3.4")
|
origIP = net.ParseIP("1.2.3.4").To4()
|
||||||
ecsIP = net.ParseIP("4.3.2.1")
|
ecsIP = net.ParseIP("4.3.2.1").To4()
|
||||||
})
|
})
|
||||||
|
|
||||||
JustBeforeEach(func() {
|
JustBeforeEach(func() {
|
||||||
|
@ -63,7 +57,7 @@ var _ = Describe("EcsResolver", func() {
|
||||||
sut.Next(m)
|
sut.Next(m)
|
||||||
})
|
})
|
||||||
|
|
||||||
When("ecs is disabled", func() {
|
When("ECS is disabled", func() {
|
||||||
Describe("IsEnabled", func() {
|
Describe("IsEnabled", func() {
|
||||||
It("is false", func() {
|
It("is false", func() {
|
||||||
Expect(sut.IsEnabled()).Should(BeFalse())
|
Expect(sut.IsEnabled()).Should(BeFalse())
|
||||||
|
@ -71,7 +65,7 @@ var _ = Describe("EcsResolver", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("ecs is enabled", func() {
|
When("ECS is enabled", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
sutConfig.UseAsClient = true
|
sutConfig.UseAsClient = true
|
||||||
})
|
})
|
||||||
|
@ -82,12 +76,12 @@ var _ = Describe("EcsResolver", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("use ecs client ip is enabled", func() {
|
When("use ECS client ip is enabled", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
sutConfig.UseAsClient = true
|
sutConfig.UseAsClient = true
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should change ClientIP with subnet 32", func() {
|
It("should change ClientIP with subnet 32", func(ctx context.Context) {
|
||||||
request := newRequest("example.com.", A)
|
request := newRequest("example.com.", A)
|
||||||
request.ClientIP = origIP
|
request.ClientIP = origIP
|
||||||
|
|
||||||
|
@ -108,7 +102,7 @@ var _ = Describe("EcsResolver", func() {
|
||||||
HaveReason("Test")))
|
HaveReason("Test")))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("shouldn't change ClientIP with subnet 24", func() {
|
It("shouldn't change ClientIP with subnet 24", func(ctx context.Context) {
|
||||||
request := newRequest("example.com.", A)
|
request := newRequest("example.com.", A)
|
||||||
request.ClientIP = origIP
|
request.ClientIP = origIP
|
||||||
|
|
||||||
|
@ -130,14 +124,13 @@ var _ = Describe("EcsResolver", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("forward ecs is enabled", func() {
|
When("add ECS information", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
sutConfig.Forward = true
|
|
||||||
sutConfig.IPv4Mask = 32
|
sutConfig.IPv4Mask = 32
|
||||||
sutConfig.IPv6Mask = 128
|
sutConfig.IPv6Mask = 128
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should add Ecs information with subnet 32", func() {
|
It("should add ECS information with subnet 32", func(ctx context.Context) {
|
||||||
request := newRequest("example.com.", A)
|
request := newRequest("example.com.", A)
|
||||||
request.ClientIP = origIP
|
request.ClientIP = origIP
|
||||||
|
|
||||||
|
@ -157,7 +150,7 @@ var _ = Describe("EcsResolver", func() {
|
||||||
HaveReason("Test")))
|
HaveReason("Test")))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should add Ecs information with subnet 128", func() {
|
It("should add ECS information with subnet 128", func(ctx context.Context) {
|
||||||
request := newRequest("example.com.", AAAA)
|
request := newRequest("example.com.", AAAA)
|
||||||
request.ClientIP = net.ParseIP("2001:db8::68")
|
request.ClientIP = net.ParseIP("2001:db8::68")
|
||||||
|
|
||||||
|
@ -176,6 +169,94 @@ var _ = Describe("EcsResolver", func() {
|
||||||
HaveReason("Test")))
|
HaveReason("Test")))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
When("forward ECS information", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
sutConfig.IPv4Mask = 32
|
||||||
|
sutConfig.IPv6Mask = 128
|
||||||
|
sutConfig.Forward = true
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should forward ECS information with subnet 32", func(ctx context.Context) {
|
||||||
|
request := newRequest("example.com.", A)
|
||||||
|
request.ClientIP = origIP
|
||||||
|
|
||||||
|
addEcsOption(request.Req, ecsIP, ecsMaskIPv4)
|
||||||
|
|
||||||
|
m.ResolveFn = func(ctx context.Context, req *Request) (*Response, error) {
|
||||||
|
Expect(req.ClientIP).Should(Equal(ecsIP))
|
||||||
|
Expect(req.Req).Should(HaveEdnsOption(dns.EDNS0SUBNET))
|
||||||
|
|
||||||
|
so := util.GetEdns0Option[*dns.EDNS0_SUBNET](req.Req)
|
||||||
|
Expect(so.Address).Should(Equal(ecsIP))
|
||||||
|
|
||||||
|
return respondWith(mockAnswer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(sut.Resolve(ctx, request)).
|
||||||
|
Should(
|
||||||
|
SatisfyAll(
|
||||||
|
HaveNoAnswer(),
|
||||||
|
HaveResponseType(ResponseTypeRESOLVED),
|
||||||
|
HaveReturnCode(dns.RcodeSuccess),
|
||||||
|
HaveReason("Test")))
|
||||||
|
})
|
||||||
|
|
||||||
|
When("subnet mask is 24", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
sutConfig.IPv4Mask = 24
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should modify ECS information", func(ctx context.Context) {
|
||||||
|
request := newRequest("example.com.", A)
|
||||||
|
request.ClientIP = origIP
|
||||||
|
|
||||||
|
addEcsOption(request.Req, ecsIP, ecsMaskIPv4)
|
||||||
|
|
||||||
|
m.ResolveFn = func(ctx context.Context, req *Request) (*Response, error) {
|
||||||
|
Expect(req.ClientIP).Should(Equal(ecsIP))
|
||||||
|
Expect(req.Req).Should(HaveEdnsOption(dns.EDNS0SUBNET))
|
||||||
|
|
||||||
|
so := util.GetEdns0Option[*dns.EDNS0_SUBNET](req.Req)
|
||||||
|
Expect(so.Address).Should(Equal(net.ParseIP("4.3.2.0").To4()))
|
||||||
|
|
||||||
|
return respondWith(mockAnswer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(sut.Resolve(ctx, request)).
|
||||||
|
Should(
|
||||||
|
SatisfyAll(
|
||||||
|
HaveNoAnswer(),
|
||||||
|
HaveResponseType(ResponseTypeRESOLVED),
|
||||||
|
HaveReturnCode(dns.RcodeSuccess),
|
||||||
|
HaveReason("Test")))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should forward ECS information with subnet 128", func(ctx context.Context) {
|
||||||
|
request := newRequest("example.com.", AAAA)
|
||||||
|
request.ClientIP = net.ParseIP("2001:db8::68")
|
||||||
|
|
||||||
|
addEcsOption(request.Req, net.ParseIP("2001:db8::68"), 128)
|
||||||
|
|
||||||
|
m.ResolveFn = func(ctx context.Context, req *Request) (*Response, error) {
|
||||||
|
Expect(req.Req).Should(HaveEdnsOption(dns.EDNS0SUBNET))
|
||||||
|
|
||||||
|
so := util.GetEdns0Option[*dns.EDNS0_SUBNET](req.Req)
|
||||||
|
Expect(so.Address).Should(Equal(net.ParseIP("2001:db8::68")))
|
||||||
|
|
||||||
|
return respondWith(mockAnswer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
Expect(sut.Resolve(ctx, request)).
|
||||||
|
Should(
|
||||||
|
SatisfyAll(
|
||||||
|
HaveNoAnswer(),
|
||||||
|
HaveResponseType(ResponseTypeRESOLVED),
|
||||||
|
HaveReturnCode(dns.RcodeSuccess),
|
||||||
|
HaveReason("Test")))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("maskIP", func() {
|
Context("maskIP", func() {
|
||||||
|
|
Loading…
Reference in New Issue