Pr 586 tweaks (#594)
* add proxy tcp * add truncated for tcp to udp response * move truncation to scrubbing * add test that executes upstream over tcp * middleware/proxy: some tweaks rename force-tcp to force_tcp to be inline with the rest and use a dnsOptions struct to put the options in to allow it to be extended. Add some parse tests as well. * Fix test and rename dnsOptions Options
This commit is contained in:
parent
5b32a07ae6
commit
5ac6020f45
6 changed files with 94 additions and 7 deletions
|
@ -26,7 +26,7 @@ proxy FROM TO... {
|
||||||
health_check PATH:PORT [DURATION]
|
health_check PATH:PORT [DURATION]
|
||||||
except IGNORED_NAMES...
|
except IGNORED_NAMES...
|
||||||
spray
|
spray
|
||||||
protocol [dns|https_google [bootstrap ADDRESS...]|grpc [insecure|CA-PEM|KEY-PEM CERT-PEM|KEY-PEM CERT-PEM CA-PEM]]
|
protocol [dns [force_tcp]|https_google [bootstrap ADDRESS...]|grpc [insecure|CA-PEM|KEY-PEM CERT-PEM|KEY-PEM CERT-PEM CA-PEM]]
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
@ -71,7 +71,8 @@ Currently `protocol` supports `dns` (i.e., standard DNS over UDP/TCP) and `https
|
||||||
payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and
|
payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and
|
||||||
*Google* can see your DNS activity.
|
*Google* can see your DNS activity.
|
||||||
|
|
||||||
* `dns`: no options can be given at the moment.
|
* `dns`: uses the standard DNS exchange. You can pass `force_tcp` to make sure that the proxied connection is performed
|
||||||
|
over TCP, regardless of the inbound request's protocol.
|
||||||
* `https_google`: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com` to an address to
|
* `https_google`: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com` to an address to
|
||||||
connect to. This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53.
|
connect to. This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53.
|
||||||
Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as
|
Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as
|
||||||
|
|
|
@ -14,10 +14,19 @@ import (
|
||||||
type dnsEx struct {
|
type dnsEx struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
group *singleflight.Group
|
group *singleflight.Group
|
||||||
|
Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
ForceTCP bool // If true use TCP for upstream no matter what
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDNSEx() *dnsEx {
|
func newDNSEx() *dnsEx {
|
||||||
return &dnsEx{group: new(singleflight.Group), Timeout: defaultTimeout * time.Second}
|
return newDNSExWithOption(Options{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDNSExWithOption(opt Options) *dnsEx {
|
||||||
|
return &dnsEx{group: new(singleflight.Group), Timeout: defaultTimeout * time.Second, Options: opt}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsEx) Protocol() string { return "dns" }
|
func (d *dnsEx) Protocol() string { return "dns" }
|
||||||
|
@ -26,7 +35,11 @@ func (d *dnsEx) OnStartup(p *Proxy) error { return nil }
|
||||||
|
|
||||||
// Exchange implements the Exchanger interface.
|
// Exchange implements the Exchanger interface.
|
||||||
func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
|
func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
|
||||||
co, err := net.DialTimeout(state.Proto(), addr, d.Timeout)
|
proto := state.Proto()
|
||||||
|
if d.Options.ForceTCP {
|
||||||
|
proto = "tcp"
|
||||||
|
}
|
||||||
|
co, err := net.DialTimeout(proto, addr, d.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +56,8 @@ func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Make sure it fits in the DNS response.
|
||||||
|
reply, _ = state.Scrub(reply)
|
||||||
reply.Compress = true
|
reply.Compress = true
|
||||||
reply.Id = state.Req.Id
|
reply.Id = state.Req.Id
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,11 @@ import (
|
||||||
|
|
||||||
// NewLookup create a new proxy with the hosts in host and a Random policy.
|
// NewLookup create a new proxy with the hosts in host and a Random policy.
|
||||||
func NewLookup(hosts []string) Proxy {
|
func NewLookup(hosts []string) Proxy {
|
||||||
|
return NewLookupWithOption(hosts, Options{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLookupWithForcedProto process creates a simple round robin forward with potentially forced proto for upstream.
|
||||||
|
func NewLookupWithOption(hosts []string, opts Options) Proxy {
|
||||||
p := Proxy{Next: nil}
|
p := Proxy{Next: nil}
|
||||||
|
|
||||||
upstream := &staticUpstream{
|
upstream := &staticUpstream{
|
||||||
|
@ -23,7 +28,7 @@ func NewLookup(hosts []string) Proxy {
|
||||||
Spray: nil,
|
Spray: nil,
|
||||||
FailTimeout: 10 * time.Second,
|
FailTimeout: 10 * time.Second,
|
||||||
MaxFails: 3, // TODO(miek): disable error checking for simple lookups?
|
MaxFails: 3, // TODO(miek): disable error checking for simple lookups?
|
||||||
ex: newDNSEx(),
|
ex: newDNSExWithOption(opts),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, host := range hosts {
|
for i, host := range hosts {
|
||||||
|
|
|
@ -190,7 +190,16 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
|
||||||
}
|
}
|
||||||
switch encArgs[0] {
|
switch encArgs[0] {
|
||||||
case "dns":
|
case "dns":
|
||||||
|
if len(encArgs) > 1 {
|
||||||
|
if encArgs[1] == "force_tcp" {
|
||||||
|
opts := Options{ForceTCP: true}
|
||||||
|
u.ex = newDNSExWithOption(opts)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("only force_tcp allowed as parameter to dns")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
u.ex = newDNSEx()
|
u.ex = newDNSEx()
|
||||||
|
}
|
||||||
case "https_google":
|
case "https_google":
|
||||||
boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
|
boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
|
||||||
if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
|
if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
|
||||||
|
|
|
@ -198,6 +198,13 @@ proxy . 8.8.8.8:53 {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
|
proxy . 8.8.8.8:53 {
|
||||||
|
protocol dns force_tcp
|
||||||
|
}`,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`
|
||||||
proxy . 8.8.8.8:53 {
|
proxy . 8.8.8.8:53 {
|
||||||
protocol grpc a b c d
|
protocol grpc a b c d
|
||||||
}`,
|
}`,
|
||||||
|
@ -262,6 +269,13 @@ proxy . 8.8.8.8:53 {
|
||||||
`
|
`
|
||||||
proxy . 8.8.8.8:53 {
|
proxy . 8.8.8.8:53 {
|
||||||
health_check
|
health_check
|
||||||
|
}`,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`
|
||||||
|
proxy . 8.8.8.8:53 {
|
||||||
|
protocol dns force
|
||||||
}`,
|
}`,
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,6 +56,50 @@ func TestLookupProxy(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLookupDnsWithForcedTcp(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
name, rm, err := test.TempFile(".", exampleOrg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create zone: %s", err)
|
||||||
|
}
|
||||||
|
defer rm()
|
||||||
|
|
||||||
|
corefile := `example.org:0 {
|
||||||
|
file ` + name + `
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
i, err := CoreDNSServer(corefile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not get CoreDNS serving instance: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, tcp := CoreDNSServerPorts(i, 0)
|
||||||
|
if tcp == "" {
|
||||||
|
t.Fatalf("Could not get TCP listening port")
|
||||||
|
}
|
||||||
|
defer i.Stop()
|
||||||
|
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
|
p := proxy.NewLookupWithOption([]string{tcp}, proxy.Options{ForceTCP: true})
|
||||||
|
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||||
|
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected to receive reply, but didn't")
|
||||||
|
}
|
||||||
|
// expect answer section with A record in it
|
||||||
|
if len(resp.Answer) == 0 {
|
||||||
|
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||||
|
}
|
||||||
|
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||||
|
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||||
|
}
|
||||||
|
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
|
||||||
|
t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkLookupProxy(b *testing.B) {
|
func BenchmarkLookupProxy(b *testing.B) {
|
||||||
t := new(testing.T)
|
t := new(testing.T)
|
||||||
name, rm, err := test.TempFile(".", exampleOrg)
|
name, rm, err := test.TempFile(".", exampleOrg)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue