Skip to content

Allow server name scoping in TXT record for auth #481

@joelverhagen

Description

@joelverhagen

Is your feature request related to a problem? Please describe.

Currently, a TXT record on a DNS name provides wildcard (broad) access to all subdomains over the TXT record (not just immediate subdomains -- all levels, which is a little surprising compared to wildcard SSL certs which only provide one level of subdomain). It also provides all values for the path portion of the server name with a similar wildcard.

Here are the claims in a DNS TXT auth token.

Image

This is for the following TXT record on my personal mcp.joelverhagen.com domain:

v=MCPv1; k=ed25519; p=OHjrTGdvR2dFk1g5uTVNJ4/RxpDLYjVJTtTQlcwW0Jg=

This currently asserts that the domain record controller deserves the same permissions as the MCP server author. In other words, it is not possible to have a DNS record admin provide subsets of names to teams in the path portion of the server name. This is possible with subdomains, like (com.microsoft.foo/* and com.microsoft.bar/*) but not with server names.

At Microsoft we are considering com.microsoft for all DNS parts of the server name and qualifiers only within the path. It would be great to allow name prefixes to be defines in the TXT record.

Describe the solution you'd like

Add an optional n=<pattern> key value pair to the TXT record which internally defaults to * in the authentication service when it is evaluating TXT records.

For example, com.joelverhagen.mcp/foo-* could be expressed as:

v=MCPv1; k=ed25519; p=OHjrTGdvR2dFk1g5uTVNJ4/RxpDLYjVJTtTQlcwW0Jg=; n=com.joelverhagen.mcp/foo-*

This is only 75 characters, well below the 255 character maximum.

This extension would allow the DNS controller to issue key pairs to teams and isolate them to specific name prefixes.

We could opt to make n= only about the path portion if we want to be more succinct but this would prevent the DNS controller from restricting access to subdomains.

It is very cool you have multiple TXT records and the first with a matching key words. This allows zero downtime key rotation as well. Nice job!

The code that would change is here I think:

publicKeys := h.parsePublicKeysFromTXT(txtRecords)
if len(publicKeys) == 0 {
return nil, fmt.Errorf("no valid MCP public keys found in DNS TXT records")
}
// Verify signature with any of the public keys
messageBytes := []byte(timestamp)
signatureValid := false
for _, publicKey := range publicKeys {
if ed25519.Verify(publicKey, messageBytes, signature) {
signatureValid = true
break
}
}
if !signatureValid {
return nil, fmt.Errorf("signature verification failed")
}
// Build permissions for domain and subdomains
permissions := h.buildPermissions(domain)

Describe alternatives you've considered

  • Keep namespacing and permission isolation at the subdomain level not with prefixes in the path name portion.
  • Simply issue N key pairs to the various teams using the same DNS name, conceding that they have shared permissions. This at least allows granular revocation.

Additional context

N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions