From 789b8d1665be61ae24e9753bc9e22d5272b9a53a Mon Sep 17 00:00:00 2001 From: houyuwushang Date: Thu, 27 Aug 2026 05:13:48 +0800 Subject: [PATCH] plugin/tsig: expose validated TSIG key identity (#8471) Store the normalized key name in the request context only after successful TSIG verification. This lets downstream plugins distinguish unsigned requests from authenticated requests and authorize by key without relying on the stripped TSIG RR or exposing secret material. Signed-off-by: houyuwushang --- plugin/tsig/README.md | 4 ++++ plugin/tsig/context.go | 17 +++++++++++++++++ plugin/tsig/tsig.go | 1 + plugin/tsig/tsig_test.go | 15 ++++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 plugin/tsig/context.go 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)