plugin/rewrite: streamline the ResponseRule handling. (#4473)

* plugin/rewrite: streamline the ResponseRule handling.

The functionality of a response rule is now completely encapsulated behind
a `ResponseRule` interface. This significantly simplifies the complete
processing flow, it enables more flexible response handling and it is possible
to eliminate lots of state flags, ifs and switches.

Based on the new flexibility the pull request also enables to support a
response name rewrite for all name rewrite types.
To be compatible, an explicit `answer auto` option is added to support
a best effort response rewrite (name and value).
Additionally now all name rewrite rules support additional name and value
reponse rewrite options.

Using this feature it is also possible now to rewrite a complete sub domain
hierarchy to a single domain name combined with a correct rewrite (#2389).

Signed-off-by: Uwe Krueger <uwe.krueger@sap.com>

* revert policy

Signed-off-by: Uwe Krueger <uwe.krueger@sap.com>

Co-authored-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Uwe Krueger
2021-05-04 10:05:45 +02:00
committed by GitHub
parent 696c8731d6
commit 40edf1e566
13 changed files with 889 additions and 521 deletions

View File

@@ -16,9 +16,9 @@ func TestRewriteIllegalName(t *testing.T) {
r, _ := newNameRule("stop", "example.org.", "example..org.")
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
noRevert: true,
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
RevertPolicy: NoRevertPolicy(),
}
ctx := context.TODO()
@@ -55,9 +55,9 @@ func TestRewriteNamePrefixSuffix(t *testing.T) {
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
noRevert: true,
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
RevertPolicy: NoRevertPolicy(),
}
m := new(dns.Msg)
@@ -75,6 +75,237 @@ func TestRewriteNamePrefixSuffix(t *testing.T) {
}
}
func TestRewriteNameNoRewrite(t *testing.T) {
ctx, close := context.WithCancel(context.TODO())
defer close()
tests := []struct {
next string
args []string
question string
expected string
}{
{"stop", []string{"prefix", "foo", "bar"}, "coredns.foo.", "coredns.foo."},
{"stop", []string{"prefix", "foo", "bar."}, "coredns.foo.", "coredns.foo."},
{"stop", []string{"suffix", "com", "org"}, "com.coredns.", "com.coredns."},
{"stop", []string{"suffix", "com", "org."}, "com.coredns.", "com.coredns."},
{"stop", []string{"substring", "service", "svc"}, "com.coredns.", "com.coredns."},
}
for i, tc := range tests {
r, err := newNameRule(tc.next, tc.args...)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
}
m := new(dns.Msg)
m.SetQuestion(tc.question, dns.TypeA)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err = rw.ServeDNS(ctx, rec, m)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
actual := rec.Msg.Answer[0].Header().Name
if actual != tc.expected {
t.Fatalf("Test %d: Expected answer rewrite to %v, got %v", i, tc.expected, actual)
}
}
}
func TestRewriteNamePrefixSuffixNoAutoAnswer(t *testing.T) {
ctx, close := context.WithCancel(context.TODO())
defer close()
tests := []struct {
next string
args []string
question string
expected string
}{
{"stop", []string{"prefix", "foo", "bar"}, "foo.example.com.", "bar.example.com."},
{"stop", []string{"prefix", "foo.", "bar."}, "foo.example.com.", "bar.example.com."},
{"stop", []string{"suffix", "com", "org"}, "foo.example.com.", "foo.example.org."},
{"stop", []string{"suffix", ".com", ".org"}, "foo.example.com.", "foo.example.org."},
{"stop", []string{"suffix", ".ingress.coredns.rocks", "nginx.coredns.rocks"}, "coredns.ingress.coredns.rocks.", "corednsnginx.coredns.rocks."},
}
for i, tc := range tests {
r, err := newNameRule(tc.next, tc.args...)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
}
m := new(dns.Msg)
m.SetQuestion(tc.question, dns.TypeA)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err = rw.ServeDNS(ctx, rec, m)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
actual := rec.Msg.Answer[0].Header().Name
if actual != tc.expected {
t.Fatalf("Test %d: Expected answer rewrite to %v, got %v", i, tc.expected, actual)
}
}
}
func TestRewriteNamePrefixSuffixAutoAnswer(t *testing.T) {
ctx, close := context.WithCancel(context.TODO())
defer close()
tests := []struct {
next string
args []string
question string
rewrite string
expected string
}{
{"stop", []string{"prefix", "foo", "bar", "answer", "auto"}, "foo.example.com.", "bar.example.com.", "foo.example.com."},
{"stop", []string{"prefix", "foo.", "bar.", "answer", "auto"}, "foo.example.com.", "bar.example.com.", "foo.example.com."},
{"stop", []string{"suffix", "com", "org", "answer", "auto"}, "foo.example.com.", "foo.example.org.", "foo.example.com."},
{"stop", []string{"suffix", ".com", ".org", "answer", "auto"}, "foo.example.com.", "foo.example.org.", "foo.example.com."},
{"stop", []string{"suffix", ".ingress.coredns.rocks", "nginx.coredns.rocks", "answer", "auto"}, "coredns.ingress.coredns.rocks.", "corednsnginx.coredns.rocks.", "coredns.ingress.coredns.rocks."},
}
for i, tc := range tests {
r, err := newNameRule(tc.next, tc.args...)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
RevertPolicy: NoRestorePolicy(),
}
m := new(dns.Msg)
m.SetQuestion(tc.question, dns.TypeA)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err = rw.ServeDNS(ctx, rec, m)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rewrite := rec.Msg.Question[0].Name
if rewrite != tc.rewrite {
t.Fatalf("Test %d: Expected question rewrite to %v, got %v", i, tc.rewrite, rewrite)
}
actual := rec.Msg.Answer[0].Header().Name
if actual != tc.expected {
t.Fatalf("Test %d: Expected answer rewrite to %v, got %v", i, tc.expected, actual)
}
}
}
func TestRewriteNameExactAnswer(t *testing.T) {
ctx, close := context.WithCancel(context.TODO())
defer close()
tests := []struct {
next string
args []string
question string
rewrite string
expected string
}{
{"stop", []string{"exact", "coredns.rocks", "service.consul", "answer", "auto"}, "coredns.rocks.", "service.consul.", "coredns.rocks."},
{"stop", []string{"exact", "coredns.rocks.", "service.consul.", "answer", "auto"}, "coredns.rocks.", "service.consul.", "coredns.rocks."},
{"stop", []string{"exact", "coredns.rocks", "service.consul"}, "coredns.rocks.", "service.consul.", "coredns.rocks."},
{"stop", []string{"exact", "coredns.rocks.", "service.consul."}, "coredns.rocks.", "service.consul.", "coredns.rocks."},
{"stop", []string{"exact", "coredns.org.", "service.consul."}, "coredns.rocks.", "coredns.rocks.", "coredns.rocks."},
}
for i, tc := range tests {
r, err := newNameRule(tc.next, tc.args...)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
RevertPolicy: NoRestorePolicy(),
}
m := new(dns.Msg)
m.SetQuestion(tc.question, dns.TypeA)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err = rw.ServeDNS(ctx, rec, m)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rewrite := rec.Msg.Question[0].Name
if rewrite != tc.rewrite {
t.Fatalf("Test %d: Expected question rewrite to %v, got %v", i, tc.rewrite, rewrite)
}
actual := rec.Msg.Answer[0].Header().Name
if actual != tc.expected {
t.Fatalf("Test %d: Expected answer rewrite to %v, got %v", i, tc.expected, actual)
}
}
}
func TestRewriteNameRegexAnswer(t *testing.T) {
ctx, close := context.WithCancel(context.TODO())
defer close()
tests := []struct {
next string
args []string
question string
rewrite string
expected string
}{
{"stop", []string{"regex", "(.*).coredns.rocks", "{1}.coredns.maps", "answer", "auto"}, "foo.coredns.rocks.", "foo.coredns.maps.", "foo.coredns.rocks."},
{"stop", []string{"regex", "(.*).coredns.rocks", "{1}.coredns.maps", "answer", "name", "(.*).coredns.maps", "{1}.coredns.works"}, "foo.coredns.rocks.", "foo.coredns.maps.", "foo.coredns.works."},
{"stop", []string{"regex", "(.*).coredns.rocks", "{1}.coredns.maps"}, "foo.coredns.rocks.", "foo.coredns.maps.", "foo.coredns.maps."},
}
for i, tc := range tests {
r, err := newNameRule(tc.next, tc.args...)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: []Rule{r},
RevertPolicy: NoRestorePolicy(),
}
m := new(dns.Msg)
m.SetQuestion(tc.question, dns.TypeA)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err = rw.ServeDNS(ctx, rec, m)
if err != nil {
t.Fatalf("Test %d: Expected no error, got %s", i, err)
}
rewrite := rec.Msg.Question[0].Name
if rewrite != tc.rewrite {
t.Fatalf("Test %d: Expected question rewrite to %v, got %v", i, tc.rewrite, rewrite)
}
actual := rec.Msg.Answer[0].Header().Name
if actual != tc.expected {
t.Fatalf("Test %d: Expected answer rewrite to %v, got %v", i, tc.expected, actual)
}
}
}
func TestNewNameRule(t *testing.T) {
tests := []struct {
next string
@@ -91,6 +322,26 @@ func TestNewNameRule(t *testing.T) {
{"stop", []string{"regex", "(.*).coredns.rocks", "{1}.coredns.rocks"}, false},
{"stop", []string{"regex", "(.*).coredns.rocks", "{1}.{2}.coredns.rocks"}, true},
{"stop", []string{"regex", "staging.mydomain.com", "aws-loadbalancer-id.us-east-1.elb.amazonaws.com"}, false},
{"stop", []string{"suffix", "staging.mydomain.com", "coredns.rock", "answer"}, true},
{"stop", []string{"suffix", "staging.mydomain.com", "coredns.rock", "answer", "name"}, true},
{"stop", []string{"suffix", "staging.mydomain.com", "coredns.rock", "answer", "other"}, true},
{"stop", []string{"suffix", "staging.mydomain.com", "coredns.rock", "answer", "auto"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "auto"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name"}, true},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "coredns.rock", "staging.mydomain.com"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.{2}.staging.mydomain.com"}, true},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "name", "(.*).coredns.rock"}, true},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"regex", "staging.mydomain.com", "coredns.rock", "answer", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com", "value", "(.*).coredns.rock"}, true},
{"stop", []string{"suffix", "staging.mydomain.com.", "coredns.rock.", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"suffix", "staging.mydomain.com.", "coredns.rock.", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"suffix", "staging.mydomain.com.", "coredns.rock.", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com", "name", "(.*).coredns.rock", "{1}.staging.mydomain.com"}, false},
{"stop", []string{"suffix", "staging.mydomain.com.", "coredns.rock.", "answer", "value", "(.*).coredns.rock", "{1}.staging.mydomain.com", "value", "(.*).coredns.rock"}, true},
}
for i, tc := range tests {
failed := false