From 3bc7ff99ccea861849a9b22e088ecccb0114bfae Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 17 Jun 2026 16:07:33 +0530 Subject: [PATCH 01/10] feat(go): make skip tls verify configurable; --- logic/settings.go | 4 ++++ models/settings.go | 1 + pro/email/email.go | 1 + pro/email/smtp.go | 3 ++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/logic/settings.go b/logic/settings.go index 9b00ff5aa..262eaa8d1 100644 --- a/logic/settings.go +++ b/logic/settings.go @@ -345,6 +345,10 @@ func GetSmtpPort() int { return GetServerSettings().SmtpPort } +func SmtpSkipTlsVerify() bool { + return GetServerSettings().SmtpSkipTlsVerify +} + func GetSenderEmail() string { return GetServerSettings().EmailSenderAddr } diff --git a/models/settings.go b/models/settings.go index 8cb52d6d7..d35ebb0d9 100644 --- a/models/settings.go +++ b/models/settings.go @@ -41,6 +41,7 @@ type ServerSettings struct { EmailSenderPassword string `json:"email_sender_password"` SmtpHost string `json:"smtp_host"` SmtpPort int `json:"smtp_port"` + SmtpSkipTlsVerify bool `json:"smtp_skip_tls_verify"` MetricInterval string `json:"metric_interval"` MetricsPort int `json:"metrics_port"` // IPDetectionInterval is the interval (in seconds) at which devices check for changes in public ip. diff --git a/pro/email/email.go b/pro/email/email.go index 12870c6ad..f3dc24ecf 100644 --- a/pro/email/email.go +++ b/pro/email/email.go @@ -24,6 +24,7 @@ func Init() { SenderEmail: logic.GetSenderEmail(), SendUser: logic.GetSenderUser(), SenderPass: logic.GetEmaiSenderPassword(), + SkipVerify: logic.SmtpSkipTlsVerify(), } if smtpSender.SendUser == "" { smtpSender.SendUser = smtpSender.SenderEmail diff --git a/pro/email/smtp.go b/pro/email/smtp.go index 4c96e5b09..48a31c200 100644 --- a/pro/email/smtp.go +++ b/pro/email/smtp.go @@ -13,6 +13,7 @@ type SmtpSender struct { SenderEmail string SendUser string SenderPass string + SkipVerify bool } func (s *SmtpSender) SendEmail(ctx context.Context, n Notification, e Mail) error { @@ -32,7 +33,7 @@ func (s *SmtpSender) SendEmail(ctx context.Context, n Notification, e Mail) erro // This is only needed when SSL/TLS certificate is not valid on server. // In production this should be set to false. - d.TLSConfig = &tls.Config{InsecureSkipVerify: true} + d.TLSConfig = &tls.Config{InsecureSkipVerify: s.SkipVerify} // Now send E-Mail if err := d.DialAndSend(m); err != nil { From 02f32ac0940d6eae7c895aee41f254ff4cdfa6e7 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 17 Jun 2026 16:26:00 +0530 Subject: [PATCH 02/10] feat(go): skip tls verification for older deployments when tls verification wasn't configurable; --- migrate/migrate.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migrate/migrate.go b/migrate/migrate.go index fec52c70c..0e0ec83a0 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -564,6 +564,13 @@ func migrateSettings() { if settings.StunServers == "" { settings.StunServers = servercfg.GetStunServers() } + if settings.SmtpHost != "" { + _, ok := settingsD["smtp_skip_tls_verify"] + if !ok { + // skip tls verification for older deployments when tls verification wasn't configurable. + settings.SmtpSkipTlsVerify = true + } + } logic.UpsertServerSettings(settings) } From 9ea8fbdbe73232b492744a3042d5248a8f7a3847 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 17 Jun 2026 22:03:48 +0530 Subject: [PATCH 03/10] feat(go): ensure server name set just in case tls verification is skipped; --- pro/email/smtp.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pro/email/smtp.go b/pro/email/smtp.go index 48a31c200..209af22da 100644 --- a/pro/email/smtp.go +++ b/pro/email/smtp.go @@ -31,9 +31,10 @@ func (s *SmtpSender) SendEmail(ctx context.Context, n Notification, e Mail) erro // Settings for SMTP server d := gomail.NewDialer(s.SmtpHost, s.SmtpPort, s.SendUser, s.SenderPass) - // This is only needed when SSL/TLS certificate is not valid on server. - // In production this should be set to false. - d.TLSConfig = &tls.Config{InsecureSkipVerify: s.SkipVerify} + d.TLSConfig = &tls.Config{ + ServerName: s.SmtpHost, + InsecureSkipVerify: s.SkipVerify, + } // Now send E-Mail if err := d.DialAndSend(m); err != nil { From cf1cfae812158e64a9cd90802ca92cbba0a29bca Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Fri, 19 Jun 2026 17:01:46 +0530 Subject: [PATCH 04/10] fix(go): delete old tag regardless if tag being updated; --- pro/logic/tags.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pro/logic/tags.go b/pro/logic/tags.go index 01ab56bfd..813ae30ca 100644 --- a/pro/logic/tags.go +++ b/pro/logic/tags.go @@ -201,9 +201,7 @@ func UpdateTag(req models.UpdateTagReq, newID models.TagID) { // unassign old tag if _, ok := extclient.Tags[req.ID]; ok { - if newID != "" { - delete(extclient.Tags, req.ID) - } + delete(extclient.Tags, req.ID) } // assign tag if in taggedExtclientIDs. From 063bfb844ae4b5ac206212649a9a58e45682c734 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Fri, 19 Jun 2026 18:17:52 +0530 Subject: [PATCH 05/10] fix(go): remove unnecessary guard around delete; --- pro/logic/tags.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pro/logic/tags.go b/pro/logic/tags.go index 813ae30ca..c5e2b5c13 100644 --- a/pro/logic/tags.go +++ b/pro/logic/tags.go @@ -200,9 +200,7 @@ func UpdateTag(req models.UpdateTagReq, newID models.TagID) { } // unassign old tag - if _, ok := extclient.Tags[req.ID]; ok { - delete(extclient.Tags, req.ID) - } + delete(extclient.Tags, req.ID) // assign tag if in taggedExtclientIDs. if _, ok := taggedExtclientIDs[extclient.ClientID]; ok { From 35e6cb8c0ffdaa5d8d13cb4eedb8aaf081e5e136 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Mon, 22 Jun 2026 10:41:32 +0530 Subject: [PATCH 06/10] fix(go): skip extclient filtering for auditors; --- controllers/ext_client.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index e963a644a..ff557a62a 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -92,18 +92,20 @@ func getNetworkExtClients(w http.ResponseWriter, r *http.Request) { } err := user.Get(r.Context()) if err == nil { - userRole := &schema.UserRole{ - ID: user.PlatformRoleID, - } - err := userRole.Get(r.Context()) - if err != nil || !userRole.FullAccess { - filtered := []models.ExtClient{} - for _, ec := range extclients { - if logic.IsUserAllowedAccessToExtClient(username, ec) { - filtered = append(filtered, ec) + if user.PlatformRoleID != schema.Auditor { + userRole := &schema.UserRole{ + ID: user.PlatformRoleID, + } + err := userRole.Get(r.Context()) + if err != nil || !userRole.FullAccess { + filtered := []models.ExtClient{} + for _, ec := range extclients { + if logic.IsUserAllowedAccessToExtClient(username, ec) { + filtered = append(filtered, ec) + } } + extclients = filtered } - extclients = filtered } } } From 01d772ffc04004e1ee7e3bd86ca1d245f593c80d Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Mon, 22 Jun 2026 10:48:32 +0530 Subject: [PATCH 07/10] fix(go): deny access to all users who don't own the extclient; --- controllers/ext_client.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index ff557a62a..4f89eed17 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -214,6 +214,23 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) { return } + username := r.Header.Get("user") + if r.Header.Get("ismaster") != "yes" { + user := &schema.User{ + Username: username, + } + err := user.Get(r.Context()) + if err == nil { + if user.PlatformRoleID != schema.SuperAdminRole && + user.PlatformRoleID != schema.AdminRole && + user.Username != client.OwnerID { + err = fmt.Errorf("access denied") + logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Forbidden)) + return + } + } + } + gwnode, err := logic.GetNodeByID(client.IngressGatewayID) if err != nil { logger.Log( From 4056703f4663444b9759d50bb9aea442c9c98bec Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Mon, 22 Jun 2026 16:07:54 +0530 Subject: [PATCH 08/10] fix(go): return error if qrcode exceeds qrcode spec hardlimits; --- controllers/ext_client.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 4f89eed17..615656369 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -408,10 +408,17 @@ Endpoint = %s ) if params["type"] == "qr" { - bytes, err := qrcode.Encode(config, qrcode.Medium, -5) + bytes, err := qrcode.Encode(config, qrcode.Low, -5) if err != nil { logger.Log(1, r.Header.Get("user"), "failed to encode qr code: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + if strings.Contains(err.Error(), "content too long to encode") { + logic.ReturnErrorResponse(w, r, logic.FormatError( + fmt.Errorf("config is too large to encode as a QR code; please use the file download instead"), + "badrequest", + )) + } else { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + } return } w.Header().Set("Content-Type", "image/png") From 7d121066e56c86786d15ddc517d08e2fd1e55ca3 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Tue, 23 Jun 2026 08:47:25 +0530 Subject: [PATCH 09/10] fix(go): allow network admins access to list and view extclient configs; --- controllers/ext_client.go | 13 ++++++++----- logic/user_mgmt.go | 1 + pro/initialize.go | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 615656369..38b7ddb63 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -98,13 +98,15 @@ func getNetworkExtClients(w http.ResponseWriter, r *http.Request) { } err := userRole.Get(r.Context()) if err != nil || !userRole.FullAccess { - filtered := []models.ExtClient{} - for _, ec := range extclients { - if logic.IsUserAllowedAccessToExtClient(username, ec) { - filtered = append(filtered, ec) + if !logic.IsNetworkAdmin(user, network) { + var filtered []models.ExtClient + for _, ec := range extclients { + if logic.IsUserAllowedAccessToExtClient(username, ec) { + filtered = append(filtered, ec) + } } + extclients = filtered } - extclients = filtered } } } @@ -223,6 +225,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) { if err == nil { if user.PlatformRoleID != schema.SuperAdminRole && user.PlatformRoleID != schema.AdminRole && + !logic.IsNetworkAdmin(user, networkid) && user.Username != client.OwnerID { err = fmt.Errorf("access denied") logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Forbidden)) diff --git a/logic/user_mgmt.go b/logic/user_mgmt.go index 1288332ea..7d4a5e6e1 100644 --- a/logic/user_mgmt.go +++ b/logic/user_mgmt.go @@ -87,6 +87,7 @@ var AddGlobalGroupOnRoleUpgrade = func(oldRole, newRole schema.UserRoleID, group var PlatformRoleRequiresGroupEnforcement = func(role schema.UserRoleID) bool { return false } var UserHasGlobalNetworksAdminMembership = func(user *schema.User) bool { return false } var UserHasNetworkGroupAccess = func(user *schema.User, networkID string) bool { return false } +var IsNetworkAdmin = func(user *schema.User, networkID string) bool { return false } var CanUserCreateNetwork = func(ctx context.Context, username string) bool { return true } var EmailInit = func() {} diff --git a/pro/initialize.go b/pro/initialize.go index 26bbea938..57037eaff 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -179,6 +179,7 @@ func InitPro() { logic.PlatformRoleRequiresGroupEnforcement = proLogic.PlatformRoleRequiresGroupEnforcement logic.UserHasGlobalNetworksAdminMembership = proLogic.UserHasGlobalNetworksAdminMembership logic.UserHasNetworkGroupAccess = proLogic.UserHasNetworkGroupAccess + logic.IsNetworkAdmin = proLogic.IsNetworkAdmin logic.CanUserCreateNetwork = proLogic.CanUserCreateNetwork logic.GetUserGroup = proLogic.GetUserGroup From 379a43dc81ae076da1e5f8d45bd04bbc8c2b2d84 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Tue, 23 Jun 2026 08:53:57 +0530 Subject: [PATCH 10/10] fix(go): allow only platform users with network admins to list all extclients and get wg config; --- controllers/ext_client.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 38b7ddb63..e82648042 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -98,7 +98,8 @@ func getNetworkExtClients(w http.ResponseWriter, r *http.Request) { } err := userRole.Get(r.Context()) if err != nil || !userRole.FullAccess { - if !logic.IsNetworkAdmin(user, network) { + if (user.PlatformRoleID == schema.PlatformUser && !logic.IsNetworkAdmin(user, network)) || + user.PlatformRoleID != schema.PlatformUser { var filtered []models.ExtClient for _, ec := range extclients { if logic.IsUserAllowedAccessToExtClient(username, ec) { @@ -225,7 +226,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) { if err == nil { if user.PlatformRoleID != schema.SuperAdminRole && user.PlatformRoleID != schema.AdminRole && - !logic.IsNetworkAdmin(user, networkid) && + !(user.PlatformRoleID == schema.PlatformUser && logic.IsNetworkAdmin(user, networkid)) && user.Username != client.OwnerID { err = fmt.Errorf("access denied") logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Forbidden))