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 <liuluoqianqiu@outlook.com>
This commit is contained in:
houyuwushang
2026-08-27 05:13:48 +08:00
committed by GitHub
parent ff06b2a593
commit 789b8d1665
4 changed files with 36 additions and 1 deletions

View File

@@ -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
~~~

17
plugin/tsig/context.go Normal file
View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)