dns.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. package logic
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "os"
  9. "regexp"
  10. "sort"
  11. "strings"
  12. validator "github.com/go-playground/validator/v10"
  13. "github.com/gravitl/netmaker/database"
  14. "github.com/gravitl/netmaker/db"
  15. "github.com/gravitl/netmaker/logger"
  16. "github.com/gravitl/netmaker/models"
  17. "github.com/gravitl/netmaker/schema"
  18. "github.com/gravitl/netmaker/servercfg"
  19. "github.com/txn2/txeh"
  20. )
  21. var GetNameserversForNode = getNameserversForNode
  22. var GetNameserversForHost = getNameserversForHost
  23. var ValidateNameserverReq = validateNameserverReq
  24. type GlobalNs struct {
  25. ID string `json:"id"`
  26. IPs []string `json:"ips"`
  27. }
  28. var GlobalNsList = map[string]GlobalNs{
  29. "Google": {
  30. ID: "Google",
  31. IPs: []string{
  32. "8.8.8.8",
  33. "8.8.4.4",
  34. "2001:4860:4860::8888",
  35. "2001:4860:4860::8844",
  36. },
  37. },
  38. "Cloudflare": {
  39. ID: "Cloudflare",
  40. IPs: []string{
  41. "1.1.1.1",
  42. "1.0.0.1",
  43. "2606:4700:4700::1111",
  44. "2606:4700:4700::1001",
  45. },
  46. },
  47. "Quad9": {
  48. ID: "Quad9",
  49. IPs: []string{
  50. "9.9.9.9",
  51. "149.112.112.112",
  52. "2620:fe::fe",
  53. "2620:fe::9",
  54. },
  55. },
  56. }
  57. // SetDNS - sets the dns on file
  58. func SetDNS() error {
  59. hostfile, err := txeh.NewHosts(&txeh.HostsConfig{})
  60. if err != nil {
  61. return err
  62. }
  63. var corefilestring string
  64. networks, err := GetNetworks()
  65. if err != nil && !database.IsEmptyRecord(err) {
  66. return err
  67. }
  68. for _, net := range networks {
  69. corefilestring = corefilestring + net.NetID + " "
  70. dns, err := GetDNS(net.NetID)
  71. if err != nil && !database.IsEmptyRecord(err) {
  72. return err
  73. }
  74. for _, entry := range dns {
  75. hostfile.AddHost(entry.Address, entry.Name)
  76. }
  77. }
  78. dns := GetExtclientDNS()
  79. for _, entry := range dns {
  80. hostfile.AddHost(entry.Address, entry.Name)
  81. }
  82. if corefilestring == "" {
  83. corefilestring = "example.com"
  84. }
  85. err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
  86. if err != nil {
  87. return err
  88. }
  89. /* if something goes wrong with server DNS, check here
  90. // commented out bc we were not using IsSplitDNS
  91. if servercfg.IsSplitDNS() {
  92. err = SetCorefile(corefilestring)
  93. }
  94. */
  95. return err
  96. }
  97. // GetDNS - gets the DNS of a current network
  98. func GetDNS(network string) ([]models.DNSEntry, error) {
  99. dns, err := GetNodeDNS(network)
  100. if err != nil && !database.IsEmptyRecord(err) {
  101. return dns, err
  102. }
  103. customdns, err := GetCustomDNS(network)
  104. if err != nil && !database.IsEmptyRecord(err) {
  105. return dns, err
  106. }
  107. dns = append(dns, customdns...)
  108. return dns, nil
  109. }
  110. func EgressDNs(network string) (entries []models.DNSEntry) {
  111. egs, _ := (&schema.Egress{
  112. Network: network,
  113. }).ListByNetwork(db.WithContext(context.TODO()))
  114. for _, egI := range egs {
  115. if egI.Domain != "" && len(egI.DomainAns) > 0 {
  116. entry := models.DNSEntry{
  117. Name: egI.Domain,
  118. }
  119. for _, domainAns := range egI.DomainAns {
  120. ip, _, err := net.ParseCIDR(domainAns)
  121. if err == nil {
  122. if ip.To4() != nil {
  123. entry.Address = ip.String()
  124. } else {
  125. entry.Address6 = ip.String()
  126. }
  127. }
  128. }
  129. entries = append(entries, entry)
  130. }
  131. }
  132. return
  133. }
  134. // GetExtclientDNS - gets all extclients dns entries
  135. func GetExtclientDNS() []models.DNSEntry {
  136. extclients, err := GetAllExtClients()
  137. if err != nil {
  138. return []models.DNSEntry{}
  139. }
  140. var dns []models.DNSEntry
  141. for _, extclient := range extclients {
  142. var entry = models.DNSEntry{}
  143. entry.Name = fmt.Sprintf("%s.%s", extclient.ClientID, extclient.Network)
  144. entry.Network = extclient.Network
  145. if extclient.Address != "" {
  146. entry.Address = extclient.Address
  147. }
  148. if extclient.Address6 != "" {
  149. entry.Address6 = extclient.Address6
  150. }
  151. dns = append(dns, entry)
  152. }
  153. return dns
  154. }
  155. // GetNodeDNS - gets the DNS of a network node
  156. func GetNodeDNS(network string) ([]models.DNSEntry, error) {
  157. var dns []models.DNSEntry
  158. nodes, err := GetNetworkNodes(network)
  159. if err != nil {
  160. return dns, err
  161. }
  162. defaultDomain := GetDefaultDomain()
  163. for _, node := range nodes {
  164. if node.Network != network {
  165. continue
  166. }
  167. host, err := GetHost(node.HostID.String())
  168. if err != nil {
  169. continue
  170. }
  171. var entry = models.DNSEntry{}
  172. if defaultDomain == "" {
  173. entry.Name = fmt.Sprintf("%s.%s", host.Name, network)
  174. } else {
  175. entry.Name = fmt.Sprintf("%s.%s.%s", host.Name, network, defaultDomain)
  176. }
  177. entry.Network = network
  178. if node.Address.IP != nil {
  179. entry.Address = node.Address.IP.String()
  180. }
  181. if node.Address6.IP != nil {
  182. entry.Address6 = node.Address6.IP.String()
  183. }
  184. dns = append(dns, entry)
  185. }
  186. return dns, nil
  187. }
  188. func GetGwDNS(node *models.Node) string {
  189. if !servercfg.GetManageDNS() {
  190. return ""
  191. }
  192. h, err := GetHost(node.HostID.String())
  193. if err != nil {
  194. return ""
  195. }
  196. if h.DNS != "yes" {
  197. return ""
  198. }
  199. dns := []string{}
  200. if node.Address.IP != nil {
  201. dns = append(dns, node.Address.IP.String())
  202. }
  203. if node.Address6.IP != nil {
  204. dns = append(dns, node.Address6.IP.String())
  205. }
  206. return strings.Join(dns, ",")
  207. }
  208. func SetDNSOnWgConfig(gwNode *models.Node, extclient *models.ExtClient) {
  209. if extclient.DNS != "" {
  210. return
  211. }
  212. extclient.DNS = GetGwDNS(gwNode)
  213. }
  214. // GetCustomDNS - gets the custom DNS of a network
  215. func GetCustomDNS(network string) ([]models.DNSEntry, error) {
  216. var dns []models.DNSEntry
  217. collection, err := database.FetchRecords(database.DNS_TABLE_NAME)
  218. if err != nil {
  219. return dns, err
  220. }
  221. for _, value := range collection { // filter for entries based on network
  222. var entry models.DNSEntry
  223. if err := json.Unmarshal([]byte(value), &entry); err != nil {
  224. continue
  225. }
  226. if entry.Network == network {
  227. dns = append(dns, entry)
  228. }
  229. }
  230. return dns, err
  231. }
  232. // SetCorefile - sets the core file of the system
  233. func SetCorefile(domains string) error {
  234. dir, err := os.Getwd()
  235. if err != nil {
  236. return err
  237. }
  238. err = os.MkdirAll(dir+"/config/dnsconfig", 0744)
  239. if err != nil {
  240. logger.Log(0, "couldnt find or create /config/dnsconfig")
  241. return err
  242. }
  243. corefile := domains + ` {
  244. reload 15s
  245. hosts /root/dnsconfig/netmaker.hosts {
  246. fallthrough
  247. }
  248. forward . 8.8.8.8 8.8.4.4
  249. log
  250. }
  251. `
  252. err = os.WriteFile(dir+"/config/dnsconfig/Corefile", []byte(corefile), 0644)
  253. if err != nil {
  254. return err
  255. }
  256. return err
  257. }
  258. // GetAllDNS - gets all dns entries
  259. func GetAllDNS() ([]models.DNSEntry, error) {
  260. var dns []models.DNSEntry
  261. networks, err := GetNetworks()
  262. if err != nil && !database.IsEmptyRecord(err) {
  263. return []models.DNSEntry{}, err
  264. }
  265. for _, net := range networks {
  266. netdns, err := GetDNS(net.NetID)
  267. if err != nil {
  268. return []models.DNSEntry{}, nil
  269. }
  270. dns = append(dns, netdns...)
  271. }
  272. return dns, nil
  273. }
  274. // GetDNSEntryNum - gets which entry the dns was
  275. func GetDNSEntryNum(domain string, network string) (int, error) {
  276. num := 0
  277. entries, err := GetDNS(network)
  278. if err != nil {
  279. return 0, err
  280. }
  281. for i := 0; i < len(entries); i++ {
  282. if domain == entries[i].Name {
  283. num++
  284. }
  285. }
  286. return num, nil
  287. }
  288. // SortDNSEntrys - Sorts slice of DNSEnteys by their Address alphabetically with numbers first
  289. func SortDNSEntrys(unsortedDNSEntrys []models.DNSEntry) {
  290. sort.Slice(unsortedDNSEntrys, func(i, j int) bool {
  291. return unsortedDNSEntrys[i].Address < unsortedDNSEntrys[j].Address
  292. })
  293. }
  294. // IsNetworkNameValid - checks if a netid of a network uses valid characters
  295. func IsDNSEntryValid(d string) bool {
  296. re := regexp.MustCompile(`^[A-Za-z0-9-.]+$`)
  297. return re.MatchString(d)
  298. }
  299. // ValidateDNSCreate - checks if an entry is valid
  300. func ValidateDNSCreate(entry models.DNSEntry) error {
  301. if !IsDNSEntryValid(entry.Name) {
  302. return errors.New("invalid input. Only uppercase letters (A-Z), lowercase letters (a-z), numbers (0-9), minus sign (-) and dots (.) are allowed")
  303. }
  304. v := validator.New()
  305. _ = v.RegisterValidation("whitespace", func(f1 validator.FieldLevel) bool {
  306. match, _ := regexp.MatchString(`\s`, entry.Name)
  307. return !match
  308. })
  309. _ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
  310. num, err := GetDNSEntryNum(entry.Name, entry.Network)
  311. return err == nil && num == 0
  312. })
  313. _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
  314. _, err := GetParentNetwork(entry.Network)
  315. return err == nil
  316. })
  317. err := v.Struct(entry)
  318. if err != nil {
  319. for _, e := range err.(validator.ValidationErrors) {
  320. logger.Log(1, e.Error())
  321. }
  322. }
  323. return err
  324. }
  325. // ValidateDNSUpdate - validates a DNS update
  326. func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
  327. v := validator.New()
  328. _ = v.RegisterValidation("whitespace", func(f1 validator.FieldLevel) bool {
  329. match, _ := regexp.MatchString(`\s`, entry.Name)
  330. return !match
  331. })
  332. _ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
  333. //if name & net not changing name we are good
  334. if change.Name == entry.Name && change.Network == entry.Network {
  335. return true
  336. }
  337. num, err := GetDNSEntryNum(change.Name, change.Network)
  338. return err == nil && num == 0
  339. })
  340. _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
  341. _, err := GetParentNetwork(change.Network)
  342. return err == nil
  343. })
  344. err := v.Struct(change)
  345. if err != nil {
  346. for _, e := range err.(validator.ValidationErrors) {
  347. logger.Log(1, e.Error())
  348. }
  349. }
  350. return err
  351. }
  352. // DeleteDNS - deletes a DNS entry
  353. func DeleteDNS(domain string, network string) error {
  354. key, err := GetRecordKey(domain, network)
  355. if err != nil {
  356. return err
  357. }
  358. err = database.DeleteRecord(database.DNS_TABLE_NAME, key)
  359. return err
  360. }
  361. // CreateDNS - creates a DNS entry
  362. func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
  363. k, err := GetRecordKey(entry.Name, entry.Network)
  364. if err != nil {
  365. return models.DNSEntry{}, err
  366. }
  367. data, err := json.Marshal(&entry)
  368. if err != nil {
  369. return models.DNSEntry{}, err
  370. }
  371. err = database.Insert(k, string(data), database.DNS_TABLE_NAME)
  372. return entry, err
  373. }
  374. func validateNameserverReq(ns schema.Nameserver) error {
  375. if ns.Name == "" {
  376. return errors.New("name is required")
  377. }
  378. if ns.NetworkID == "" {
  379. return errors.New("network is required")
  380. }
  381. if len(ns.Servers) == 0 {
  382. return errors.New("atleast one nameserver should be specified")
  383. }
  384. _, err := GetNetwork(ns.NetworkID)
  385. if err != nil {
  386. return errors.New("invalid network id")
  387. }
  388. for _, nsIPStr := range ns.Servers {
  389. nsIP := net.ParseIP(nsIPStr)
  390. if nsIP == nil {
  391. return errors.New("invalid nameserver " + nsIPStr)
  392. }
  393. }
  394. if !ns.MatchAll && len(ns.Domains) == 0 {
  395. return errors.New("atleast one match domain is required")
  396. }
  397. if !ns.MatchAll {
  398. for _, domain := range ns.Domains {
  399. if !IsValidMatchDomain(domain.Domain) {
  400. return errors.New("invalid match domain")
  401. }
  402. }
  403. }
  404. // check if valid broadcast peers are added
  405. if len(ns.Nodes) > 0 {
  406. for nodeID := range ns.Nodes {
  407. node, err := GetNodeByID(nodeID)
  408. if err != nil {
  409. return errors.New("invalid node")
  410. }
  411. if node.Network != ns.NetworkID {
  412. return errors.New("invalid network node")
  413. }
  414. }
  415. }
  416. return nil
  417. }
  418. func getNameserversForNode(node *models.Node) (returnNsLi []models.Nameserver) {
  419. filters := make(map[string]bool)
  420. if node.Address.IP != nil {
  421. filters[node.Address.IP.String()] = true
  422. }
  423. if node.Address6.IP != nil {
  424. filters[node.Address6.IP.String()] = true
  425. }
  426. ns := &schema.Nameserver{
  427. NetworkID: node.Network,
  428. }
  429. nsLi, _ := ns.ListByNetwork(db.WithContext(context.TODO()))
  430. for _, nsI := range nsLi {
  431. if !nsI.Status {
  432. continue
  433. }
  434. filteredIps := FilterOutIPs(nsI.Servers, filters)
  435. if len(filteredIps) == 0 {
  436. continue
  437. }
  438. _, all := nsI.Tags["*"]
  439. if all {
  440. for _, domain := range nsI.Domains {
  441. returnNsLi = append(returnNsLi, models.Nameserver{
  442. IPs: filteredIps,
  443. MatchDomain: domain.Domain,
  444. IsSearchDomain: domain.IsSearchDomain,
  445. })
  446. }
  447. continue
  448. }
  449. if _, ok := nsI.Nodes[node.ID.String()]; ok {
  450. for _, domain := range nsI.Domains {
  451. returnNsLi = append(returnNsLi, models.Nameserver{
  452. IPs: filteredIps,
  453. MatchDomain: domain.Domain,
  454. IsSearchDomain: domain.IsSearchDomain,
  455. })
  456. }
  457. }
  458. }
  459. if node.IsInternetGateway {
  460. globalNs := models.Nameserver{
  461. MatchDomain: ".",
  462. }
  463. for _, nsI := range GlobalNsList {
  464. globalNs.IPs = append(globalNs.IPs, nsI.IPs...)
  465. }
  466. returnNsLi = append(returnNsLi, globalNs)
  467. }
  468. return
  469. }
  470. func getNameserversForHost(h *models.Host) (returnNsLi []models.Nameserver) {
  471. if h.DNS != "yes" {
  472. return
  473. }
  474. for _, nodeID := range h.Nodes {
  475. node, err := GetNodeByID(nodeID)
  476. if err != nil {
  477. continue
  478. }
  479. filters := make(map[string]bool)
  480. if node.Address.IP != nil {
  481. filters[node.Address.IP.String()] = true
  482. }
  483. if node.Address6.IP != nil {
  484. filters[node.Address6.IP.String()] = true
  485. }
  486. ns := &schema.Nameserver{
  487. NetworkID: node.Network,
  488. }
  489. nsLi, _ := ns.ListByNetwork(db.WithContext(context.TODO()))
  490. for _, nsI := range nsLi {
  491. if !nsI.Status {
  492. continue
  493. }
  494. filteredIps := FilterOutIPs(nsI.Servers, filters)
  495. if len(filteredIps) == 0 {
  496. continue
  497. }
  498. _, all := nsI.Tags["*"]
  499. if all {
  500. for _, domain := range nsI.Domains {
  501. returnNsLi = append(returnNsLi, models.Nameserver{
  502. IPs: filteredIps,
  503. MatchDomain: domain.Domain,
  504. IsSearchDomain: domain.IsSearchDomain,
  505. })
  506. }
  507. continue
  508. }
  509. if _, ok := nsI.Nodes[node.ID.String()]; ok {
  510. for _, domain := range nsI.Domains {
  511. returnNsLi = append(returnNsLi, models.Nameserver{
  512. IPs: filteredIps,
  513. MatchDomain: domain.Domain,
  514. IsSearchDomain: domain.IsSearchDomain,
  515. })
  516. }
  517. }
  518. }
  519. if node.IsInternetGateway {
  520. globalNs := models.Nameserver{
  521. MatchDomain: ".",
  522. }
  523. for _, nsI := range GlobalNsList {
  524. globalNs.IPs = append(globalNs.IPs, nsI.IPs...)
  525. }
  526. returnNsLi = append(returnNsLi, globalNs)
  527. }
  528. }
  529. return
  530. }
  531. // IsValidMatchDomain reports whether s is a valid "match domain".
  532. // Rules (simple/ASCII):
  533. // - "~." is allowed (match all).
  534. // - Optional leading "~" allowed (e.g., "~example.com").
  535. // - Optional single trailing "." allowed (FQDN form).
  536. // - No wildcards "*", no leading ".", no underscores.
  537. // - Labels: letters/digits/hyphen (LDH), 1–63 chars, no leading/trailing hyphen.
  538. // - Total length (without trailing dot) ≤ 253.
  539. func IsValidMatchDomain(s string) bool {
  540. s = strings.TrimSpace(s)
  541. if s == "" {
  542. return false
  543. }
  544. if s == "~." { // special case: match-all
  545. return true
  546. }
  547. // Strip optional leading "~"
  548. if strings.HasPrefix(s, "~") {
  549. s = s[1:]
  550. if s == "" {
  551. return false
  552. }
  553. }
  554. // Allow exactly one trailing dot
  555. if strings.HasSuffix(s, ".") {
  556. s = s[:len(s)-1]
  557. if s == "" {
  558. return false
  559. }
  560. }
  561. // Disallow leading dot, wildcards, underscores
  562. if strings.HasPrefix(s, ".") || strings.Contains(s, "*") || strings.Contains(s, "_") {
  563. return false
  564. }
  565. // Lowercase for ASCII checks
  566. s = strings.ToLower(s)
  567. // Length check
  568. if len(s) > 253 {
  569. return false
  570. }
  571. // Label regex: LDH, 1–63, no leading/trailing hyphen
  572. reLabel := regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`)
  573. parts := strings.Split(s, ".")
  574. for _, lbl := range parts {
  575. if len(lbl) == 0 || len(lbl) > 63 {
  576. return false
  577. }
  578. if !reLabel.MatchString(lbl) {
  579. return false
  580. }
  581. }
  582. return true
  583. }
  584. // FilterOutIPs removes ips in the filters map from the ips slice.
  585. func FilterOutIPs(ips []string, filters map[string]bool) []string {
  586. var filteredIps []string
  587. for _, ip := range ips {
  588. _, ok := filters[ip]
  589. if !ok {
  590. filteredIps = append(filteredIps, ip)
  591. }
  592. }
  593. return filteredIps
  594. }