diff --git a/plugin/tsig/README.md b/plugin/tsig/README.md index ed4a8f15f..bf325b29a 100644 --- a/plugin/tsig/README.md +++ b/plugin/tsig/README.md @@ -12,6 +12,10 @@ respective plugins sending those requests to sign them using the keys defined by The *tsig* plugin can also require that incoming requests be signed for certain query types, refusing requests that do not comply. +After successfully validating a TSIG record, the plugin adds the normalized key name to the request context. Downstream Go +plugins can call `tsig.ValidatedKeyName(ctx)` to retrieve the key name and distinguish validated requests from unsigned +requests. The value is not set for requests outside the configured zones because the *tsig* plugin does not validate them. + ## Syntax ~~~ diff --git a/plugin/tsig/context.go b/plugin/tsig/context.go new file mode 100644 index 000000000..73e90204e --- /dev/null +++ b/plugin/tsig/context.go @@ -0,0 +1,17 @@ +package tsig + +import "context" + +type validatedKeyNameKey struct{} + +// ValidatedKeyName returns the normalized name of the TSIG key validated by +// the tsig plugin. The boolean is false for unsigned requests and requests the +// plugin did not validate. +func ValidatedKeyName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(validatedKeyNameKey{}).(string) + return name, ok +} + +func withValidatedKeyName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, validatedKeyNameKey{}, name) +} diff --git a/plugin/tsig/tsig.go b/plugin/tsig/tsig.go index ab3dbf672..d387c1d17 100644 --- a/plugin/tsig/tsig.go +++ b/plugin/tsig/tsig.go @@ -71,6 +71,7 @@ func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns. } tsigRR.Error = dns.RcodeSuccess + ctx = withValidatedKeyName(ctx, plugin.Name(tsigRR.Hdr.Name).Normalize()) rcode, err := plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) if err != nil { log.Errorf("request handler returned an error: %v\n", err) diff --git a/plugin/tsig/tsig_test.go b/plugin/tsig/tsig_test.go index dced97d3f..44a109994 100644 --- a/plugin/tsig/tsig_test.go +++ b/plugin/tsig/tsig_test.go @@ -329,6 +329,8 @@ func TestServeDNSTsigNext(t *testing.T) { reqSigned bool expectExtra []uint16 expectNext int + expectValid bool + expectKey string }{ { desc: "Optional TSIG", @@ -338,6 +340,7 @@ func TestServeDNSTsigNext(t *testing.T) { reqSigned: false, expectExtra: []uint16{dns.TypeOPT}, expectNext: 1, + expectValid: false, }, { desc: "Missing TSIG", @@ -355,6 +358,7 @@ func TestServeDNSTsigNext(t *testing.T) { reqSigned: true, expectExtra: []uint16{dns.TypeOPT, dns.TypeTSIG}, expectNext: 1, + expectValid: false, }, { desc: "Bad Status", @@ -373,6 +377,8 @@ func TestServeDNSTsigNext(t *testing.T) { reqSigned: true, expectExtra: []uint16{dns.TypeOPT}, expectNext: 1, + expectValid: true, + expectKey: "test.key.", }, } @@ -385,6 +391,13 @@ func TestServeDNSTsigNext(t *testing.T) { allTypes: tc.tsigRequired, Next: test.HandlerFunc(func(_ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { nextCalled++ + keyName, validated := ValidatedKeyName(_ctx) + if validated != tc.expectValid { + t.Errorf("ValidatedKeyName() validated = %t, want %t", validated, tc.expectValid) + } + if keyName != tc.expectKey { + t.Errorf("ValidatedKeyName() name = %q, want %q", keyName, tc.expectKey) + } if !slices.EqualFunc(r.Extra, tc.expectExtra, func(rr dns.RR, t uint16) bool { return rr.Header().Rrtype == t }) { t.Errorf("expected %v, got %v", tc.expectExtra, r.Extra) } @@ -400,7 +413,7 @@ func TestServeDNSTsigNext(t *testing.T) { r.SetQuestion("test.example.", dns.TypeA) r.Extra = tc.reqExtra if tc.reqSigned { - r.SetTsig("test.key.", dns.HmacSHA256, 300, time.Now().Unix()) + r.SetTsig("TEST.Key", dns.HmacSHA256, 300, time.Now().Unix()) } _, err := tsig.ServeDNS(ctx, w, r)