Repository: netbirdio/netbird

Issue:
Nameserver cannot be scoped to TLD
**Describe the problem**

Netbird currently does not allow you to pass a top level domain to resolve.

Example: one cannot have a match domain scoped to `consul.`, that would allow to resolve anything under *.consul

I believe this is only a GUI issue in the dashboard, and the feature should (???) work, it just cannot be set because of some UI input filter.

**To Reproduce**

Steps to reproduce the behavior:
1. Go to DNS settings
2. Try adding a domain match that is a TLD (com, fr, eu, etc..)

**Expected behavior**

match domain should support TLD scope

**Are you using NetBird Cloud?**

No

**NetBird version**

0.27.3


Hints from issue comments:
@pascal-fischer: Hi @ednxzu, yes that seems to be a limitation of the front end right now. We are going to fix that. Till then if you need this earlier you can try setting the domain by calling the API directly.@taylorwilsdon: Hey @pascal-fischer, also very curious about this one but I tried via api (route `/api/dns/nameservers/{nameserverid}`) and when including any of the following:

`consul`
`.consul`
`consul.` 

in the body payload under "domains", it will return a 422 (Unprocessable content) - if you change that to `abc.consul` with nothing else different it goes through. The message in the response is:
`{"message":"nameserver group got an invalid domain: consul \"domain should consists of only letters, numbers, and hyphens with no leading, trailing hyphens, or spaces\"","code":422}`

So it does appear the same format checks present in the UI are also present in the backend. 
The regex used to dictate this is declared here: https://github.com/netbirdio/netbird/blob/main/management/server/nameserver.go#L17

You can confirm at regex101.com that for the current regular expression `^(?i)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$` the test string "consul.com" validates but simply "consul" or "consul." does not. @pascal-fischer: Ah you are right, my bad. Then we need to update and test this on the backend as well.@nazarewk: Aggregation of TLD-related feature requests:
- https://github.com/netbirdio/netbird/issues/2496
- https://github.com/netbirdio/netbird/issues/2593
- https://github.com/netbirdio/netbird/issues/2227
- https://github.com/netbirdio/netbird/issues/2921 (this is about Network Routes, not Nameservers, so not really related)

---

This  https://github.com/netbirdio/netbird/issues/2593#issuecomment-2881634381 lists some implementation details worth noting when we finally decide to implement the feature fully@Cyberbeni: Hi, I think this should also be changed for `Settings > Networks > DNS Domain`.

I want to have a setup where people can use the same address whether they are at home or connected through NetBird. iOS/macOS devices don't resolve `server` to `server.local` out of the box (and typing `server` into your browser will search for it, so people would have to type `http://server/` instead, so using `server.local` would be preferred).

In my test setup, I currently also have AdGuard Home to direct `server.local` to the NetBird address (With NetBird set up to use it as a DNS), which almost works as expected. It adds a noticeable delay compared to using `server.netbird.cloud` and if I want to use it as a DNS at home without NetBird running then `server.local` doesn't work.

@mlsmaycon Should I open a separate issue for this?

Solution patch:
diff --git a/management/server/nameserver.go b/management/server/nameserver.go
index 797d7c11..18339335 100644
--- a/management/server/nameserver.go
+++ b/management/server/nameserver.go
@@ -18,7 +18,9 @@ import (
 	"github.com/netbirdio/netbird/management/server/types"
 )
 
-const domainPattern = `^(?i)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$`
+const domainPattern = `^(?i)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*[*.a-z]{1,}$`
+
+var invalidDomainName = errors.New("invalid domain name")
 
 // GetNameServerGroup gets a nameserver group object from account and nameserver group IDs
 func (am *DefaultAccountManager) GetNameServerGroup(ctx context.Context, accountID, userID, nsGroupID string) (*nbdns.NameServerGroup, error) {
@@ -319,13 +321,9 @@ func validateDomain(domain string) error {
 		return errors.New("domain should consists of only letters, numbers, and hyphens with no leading, trailing hyphens, or spaces")
 	}
 
-	labels, valid := dns.IsDomainName(domain)
+	_, valid := dns.IsDomainName(domain)
 	if !valid {
-		return errors.New("invalid domain name")
-	}
-
-	if labels < 2 {
-		return errors.New("domain should consists of a minimum of two labels")
+		return invalidDomainName
 	}
 
 	return nil



Test patch:
diff --git a/management/server/nameserver_test.go b/management/server/nameserver_test.go
index 1ba79079..dc4be594 100644
--- a/management/server/nameserver_test.go
+++ b/management/server/nameserver_test.go
@@ -899,13 +899,33 @@ func TestValidateDomain(t *testing.T) {
 			errFunc: require.NoError,
 		},
 		{
-			name:    "Invalid domain name with double hyphen",
-			domain:  "test--example.com",
+			name:    "Valid domain name with only one label",
+			domain:  "example",
+			errFunc: require.NoError,
+		},
+		{
+			name:    "Valid domain name with trailing dot",
+			domain:  "example.",
+			errFunc: require.NoError,
+		},
+		{
+			name:    "Invalid wildcard domain name",
+			domain:  "*.example",
 			errFunc: require.Error,
 		},
 		{
-			name:    "Invalid domain name with only one label",
-			domain:  "com",
+			name:    "Invalid domain name with leading dot",
+			domain:  ".com",
+			errFunc: require.Error,
+		},
+		{
+			name:    "Invalid domain name with dot only",
+			domain:  ".",
+			errFunc: require.Error,
+		},
+		{
+			name:    "Invalid domain name with double hyphen",
+			domain:  "test--example.com",
 			errFunc: require.Error,
 		},
 		{