diff --git a/assets/tpl/admin_edit_interface.html b/assets/tpl/admin_edit_interface.html
index 3a0380a..a8d870a 100644
--- a/assets/tpl/admin_edit_interface.html
+++ b/assets/tpl/admin_edit_interface.html
@@ -14,12 +14,7 @@
{{template "prt_nav.html" .}}
{{template "prt_footer.html" .}}
diff --git a/assets/tpl/admin_index.html b/assets/tpl/admin_index.html
index 25be8a4..c059ea0 100644
--- a/assets/tpl/admin_index.html
+++ b/assets/tpl/admin_index.html
@@ -14,11 +14,7 @@
{{template "prt_nav.html" .}}
diff --git a/assets/tpl/prt_flashes.html b/assets/tpl/prt_flashes.html
new file mode 100644
index 0000000..f21c787
--- /dev/null
+++ b/assets/tpl/prt_flashes.html
@@ -0,0 +1,5 @@
+{{range $flash := $.Alerts}}
+
+{{end}}
\ No newline at end of file
diff --git a/assets/tpl/prt_nav.html b/assets/tpl/prt_nav.html
index afb071a..c400cad 100644
--- a/assets/tpl/prt_nav.html
+++ b/assets/tpl/prt_nav.html
@@ -7,12 +7,12 @@
- {{with eq $.Session.LoggedIn true}}{{with eq $.Session.IsAdmin true}}
+ {{with eq $.Session.LoggedIn true}}{{with eq $.Session.IsAdmin true}}{{with eq $.Route "/admin/"}}
- {{end}}{{end}}
+ {{end}}{{end}}{{end}}
{{if eq $.Session.LoggedIn true}}
diff --git a/assets/tpl/user_index.html b/assets/tpl/user_index.html
index a8d988b..df90f74 100644
--- a/assets/tpl/user_index.html
+++ b/assets/tpl/user_index.html
@@ -34,6 +34,7 @@
+
|
{{$p.Identifier}} |
{{$p.PublicKey}} |
@@ -72,7 +73,7 @@
{{if not $p.Peer}}
No Traffic data available...
{{else}}
-
{{if $p.DeactivatedAt}}-{{else}}{{$p.Peer.ReceiveBytes}} / {{$p.Peer.TransmitBytes}}{{end}}
+
{{if $p.DeactivatedAt}}-{{else}} {{formatBytes $p.Peer.ReceiveBytes}} / {{formatBytes $p.Peer.TransmitBytes}}{{end}}
{{end}}
diff --git a/go.mod b/go.mod
index 0fc1aed..bb8afa5 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
github.com/kelseyhightower/envconfig v1.4.0
github.com/sirupsen/logrus v1.7.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
+ github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gorm.io/driver/sqlite v1.1.3
diff --git a/internal/common/configuration.go b/internal/common/configuration.go
index 7e649f8..14e58c1 100644
--- a/internal/common/configuration.go
+++ b/internal/common/configuration.go
@@ -62,6 +62,7 @@ type Config struct {
AdminUser string `yaml:"adminUser" envconfig:"ADMIN_USER"` // optional, non LDAP admin user
AdminPassword string `yaml:"adminPass" envconfig:"ADMIN_PASS"`
DatabasePath string `yaml:"database" envconfig:"DATABASE_PATH"`
+ EditableKeys bool `yaml:"editableKeys" envconfig:"EDITABLE_KEYS"`
} `yaml:"core"`
Email MailConfig `yaml:"email"`
LDAP ldap.Config `yaml:"ldap"`
diff --git a/internal/common/iputil.go b/internal/common/util.go
similarity index 76%
rename from internal/common/iputil.go
rename to internal/common/util.go
index 240c620..dbdf60d 100644
--- a/internal/common/iputil.go
+++ b/internal/common/util.go
@@ -1,6 +1,7 @@
package common
import (
+ "fmt"
"net"
"strings"
)
@@ -55,3 +56,18 @@ func ParseStringList(lst string) []string {
func ListToString(lst []string) string {
return strings.Join(lst, ", ")
}
+
+// https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
+func ByteCountSI(b int64) string {
+ const unit = 1000
+ if b < unit {
+ return fmt.Sprintf("%d B", b)
+ }
+ div, exp := int64(unit), 0
+ for n := b / unit; n >= unit; n /= unit {
+ div *= unit
+ exp++
+ }
+ return fmt.Sprintf("%.1f %cB",
+ float64(b)/float64(div), "kMGTPE"[exp])
+}
diff --git a/internal/server/core.go b/internal/server/core.go
index 0038469..f350786 100644
--- a/internal/server/core.go
+++ b/internal/server/core.go
@@ -5,6 +5,7 @@ import (
"errors"
"html/template"
"math/rand"
+ "net/url"
"os"
"path/filepath"
"time"
@@ -26,6 +27,7 @@ const CacheRefreshDuration = 5 * time.Minute
func init() {
gob.Register(SessionData{})
+ gob.Register(FlashData{})
gob.Register(User{})
gob.Register(Device{})
gob.Register(LdapCreateForm{})
@@ -47,7 +49,7 @@ type SessionData struct {
FormData interface{}
}
-type AlertData struct {
+type FlashData struct {
HasAlert bool
Message string
Type string
@@ -126,6 +128,10 @@ func (s *Server) Setup() error {
// Setup http server
s.server = gin.Default()
+ s.server.SetFuncMap(template.FuncMap{
+ "formatBytes": common.ByteCountSI,
+ "urlEncode": url.QueryEscape,
+ })
// Setup templates
log.Infof("Loading templates from: %s", filepath.Join(dir, "/assets/tpl/*.html"))
@@ -205,17 +211,19 @@ func (s *Server) getSessionData(c *gin.Context) SessionData {
return sessionData
}
-func (s *Server) getAlertData(c *gin.Context) AlertData {
- currentSession := s.getSessionData(c)
- alertData := AlertData{
- HasAlert: currentSession.AlertData != "",
- Message: currentSession.AlertData,
- Type: currentSession.AlertType,
+func (s *Server) getFlashes(c *gin.Context) []FlashData {
+ session := sessions.Default(c)
+ flashes := session.Flashes()
+ if err := session.Save(); err != nil {
+ log.Errorf("Failed to store session after setting flash: %v", err)
}
- // Reset alerts
- _ = s.setAlert(c, "", "")
- return alertData
+ flashData := make([]FlashData, len(flashes))
+ for i := range flashes {
+ flashData[i] = flashes[i].(FlashData)
+ }
+
+ return flashData
}
func (s *Server) updateSessionData(c *gin.Context, data SessionData) error {
@@ -248,13 +256,15 @@ func (s *Server) getStaticData() StaticData {
}
}
-func (s *Server) setAlert(c *gin.Context, message, typ string) SessionData {
- currentSession := s.getSessionData(c)
- currentSession.AlertData = message
- currentSession.AlertType = typ
- _ = s.updateSessionData(c, currentSession)
-
- return currentSession
+func (s *Server) setFlashMessage(c *gin.Context, message, typ string) {
+ session := sessions.Default(c)
+ session.AddFlash(FlashData{
+ Message: message,
+ Type: typ,
+ })
+ if err := session.Save(); err != nil {
+ log.Errorf("Failed to store session after setting flash: %v", err)
+ }
}
func (s SessionData) GetSortIcon(field string) string {
diff --git a/internal/server/handlers_common.go b/internal/server/handlers_common.go
index 85b72af..90c60f5 100644
--- a/internal/server/handlers_common.go
+++ b/internal/server/handlers_common.go
@@ -23,13 +23,13 @@ func (s *Server) GetHandleError(c *gin.Context, code int, message, details strin
func (s *Server) GetIndex(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", struct {
Route string
- Alerts AlertData
+ Alerts []FlashData
Session SessionData
Static StaticData
Device Device
}{
Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
+ Alerts: s.getFlashes(c),
Session: s.getSessionData(c),
Static: s.getStaticData(),
Device: s.users.GetDevice(),
@@ -77,7 +77,7 @@ func (s *Server) GetAdminIndex(c *gin.Context) {
c.HTML(http.StatusOK, "admin_index.html", struct {
Route string
- Alerts AlertData
+ Alerts []FlashData
Session SessionData
Static StaticData
Peers []User
@@ -86,7 +86,7 @@ func (s *Server) GetAdminIndex(c *gin.Context) {
LdapDisabled bool
}{
Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
+ Alerts: s.getFlashes(c),
Session: currentSession,
Static: s.getStaticData(),
Peers: users,
@@ -125,7 +125,7 @@ func (s *Server) GetUserIndex(c *gin.Context) {
c.HTML(http.StatusOK, "user_index.html", struct {
Route string
- Alerts AlertData
+ Alerts []FlashData
Session SessionData
Static StaticData
Peers []User
@@ -133,7 +133,7 @@ func (s *Server) GetUserIndex(c *gin.Context) {
Device Device
}{
Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
+ Alerts: s.getFlashes(c),
Session: currentSession,
Static: s.getStaticData(),
Peers: users,
diff --git a/internal/server/handlers_interface.go b/internal/server/handlers_interface.go
index 860e87a..813de4e 100644
--- a/internal/server/handlers_interface.go
+++ b/internal/server/handlers_interface.go
@@ -20,14 +20,14 @@ func (s *Server) GetAdminEditInterface(c *gin.Context) {
c.HTML(http.StatusOK, "admin_edit_interface.html", struct {
Route string
- Alerts AlertData
+ Alerts []FlashData
Session SessionData
Static StaticData
Peers []User
Device Device
}{
Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
+ Alerts: s.getFlashes(c),
Session: currentSession,
Static: s.getStaticData(),
Peers: users,
@@ -43,7 +43,7 @@ func (s *Server) PostAdminEditInterface(c *gin.Context) {
}
if err := c.ShouldBind(&formDevice); err != nil {
_ = s.updateFormInSession(c, formDevice)
- s.setAlert(c, "failed to bind form data: "+err.Error(), "danger")
+ s.setFlashMessage(c, err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/device/edit?formerr=bind")
return
}
@@ -59,7 +59,7 @@ func (s *Server) PostAdminEditInterface(c *gin.Context) {
err := s.wg.UpdateDevice(formDevice.DeviceName, formDevice.GetDeviceConfig())
if err != nil {
_ = s.updateFormInSession(c, formDevice)
- s.setAlert(c, "failed to update device in WireGuard: "+err.Error(), "danger")
+ s.setFlashMessage(c, "Failed to update device in WireGuard: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/device/edit?formerr=wg")
return
}
@@ -68,12 +68,13 @@ func (s *Server) PostAdminEditInterface(c *gin.Context) {
err = s.users.UpdateDevice(formDevice)
if err != nil {
_ = s.updateFormInSession(c, formDevice)
- s.setAlert(c, "failed to update device in database: "+err.Error(), "danger")
+ s.setFlashMessage(c, "Failed to update device in database: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/device/edit?formerr=update")
return
}
- s.setAlert(c, "changes applied successfully", "success")
+ s.setFlashMessage(c, "Changes applied successfully!", "success")
+ s.setFlashMessage(c, "WireGuard must be restarted to apply ip changes.", "warning")
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
}
@@ -92,3 +93,21 @@ func (s *Server) GetInterfaceConfig(c *gin.Context) {
c.Data(http.StatusOK, "application/config", cfg)
return
}
+
+func (s *Server) GetApplyGlobalConfig(c *gin.Context) {
+ device := s.users.GetDevice()
+ users := s.users.GetAllUsers()
+
+ for _, user := range users {
+ user.AllowedIPs = device.AllowedIPs
+ user.AllowedIPsStr = device.AllowedIPsStr
+ if err := s.users.UpdateUser(user); err != nil {
+ s.setFlashMessage(c, err.Error(), "danger")
+ c.Redirect(http.StatusSeeOther, "/admin/device/edit")
+ }
+ }
+
+ s.setFlashMessage(c, "Allowed ip's updated for all clients.", "success")
+ c.Redirect(http.StatusSeeOther, "/admin/device/edit")
+ return
+}
diff --git a/internal/server/handlers_peer.go b/internal/server/handlers_peer.go
index dc6e55b..17c6249 100644
--- a/internal/server/handlers_peer.go
+++ b/internal/server/handlers_peer.go
@@ -2,6 +2,7 @@ package server
import (
"bytes"
+ "net"
"net/http"
"net/url"
"strings"
@@ -11,6 +12,7 @@ import (
"github.com/h44z/wg-portal/internal/common"
"github.com/h44z/wg-portal/internal/ldap"
log "github.com/sirupsen/logrus"
+ "github.com/tatsushid/go-fastping"
)
type LdapCreateForm struct {
@@ -29,19 +31,21 @@ func (s *Server) GetAdminEditPeer(c *gin.Context) {
}
c.HTML(http.StatusOK, "admin_edit_client.html", struct {
- Route string
- Alerts AlertData
- Session SessionData
- Static StaticData
- Peer User
- Device Device
+ Route string
+ Alerts []FlashData
+ Session SessionData
+ Static StaticData
+ Peer User
+ Device Device
+ EditableKeys bool
}{
- Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
- Session: currentSession,
- Static: s.getStaticData(),
- Peer: currentSession.FormData.(User),
- Device: device,
+ Route: c.Request.URL.Path,
+ Alerts: s.getFlashes(c),
+ Session: currentSession,
+ Static: s.getStaticData(),
+ Peer: currentSession.FormData.(User),
+ Device: device,
+ EditableKeys: s.config.Core.EditableKeys,
})
}
@@ -56,7 +60,7 @@ func (s *Server) PostAdminEditPeer(c *gin.Context) {
}
if err := c.ShouldBind(&formUser); err != nil {
_ = s.updateFormInSession(c, formUser)
- s.setAlert(c, "failed to bind form data: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to bind form data: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey+"&formerr=bind")
return
}
@@ -78,12 +82,12 @@ func (s *Server) PostAdminEditPeer(c *gin.Context) {
// Update in database
if err := s.UpdateUser(formUser, now); err != nil {
_ = s.updateFormInSession(c, formUser)
- s.setAlert(c, "failed to update user: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to update user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey+"&formerr=update")
return
}
- s.setAlert(c, "changes applied successfully", "success")
+ s.setFlashMessage(c, "changes applied successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
}
@@ -96,19 +100,21 @@ func (s *Server) GetAdminCreatePeer(c *gin.Context) {
return
}
c.HTML(http.StatusOK, "admin_edit_client.html", struct {
- Route string
- Alerts AlertData
- Session SessionData
- Static StaticData
- Peer User
- Device Device
+ Route string
+ Alerts []FlashData
+ Session SessionData
+ Static StaticData
+ Peer User
+ Device Device
+ EditableKeys bool
}{
- Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
- Session: currentSession,
- Static: s.getStaticData(),
- Peer: currentSession.FormData.(User),
- Device: device,
+ Route: c.Request.URL.Path,
+ Alerts: s.getFlashes(c),
+ Session: currentSession,
+ Static: s.getStaticData(),
+ Peer: currentSession.FormData.(User),
+ Device: device,
+ EditableKeys: s.config.Core.EditableKeys,
})
}
@@ -120,7 +126,7 @@ func (s *Server) PostAdminCreatePeer(c *gin.Context) {
}
if err := c.ShouldBind(&formUser); err != nil {
_ = s.updateFormInSession(c, formUser)
- s.setAlert(c, "failed to bind form data: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to bind form data: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/create?formerr=bind")
return
}
@@ -139,12 +145,12 @@ func (s *Server) PostAdminCreatePeer(c *gin.Context) {
if err := s.CreateUser(formUser); err != nil {
_ = s.updateFormInSession(c, formUser)
- s.setAlert(c, "failed to add user: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to add user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/create?formerr=create")
return
}
- s.setAlert(c, "client created successfully", "success")
+ s.setFlashMessage(c, "client created successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin")
}
@@ -157,7 +163,7 @@ func (s *Server) GetAdminCreateLdapPeers(c *gin.Context) {
c.HTML(http.StatusOK, "admin_create_clients.html", struct {
Route string
- Alerts AlertData
+ Alerts []FlashData
Session SessionData
Static StaticData
Users []*ldap.UserCacheHolderEntry
@@ -165,7 +171,7 @@ func (s *Server) GetAdminCreateLdapPeers(c *gin.Context) {
Device Device
}{
Route: c.Request.URL.Path,
- Alerts: s.getAlertData(c),
+ Alerts: s.getFlashes(c),
Session: currentSession,
Static: s.getStaticData(),
Users: s.ldapUsers.GetSortedUsers("sn", "asc"),
@@ -182,7 +188,7 @@ func (s *Server) PostAdminCreateLdapPeers(c *gin.Context) {
}
if err := c.ShouldBind(&formData); err != nil {
_ = s.updateFormInSession(c, formData)
- s.setAlert(c, "failed to bind form data: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to bind form data: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/createldap?formerr=bind")
return
}
@@ -192,7 +198,7 @@ func (s *Server) PostAdminCreateLdapPeers(c *gin.Context) {
// TODO: also check email addr for validity?
if !strings.ContainsRune(emails[i], '@') || s.ldapUsers.GetUserDNByMail(emails[i]) == "" {
_ = s.updateFormInSession(c, formData)
- s.setAlert(c, "invalid email address: "+emails[i], "danger")
+ s.setFlashMessage(c, "invalid email address: "+emails[i], "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/createldap?formerr=mail")
return
}
@@ -203,13 +209,13 @@ func (s *Server) PostAdminCreateLdapPeers(c *gin.Context) {
for i := range emails {
if err := s.CreateUserByEmail(emails[i], formData.Identifier, false); err != nil {
_ = s.updateFormInSession(c, formData)
- s.setAlert(c, "failed to add user: "+err.Error(), "danger")
+ s.setFlashMessage(c, "failed to add user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/peer/createldap?formerr=create")
return
}
}
- s.setAlert(c, "client(s) created successfully", "success")
+ s.setFlashMessage(c, "client(s) created successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin/peer/createldap")
}
@@ -219,7 +225,7 @@ func (s *Server) GetAdminDeletePeer(c *gin.Context) {
s.GetHandleError(c, http.StatusInternalServerError, "Deletion error", err.Error())
return
}
- s.setAlert(c, "user deleted successfully", "success")
+ s.setFlashMessage(c, "user deleted successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin")
}
@@ -313,6 +319,55 @@ func (s *Server) GetPeerConfigMail(c *gin.Context) {
return
}
- s.setAlert(c, "mail sent successfully", "success")
+ s.setFlashMessage(c, "mail sent successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin")
}
+
+func (s *Server) GetPeerStatus(c *gin.Context) {
+ user := s.users.GetUserByKey(c.Query("pkey"))
+ currentSession := s.getSessionData(c)
+ if !currentSession.IsAdmin && user.Email != currentSession.Email {
+ s.GetHandleError(c, http.StatusUnauthorized, "No permissions", "You don't have permissions to view this resource!")
+ return
+ }
+
+ if user.Peer == nil { // no peer means disabled
+ c.JSON(http.StatusOK, false)
+ return
+ }
+
+ isOnline := false
+ ping := make(chan bool)
+ defer close(ping)
+ for _, cidr := range user.IPs {
+ ip, _, _ := net.ParseCIDR(cidr)
+ var ra *net.IPAddr
+ if common.IsIPv6(ip.String()) {
+ ra, _ = net.ResolveIPAddr("ip6:ipv6-icmp", ip.String())
+ } else {
+
+ ra, _ = net.ResolveIPAddr("ip4:icmp", ip.String())
+ }
+
+ p := fastping.NewPinger()
+ p.AddIPAddr(ra)
+ p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
+ ping <- true
+ p.Stop()
+ }
+ p.OnIdle = func() {
+ ping <- false
+ p.Stop()
+ }
+ p.MaxRTT = 500 * time.Millisecond
+ p.RunLoop()
+
+ if <-ping {
+ isOnline = true
+ break
+ }
+ }
+
+ c.JSON(http.StatusOK, isOnline)
+ return
+}
diff --git a/internal/server/routes.go b/internal/server/routes.go
index 6da31d0..8b90f60 100644
--- a/internal/server/routes.go
+++ b/internal/server/routes.go
@@ -23,6 +23,7 @@ func SetupRoutes(s *Server) {
admin.GET("/device/edit", s.GetAdminEditInterface)
admin.POST("/device/edit", s.PostAdminEditInterface)
admin.GET("/device/download", s.GetInterfaceConfig)
+ admin.GET("/device/applyglobals", s.GetApplyGlobalConfig)
admin.GET("/peer/edit", s.GetAdminEditPeer)
admin.POST("/peer/edit", s.PostAdminEditPeer)
admin.GET("/peer/create", s.GetAdminCreatePeer)
@@ -40,6 +41,7 @@ func SetupRoutes(s *Server) {
user.GET("/profile", s.GetUserIndex)
user.GET("/download", s.GetPeerConfig)
user.GET("/email", s.GetPeerConfigMail)
+ user.GET("/status", s.GetPeerStatus)
}
func (s *Server) RequireAuthentication(scope string) gin.HandlerFunc {
diff --git a/internal/wireguard/template.go b/internal/wireguard/template.go
index f076678..b1e5f59 100644
--- a/internal/wireguard/template.go
+++ b/internal/wireguard/template.go
@@ -1,47 +1,52 @@
package wireguard
var (
- ClientCfgTpl = `[Interface]
-#{{ .Client.Identifier }}
+ ClientCfgTpl = `#{{ .Client.Identifier }}
+[Interface]
Address = {{ .Client.IPsStr }}
PrivateKey = {{ .Client.PrivateKey }}
-{{ if ne (len .Server.DNS) 0 -}}
+{{if .Server.DNSStr -}}
DNS = {{ .Server.DNSStr }}
-{{- end }}
-{{ if ne .Server.Mtu 0 -}}
-MTU = {{.Server.Mtu}}
{{- end}}
+{{- if ne .Server.Mtu 0 -}}
+MTU = {{.Server.Mtu}}
+{{- end -}}
[Peer]
PublicKey = {{ .Server.PublicKey }}
+{{- if .Client.PresharedKey -}}
PresharedKey = {{ .Client.PresharedKey }}
+{{- end -}}
AllowedIPs = {{ .Client.AllowedIPsStr }}
Endpoint = {{ .Server.Endpoint }}
-{{ if and (ne .Server.PersistentKeepalive 0) (not .Client.IgnorePersistentKeepalive) -}}
+{{if and (ne .Server.PersistentKeepalive 0) (not .Client.IgnorePersistentKeepalive) -}}
PersistentKeepalive = {{.Server.PersistentKeepalive}}
{{- end}}
`
- DeviceCfgTpl = `# Updated: {{ .Server.UpdatedAt }} / Created: {{ .Server.CreatedAt }}
+ DeviceCfgTpl = `# AUTOGENERATED FILE - DO NOT EDIT
+# Updated: {{ .Server.UpdatedAt }} / Created: {{ .Server.CreatedAt }}
[Interface]
{{- range .Server.IPs }}
Address = {{ . }}
-{{- end }}
+{{- end}}
ListenPort = {{ .Server.ListenPort }}
PrivateKey = {{ .Server.PrivateKey }}
-{{ if ne .Server.Mtu 0 -}}
+{{- if ne .Server.Mtu 0 -}}
MTU = {{.Server.Mtu}}
-{{- end}}
+{{- end -}}
PreUp = {{ .Server.PreUp }}
PostUp = {{ .Server.PostUp }}
PreDown = {{ .Server.PreDown }}
PostDown = {{ .Server.PostDown }}
-{{ range .Clients }}
-{{ if not .DeactivatedAt -}}
+{{range .Clients}}
+{{if not .DeactivatedAt -}}
# {{.Identifier}} / {{.Email}} / Updated: {{.UpdatedAt}} / Created: {{.CreatedAt}}
[Peer]
PublicKey = {{ .PublicKey }}
+{{- if .PresharedKey -}}
PresharedKey = {{ .PresharedKey }}
+{{- end -}}
AllowedIPs = {{ StringsJoin .IPs ", " }}
-{{- end }}
-{{ end }}`
+{{- end}}
+{{end}}`
)