Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NET-1603: Manage DNS NM changes #3124

Merged
merged 11 commits into from
Oct 29, 2024
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ type ServerConfig struct {
SmtpHost string `json:"smtp_host"`
SmtpPort int `json:"smtp_port"`
MetricInterval string `yaml:"metric_interval"`
ManageDNS bool `yaml:"manage_dns"`
DefaultDomain string `yaml:"default_domain"`
}

// SQLConfig - Generic SQL Config
Expand Down
38 changes: 38 additions & 0 deletions controllers/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
)

Expand All @@ -24,6 +25,8 @@ func dnsHandlers(r *mux.Router) {
Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}", logic.SecurityCheck(true, http.HandlerFunc(getDNS))).
Methods(http.MethodGet)
r.HandleFunc("/api/dns/adm/{network}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncDNS))).
Methods(http.MethodPost)
r.HandleFunc("/api/dns/{network}", logic.SecurityCheck(true, http.HandlerFunc(createDNS))).
Methods(http.MethodPost)
r.HandleFunc("/api/dns/adm/pushdns", logic.SecurityCheck(true, http.HandlerFunc(pushDNS))).
Expand Down Expand Up @@ -264,3 +267,38 @@ func pushDNS(w http.ResponseWriter, r *http.Request) {
logger.Log(1, r.Header.Get("user"), "pushed DNS updates to nameserver")
json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
}

// @Summary Sync DNS entries for a given network
// @Router /api/dns/adm/{network}/sync [post]
// @Tags DNS
// @Accept json
// @Success 200 {string} string "DNS Sync completed successfully"
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func syncDNS(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
if !servercfg.GetManageDNS() {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("manage DNS is set to false"), "badrequest"),
)
return
}
var params = mux.Vars(r)
netID := params["network"]
k, err := logic.GetDNS(netID)
if err == nil && len(k) > 0 {
err = mq.PushSyncDNS(k)
}

if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("Failed to Sync DNS entries to network %s: %v", netID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "DNS Sync complelted successfully")
json.NewEncoder(w).Encode("DNS Sync completed successfully")
}
16 changes: 16 additions & 0 deletions controllers/ext_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
} else if gwnode.IngressDNS != "" {
defaultDNS = "DNS = " + gwnode.IngressDNS
}
if servercfg.GetManageDNS() {
if gwnode.Address6.IP != nil {
if defaultDNS == "" {
defaultDNS = "DNS = " + gwnode.Address6.IP.String()
} else {
defaultDNS = defaultDNS + ", " + gwnode.Address6.IP.String()
}
}
if gwnode.Address.IP != nil {
if defaultDNS == "" {
defaultDNS = "DNS = " + gwnode.Address.IP.String()
} else {
defaultDNS = defaultDNS + ", " + gwnode.Address.IP.String()
}
}
}

defaultMTU := 1420
if host.MTU != 0 {
Expand Down
1 change: 1 addition & 0 deletions logic/peers.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}
}

hostPeerUpdate.ManageDNS = servercfg.GetManageDNS()
return hostPeerUpdate, nil
}

Expand Down
1 change: 1 addition & 0 deletions models/mqtt.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type HostPeerUpdate struct {
FwUpdate FwUpdate `json:"fw_update"`
ReplacePeers bool `json:"replace_peers"`
EndpointDetection bool `json:"endpoint_detection"`
ManageDNS bool `yaml:"manage_dns"`
}

// IngressInfo - struct for ingress info
Expand Down
2 changes: 2 additions & 0 deletions models/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ type ServerConfig struct {
IsPro bool `yaml:"isee" json:"Is_EE"`
TrafficKey []byte `yaml:"traffickey"`
MetricInterval string `yaml:"metric_interval"`
ManageDNS bool `yaml:"manage_dns"`
DefaultDomain string `yaml:"default_domain"`
}

// User.NameInCharset - returns if name is in charset below or not
Expand Down
3 changes: 3 additions & 0 deletions mq/mq.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove periodic sync check, instead this can be published when peer update is pushed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to peerUpdate in new commit. please review again.

Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ func Keepalive(ctx context.Context) {
case <-ctx.Done():
return
case <-time.After(time.Second * KEEPALIVE_TIMEOUT):
if servercfg.GetManageDNS() {
sendDNSSync()
}
sendPeers()
}
}
Expand Down
46 changes: 46 additions & 0 deletions mq/publishers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

var batchSize = servercfg.GetPeerUpdateBatchSize()
var batchUpdate = servercfg.GetBatchPeerUpdate()
var manageDNSCache = map[string]int{}

// PublishPeerUpdate --- determines and publishes a peer update to all the hosts
func PublishPeerUpdate(replacePeers bool) error {
Expand Down Expand Up @@ -249,3 +250,48 @@ func sendPeers() {
}
}
}

func sendDNSSync() error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this func should be simplified to all get all entries from the DB and push it to clients

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the cache in new commit. Please help review again.


networks, err := logic.GetNetworks()
if err == nil && len(networks) > 0 {
for _, v := range networks {
k, err := logic.GetDNS(v.NetID)
if err == nil && len(k) > 0 {
if manageDNSCache[v.NetID] != len(k) {
err = PushSyncDNS(k)
if err != nil {
slog.Warn("error publishing dns entry data for network ", v.NetID, err.Error())
continue
}
manageDNSCache[v.NetID] = len(k)
}
continue
}
slog.Warn("error getting DNS entries for network ", v.NetID, err.Error())
}
return nil
}
return err
}

func PushSyncDNS(dnsEntries []models.DNSEntry) error {
logger.Log(2, "----> Pushing Sync DNS")
data, err := json.Marshal(dnsEntries)
if err != nil {
return errors.New("failed to marshal DNS entries: " + err.Error())
}
if mqclient == nil || !mqclient.IsConnectionOpen() {
return errors.New("cannot publish ... mqclient not connected")
}
if token := mqclient.Publish(fmt.Sprintf("host/dns/sync/%s", dnsEntries[0].Network), 0, true, data); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
var err error
if token.Error() == nil {
err = errors.New("connection timeout")
} else {
err = token.Error()
}
return err
}
return nil
}
2 changes: 2 additions & 0 deletions scripts/netmaker.default.env
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,6 @@ EMAIL_SENDER_PASSWORD=
PEER_UPDATE_BATCH=true
# batch peer update size when PEER_UPDATE_BATCH is enabled
PEER_UPDATE_BATCH_SIZE=50
# default domain for internal DNS lookup
DEFAULT_DOMAIN=netmaker.hosted

38 changes: 37 additions & 1 deletion servercfg/serverconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import (
"io"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"

"github.com/gravitl/netmaker/config"

"github.com/gravitl/netmaker/models"
"golang.org/x/exp/slog"
)

// EmqxBrokerType denotes the broker type for EMQX MQTT
Expand Down Expand Up @@ -92,6 +93,8 @@ func GetServerConfig() config.ServerConfig {
cfg.JwtValidityDuration = GetJwtValidityDuration()
cfg.RacAutoDisable = GetRacAutoDisable()
cfg.MetricInterval = GetMetricInterval()
cfg.ManageDNS = GetManageDNS()
cfg.DefaultDomain = GetDefaultDomain()
return cfg
}

Expand Down Expand Up @@ -136,6 +139,8 @@ func GetServerInfo() models.ServerConfig {
cfg.Version = GetVersion()
cfg.IsPro = IsPro
cfg.MetricInterval = GetMetricInterval()
cfg.ManageDNS = GetManageDNS()
cfg.DefaultDomain = GetDefaultDomain()
return cfg
}

Expand Down Expand Up @@ -650,6 +655,37 @@ func GetMetricInterval() string {
return mi
}

// GetManageDNS - if manage DNS enabled or not
func GetManageDNS() bool {
enabled := true
if os.Getenv("MANAGE_DNS") != "" {
enabled = os.Getenv("MANAGE_DNS") == "true"
}
return enabled
}

// GetDefaultDomain - get the default domain
func GetDefaultDomain() string {
//default netmaker.hosted
domain := "netmaker.hosted"
if os.Getenv("DEFAULT_DOMAIN") != "" {
if validateDomain(os.Getenv("DEFAULT_DOMAIN")) {
domain = os.Getenv("DEFAULT_DOMAIN")
} else {
slog.Warn("invalid value, set to default domain: netmaker.hosted", "warn", os.Getenv("DEFAULT_DOMAIN"))
}
}
return domain
}

func validateDomain(domain string) bool {
domainPattern := `[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}(\.[a-zA-Z0-9][a-zA-Z0-9_-]{0,62})*(\.[a-zA-Z][a-zA-Z0-9]{0,10}){1}`

exp := regexp.MustCompile("^" + domainPattern + "$")

return exp.MatchString(domain)
}

// GetBatchPeerUpdate - if batch peer update
func GetBatchPeerUpdate() bool {
enabled := true
Expand Down
29 changes: 29 additions & 0 deletions servercfg/serverconf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package servercfg

import (
"testing"

"github.com/matryer/is"
)

func TestValidateDomain(t *testing.T) {

t.Run("", func(t *testing.T) {
is := is.New(t)
valid := validateDomain("netmaker.hosted")
is.Equal(valid, true)
})

t.Run("", func(t *testing.T) {
is := is.New(t)
valid := validateDomain("ipv4test1.hosted")
is.Equal(valid, true)
})

t.Run("", func(t *testing.T) {
is := is.New(t)
valid := validateDomain("ip_4?")
is.Equal(valid, false)
})

}