Compare commits

...

7 Commits

Author SHA1 Message Date
Christoph Haas
83271b5d34 fix user edit bug, allow to delete users from the database (#40) 2022-03-15 23:34:55 +01:00
Alexis
cc50fcf8e6 Feat/ldap certificate connexion (#92)
* Give the way to connect against LDAP server with certificate and key

* fix(ldap) Update cert variable name

In order to be more explicit

Co-authored-by: Alexis Aurin <alexis@so6.pw>
2022-03-15 22:46:00 +01:00
Christoph Haas
5d4d06db81 fix invalid interface public key (#74) 2021-12-16 19:51:45 +01:00
ultram4rine
e581b3a69f Wireguard exporter friendly tags (#81)
* add friendly name

* add friendly name as option to configuration

* add friendly name configuration to readme
2021-12-16 19:35:15 +01:00
Alexander Beck
acb629f672 do not overwrite preshared key in CreatePeer (#77) 2021-12-10 16:52:44 +01:00
Christoph Haas
b5cb967e09 improve ldap logging (#67) 2021-11-07 13:20:16 +01:00
commonism
5a9918e00d docker-compose - use logging limits (#66)
- ldap sync is very noisy, limits/rotation required
 - can be verified with
   docker inspect -f '{{.HostConfig.LogConfig}}' 88…de
   {json-file map[max-file:3 max-size:10m]}

Co-authored-by: Markus Koetter <koetter@cispa.de>
2021-11-04 22:52:14 +01:00
17 changed files with 226 additions and 105 deletions

View File

@@ -109,7 +109,7 @@ For example: `CONFIG_FILE=/home/test/config.yml ./wg-portal-amd64`.
The following configuration options are available: The following configuration options are available:
| environment | yaml | yaml_parent | default_value | description | | environment | yaml | yaml_parent | default_value | description |
|-----------------------|-------------------|-------------|-------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| |----------------------------|-------------------------|-------------|-------------------------------------------------|-------------------------------------------------------------------------------------------|
| LISTENING_ADDRESS | listeningAddress | core | :8123 | The address on which the web server is listening. Optional IP address and port, e.g.: 127.0.0.1:8080. | | LISTENING_ADDRESS | listeningAddress | core | :8123 | The address on which the web server is listening. Optional IP address and port, e.g.: 127.0.0.1:8080. |
| EXTERNAL_URL | externalUrl | core | http://localhost:8123 | The external URL where the web server is reachable. This link is used in emails that are created by the WireGuard Portal. | | EXTERNAL_URL | externalUrl | core | http://localhost:8123 | The external URL where the web server is reachable. This link is used in emails that are created by the WireGuard Portal. |
| WEBSITE_TITLE | title | core | WireGuard VPN | The website title. | | WEBSITE_TITLE | title | core | WireGuard VPN | The website title. |
@@ -121,6 +121,7 @@ The following configuration options are available:
| EDITABLE_KEYS | editableKeys | core | true | Allow to edit key-pairs in the UI. | | EDITABLE_KEYS | editableKeys | core | true | Allow to edit key-pairs in the UI. |
| CREATE_DEFAULT_PEER | createDefaultPeer | core | false | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled. | | CREATE_DEFAULT_PEER | createDefaultPeer | core | false | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled. |
| SELF_PROVISIONING | selfProvisioning | core | false | Allow registered users to automatically create peers via the RESTful API. | | SELF_PROVISIONING | selfProvisioning | core | false | Allow registered users to automatically create peers via the RESTful API. |
| WG_EXPORTER_FRIENDLY_NAMES | wgExporterFriendlyNames | core | false | Enable integration with [prometheus_wireguard_exporter friendly name](https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags). |
| LDAP_ENABLED | ldapEnabled | core | false | Enable or disable the LDAP backend. | | LDAP_ENABLED | ldapEnabled | core | false | Enable or disable the LDAP backend. |
| SESSION_SECRET | sessionSecret | core | secret | Use a custom secret to encrypt session data. | | SESSION_SECRET | sessionSecret | core | secret | Use a custom secret to encrypt session data. |
| DATABASE_TYPE | typ | database | sqlite | Either mysql or sqlite. | | DATABASE_TYPE | typ | database | sqlite | Either mysql or sqlite. |
@@ -155,6 +156,9 @@ The following configuration options are available:
| LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. | | LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. |
| LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. | | LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. |
| LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. | | LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. |
| LDAP_CERT_CONN | ldapCertConn | ldap | false | Allow connection with certificate against LDAP server without user/password |
| LDAPTLS_CERT | ldapTlsCert | ldap | | The LDAP cert's path |
| LDAPTLS_KEY | ldapTlsKey | ldap | | The LDAP key's path |
| LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. | | LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. |
| LOG_JSON | | | false | Format log output as JSON. | | LOG_JSON | | | false | Format log output as JSON. |
| LOG_COLOR | | | true | Colorize log output. | | LOG_COLOR | | | true | Colorize log output. |

View File

@@ -76,6 +76,11 @@
<button type="submit" class="btn btn-primary">Save</button> <button type="submit" class="btn btn-primary">Save</button>
<a href="/admin/users/" class="btn btn-secondary">Cancel</a> <a href="/admin/users/" class="btn btn-secondary">Cancel</a>
{{if eq $.Session.IsAdmin true}}
{{if eq .User.Source "db"}}
<a href="/admin/users/delete?pkey={{.User.Email}}" data-toggle="confirmation" data-title="Really delete user and associated peers?" title="Delete user and associated peers" class="btn btn-danger float-right">Delete</a>
{{end}}
{{end}}
</form> </form>
</div> </div>
{{template "prt_footer.html" .}} {{template "prt_footer.html" .}}

View File

@@ -4,6 +4,10 @@ services:
image: h44z/wg-portal:1.0.6 image: h44z/wg-portal:1.0.6
container_name: wg-portal container_name: wg-portal
restart: unless-stopped restart: unless-stopped
logging:
options:
max-size: "10m"
max-file: "3"
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
network_mode: "host" network_mode: "host"

View File

@@ -2,6 +2,7 @@ package ldap
import ( import (
"crypto/tls" "crypto/tls"
"io/ioutil"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -154,23 +155,49 @@ func (provider Provider) GetUserModel(ctx *authentication.AuthContext) (*authent
} }
func (provider Provider) open() (*ldap.Conn, error) { func (provider Provider) open() (*ldap.Conn, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: !provider.config.CertValidation} var tlsConfig *tls.Config
if provider.config.LdapCertConn {
cert_plain, err := ioutil.ReadFile(provider.config.LdapTlsCert)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the certificate")
}
key, err := ioutil.ReadFile(provider.config.LdapTlsKey)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the key")
}
cert_x509, err := tls.X509KeyPair(cert_plain, key)
if err != nil {
return nil, errors.WithMessage(err, "failed X509")
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert_x509}}
} else {
tlsConfig = &tls.Config{InsecureSkipVerify: !provider.config.CertValidation}
}
conn, err := ldap.DialURL(provider.config.URL, ldap.DialWithTLSConfig(tlsConfig)) conn, err := ldap.DialURL(provider.config.URL, ldap.DialWithTLSConfig(tlsConfig))
if err != nil { if err != nil {
return nil, err return nil, errors.WithMessage(err, "failed to connect to LDAP")
} }
if provider.config.StartTLS { if provider.config.StartTLS {
// Reconnect with TLS // Reconnect with TLS
err = conn.StartTLS(tlsConfig) err = conn.StartTLS(tlsConfig)
if err != nil { if err != nil {
return nil, err return nil, errors.WithMessage(err, "failed to start TLS session")
} }
} }
err = conn.Bind(provider.config.BindUser, provider.config.BindPass) err = conn.Bind(provider.config.BindUser, provider.config.BindPass)
if err != nil { if err != nil {
return nil, err return nil, errors.WithMessage(err, "failed to bind user")
} }
return conn, nil return conn, nil

View File

@@ -4,7 +4,6 @@ import (
gldap "github.com/go-ldap/ldap/v3" gldap "github.com/go-ldap/ldap/v3"
) )
type Type string type Type string
const ( const (
@@ -30,4 +29,7 @@ type Config struct {
SyncFilter string `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"` SyncFilter string `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"`
AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
AdminLdapGroup_ *gldap.DN `yaml:"-"` AdminLdapGroup_ *gldap.DN `yaml:"-"`
LdapCertConn bool `yaml:"ldapCertConn" envconfig:"LDAP_CERT_CONN"`
LdapTlsCert string `yaml:"ldapTlsCert" envconfig:"LDAPTLS_CERT"`
LdapTlsKey string `yaml:"ldapTlsKey" envconfig:"LDAPTLS_KEY"`
} }

View File

@@ -2,6 +2,7 @@ package ldap
import ( import (
"crypto/tls" "crypto/tls"
"io/ioutil"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -14,7 +15,33 @@ type RawLdapData struct {
} }
func Open(cfg *Config) (*ldap.Conn, error) { func Open(cfg *Config) (*ldap.Conn, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: !cfg.CertValidation} var tlsConfig *tls.Config
if cfg.LdapCertConn {
cert_plain, err := ioutil.ReadFile(cfg.LdapTlsCert)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the certificate")
}
key, err := ioutil.ReadFile(cfg.LdapTlsKey)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the key")
}
cert_x509, err := tls.X509KeyPair(cert_plain, key)
if err != nil {
return nil, errors.WithMessage(err, "failed X509")
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert_x509}}
} else {
tlsConfig = &tls.Config{InsecureSkipVerify: !cfg.CertValidation}
}
conn, err := ldap.DialURL(cfg.URL, ldap.DialWithTLSConfig(tlsConfig)) conn, err := ldap.DialURL(cfg.URL, ldap.DialWithTLSConfig(tlsConfig))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to connect to LDAP") return nil, errors.Wrap(err, "failed to connect to LDAP")

View File

@@ -67,6 +67,7 @@ type Config struct {
EditableKeys bool `yaml:"editableKeys" envconfig:"EDITABLE_KEYS"` EditableKeys bool `yaml:"editableKeys" envconfig:"EDITABLE_KEYS"`
CreateDefaultPeer bool `yaml:"createDefaultPeer" envconfig:"CREATE_DEFAULT_PEER"` CreateDefaultPeer bool `yaml:"createDefaultPeer" envconfig:"CREATE_DEFAULT_PEER"`
SelfProvisioningAllowed bool `yaml:"selfProvisioning" envconfig:"SELF_PROVISIONING"` SelfProvisioningAllowed bool `yaml:"selfProvisioning" envconfig:"SELF_PROVISIONING"`
WGExoprterFriendlyNames bool `yaml:"wgExporterFriendlyNames" envconfig:"WG_EXPORTER_FRIENDLY_NAMES"`
LdapEnabled bool `yaml:"ldapEnabled" envconfig:"LDAP_ENABLED"` LdapEnabled bool `yaml:"ldapEnabled" envconfig:"LDAP_ENABLED"`
SessionSecret string `yaml:"sessionSecret" envconfig:"SESSION_SECRET"` SessionSecret string `yaml:"sessionSecret" envconfig:"SESSION_SECRET"`
LogoUrl string `yaml:"logoUrl" envconfig:"LOGO_URL"` LogoUrl string `yaml:"logoUrl" envconfig:"LOGO_URL"`
@@ -91,6 +92,7 @@ func NewConfig() *Config {
cfg.Core.AdminPassword = "wgportal" cfg.Core.AdminPassword = "wgportal"
cfg.Core.LdapEnabled = false cfg.Core.LdapEnabled = false
cfg.Core.EditableKeys = true cfg.Core.EditableKeys = true
cfg.Core.WGExoprterFriendlyNames = false
cfg.Core.SessionSecret = "secret" cfg.Core.SessionSecret = "secret"
cfg.Database.Typ = "sqlite" cfg.Database.Typ = "sqlite"

View File

@@ -112,7 +112,7 @@ func (s *Server) GetInterfaceConfig(c *gin.Context) {
currentSession := GetSessionData(c) currentSession := GetSessionData(c)
device := s.peers.GetDevice(currentSession.DeviceName) device := s.peers.GetDevice(currentSession.DeviceName)
peers := s.peers.GetActivePeers(device.DeviceName) peers := s.peers.GetActivePeers(device.DeviceName)
cfg, err := device.GetConfigFile(peers) cfg, err := device.GetConfigFile(peers, s.config.Core.WGExoprterFriendlyNames)
if err != nil { if err != nil {
s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error()) s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error())
return return

View File

@@ -236,7 +236,7 @@ func (s *Server) GetPeerConfig(c *gin.Context) {
return return
} }
cfg, err := peer.GetConfigFile(s.peers.GetDevice(currentSession.DeviceName)) cfg, err := peer.GetConfigFile(s.peers.GetDevice(peer.DeviceName))
if err != nil { if err != nil {
s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error()) s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error())
return return

View File

@@ -83,6 +83,26 @@ func (s *Server) GetAdminUsersEdit(c *gin.Context) {
}) })
} }
func (s *Server) GetAdminUsersDelete(c *gin.Context) {
user := s.users.GetUserUnscoped(c.Query("pkey"))
if user == nil {
SetFlashMessage(c, "invalid user", "danger")
c.Redirect(http.StatusSeeOther, "/admin/users/")
return
}
urlEncodedKey := url.QueryEscape(c.Query("pkey"))
if err := s.HardDeleteUser(*user); err != nil {
SetFlashMessage(c, "failed to delete user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/users/edit?pkey="+urlEncodedKey+"&formerr=delete")
return
}
SetFlashMessage(c, "user deleted successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin/users/")
}
func (s *Server) PostAdminUsersEdit(c *gin.Context) { func (s *Server) PostAdminUsersEdit(c *gin.Context) {
currentUser := s.users.GetUserUnscoped(c.Query("pkey")) currentUser := s.users.GetUserUnscoped(c.Query("pkey"))
if currentUser == nil { if currentUser == nil {
@@ -113,7 +133,7 @@ func (s *Server) PostAdminUsersEdit(c *gin.Context) {
} else { } else {
formUser.DeletedAt = gorm.DeletedAt{} formUser.DeletedAt = gorm.DeletedAt{}
} }
formUser.IsAdmin = c.PostForm("isadmin") == "true" formUser.IsAdmin = c.PostForm("isadmin") != ""
if err := s.UpdateUser(formUser); err != nil { if err := s.UpdateUser(formUser); err != nil {
_ = s.updateFormInSession(c, formUser) _ = s.updateFormInSession(c, formUser)

View File

@@ -114,7 +114,7 @@ func (s *Server) disableMissingLdapUsers(ldapUsers []ldap.RawLdapData) {
} }
} }
if err := s.users.DeleteUser(&activeUsers[i]); err != nil { if err := s.users.DeleteUser(&activeUsers[i], true); err != nil {
logrus.Errorf("failed to delete deactivated user %s in database: %v", activeUsers[i].Email, err) logrus.Errorf("failed to delete deactivated user %s in database: %v", activeUsers[i].Email, err)
} }
} }

View File

@@ -64,6 +64,7 @@ func SetupRoutes(s *Server) {
admin.GET("/users/create", s.GetAdminUsersCreate) admin.GET("/users/create", s.GetAdminUsersCreate)
admin.POST("/users/create", s.PostAdminUsersCreate) admin.POST("/users/create", s.PostAdminUsersCreate)
admin.GET("/users/edit", s.GetAdminUsersEdit) admin.GET("/users/edit", s.GetAdminUsersEdit)
admin.GET("/users/delete", s.GetAdminUsersDelete)
admin.POST("/users/edit", s.PostAdminUsersEdit) admin.POST("/users/edit", s.PostAdminUsersEdit)
// User routes // User routes

View File

@@ -103,16 +103,21 @@ func (s *Server) CreatePeer(device string, peer wireguard.Peer) error {
} }
peer.SetIPAddresses(peerIPs...) peer.SetIPAddresses(peerIPs...)
} }
if peer.PrivateKey == "" && dev.Type == wireguard.DeviceTypeServer { // if private key is empty create a new one if peer.PresharedKey == "" && dev.Type == wireguard.DeviceTypeServer { // if preshared key is empty create a new one
psk, err := wgtypes.GenerateKey() psk, err := wgtypes.GenerateKey()
if err != nil { if err != nil {
return errors.Wrap(err, "failed to generate key") return errors.Wrap(err, "failed to generate key")
} }
peer.PresharedKey = psk.String()
}
if peer.PrivateKey == "" && peer.PublicKey == "" && dev.Type == wireguard.DeviceTypeServer { // if private key is empty create a new one
key, err := wgtypes.GeneratePrivateKey() key, err := wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
return errors.Wrap(err, "failed to generate private key") return errors.Wrap(err, "failed to generate private key")
} }
peer.PresharedKey = psk.String()
peer.PrivateKey = key.String() peer.PrivateKey = key.String()
peer.PublicKey = key.PublicKey().String() peer.PublicKey = key.PublicKey().String()
} }
@@ -204,7 +209,7 @@ func (s *Server) WriteWireGuardConfigFile(device string) error {
} }
dev := s.peers.GetDevice(device) dev := s.peers.GetDevice(device)
cfg, err := dev.GetConfigFile(s.peers.GetActivePeers(device)) cfg, err := dev.GetConfigFile(s.peers.GetActivePeers(device), s.config.Core.WGExoprterFriendlyNames)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to get config file") return errors.WithMessage(err, "failed to get config file")
} }
@@ -248,10 +253,6 @@ func (s *Server) CreateUser(user users.User, device string) error {
// UpdateUser updates the user in the database. If the user is marked as deleted, it will get remove from the database. // UpdateUser updates the user in the database. If the user is marked as deleted, it will get remove from the database.
// Also, if the user is re-enabled, all it's linked WireGuard peers will be activated again. // Also, if the user is re-enabled, all it's linked WireGuard peers will be activated again.
func (s *Server) UpdateUser(user users.User) error { func (s *Server) UpdateUser(user users.User) error {
if user.DeletedAt.Valid {
return s.DeleteUser(user)
}
currentUser := s.users.GetUserUnscoped(user.Email) currentUser := s.users.GetUserUnscoped(user.Email)
// Hash user password (if set) // Hash user password (if set)
@@ -270,7 +271,12 @@ func (s *Server) UpdateUser(user users.User) error {
return errors.WithMessage(err, "failed to update user in manager") return errors.WithMessage(err, "failed to update user in manager")
} }
// If user was deleted (disabled), reactivate it's peers // Set to deleted (disabled) if user's deletedAt date is not empty
if user.DeletedAt.Valid {
return s.DeleteUser(user)
}
// Otherwise, if user was deleted (disabled), reactivate it's peers
if currentUser.DeletedAt.Valid { if currentUser.DeletedAt.Valid {
for _, peer := range s.peers.GetPeersByMail(user.Email) { for _, peer := range s.peers.GetPeersByMail(user.Email) {
now := time.Now() now := time.Now()
@@ -284,18 +290,15 @@ func (s *Server) UpdateUser(user users.User) error {
return nil return nil
} }
// DeleteUser removes the user from the database. // DeleteUser soft-deletes the user from the database (disable the user).
// Also, if the user has linked WireGuard peers, they will be deactivated. // Also, if the user has linked WireGuard peers, they will be deactivated.
func (s *Server) DeleteUser(user users.User) error { func (s *Server) DeleteUser(user users.User) error {
currentUser := s.users.GetUserUnscoped(user.Email)
// Update in database // Update in database
if err := s.users.DeleteUser(&user); err != nil { if err := s.users.DeleteUser(&user, true); err != nil {
return errors.WithMessage(err, "failed to delete user in manager") return errors.WithMessage(err, "failed to disable user in manager")
} }
// If user was active, disable it's peers // Disable users peers
if !currentUser.DeletedAt.Valid {
for _, peer := range s.peers.GetPeersByMail(user.Email) { for _, peer := range s.peers.GetPeersByMail(user.Email) {
now := time.Now() now := time.Now()
peer.DeactivatedAt = &now peer.DeactivatedAt = &now
@@ -303,6 +306,23 @@ func (s *Server) DeleteUser(user users.User) error {
logrus.Errorf("failed to update deactivated peer %s for %s: %v", peer.PublicKey, user.Email, err) logrus.Errorf("failed to update deactivated peer %s for %s: %v", peer.PublicKey, user.Email, err)
} }
} }
return nil
}
// HardDeleteUser removes the user from the database.
// Also, if the user has linked WireGuard peers, they will be deleted.
func (s *Server) HardDeleteUser(user users.User) error {
// Update in database
if err := s.users.DeleteUser(&user, false); err != nil {
return errors.WithMessage(err, "failed to delete user in manager")
}
// remove all linked peers
for _, peer := range s.peers.GetPeersByMail(user.Email) {
if err := s.DeletePeer(peer); err != nil {
logrus.Errorf("failed to delete peer %s for %s: %v", peer.PublicKey, user.Email, err)
}
} }
return nil return nil

View File

@@ -161,9 +161,14 @@ func (m Manager) UpdateUser(user *User) error {
return nil return nil
} }
func (m Manager) DeleteUser(user *User) error { func (m Manager) DeleteUser(user *User, soft bool) error {
user.Email = strings.ToLower(user.Email) user.Email = strings.ToLower(user.Email)
res := m.db.Delete(user) var res *gorm.DB
if soft {
res = m.db.Delete(user)
} else {
res = m.db.Unscoped().Delete(user)
}
if res.Error != nil { if res.Error != nil {
return errors.Wrapf(res.Error, "failed to update user %s", user.Email) return errors.Wrapf(res.Error, "failed to update user %s", user.Email)
} }

View File

@@ -29,7 +29,7 @@ type User struct {
// required fields // required fields
Email string `gorm:"primaryKey" form:"email" binding:"required,email"` Email string `gorm:"primaryKey" form:"email" binding:"required,email"`
Source UserSource Source UserSource
IsAdmin bool IsAdmin bool `form:"isadmin"`
// optional fields // optional fields
Firstname string `form:"firstname" binding:"required"` Firstname string `form:"firstname" binding:"required"`

View File

@@ -338,12 +338,13 @@ func (d Device) GetConfig() wgtypes.Config {
return cfg return cfg
} }
func (d Device) GetConfigFile(peers []Peer) ([]byte, error) { func (d Device) GetConfigFile(peers []Peer, friendlyNames bool) ([]byte, error) {
var tplBuff bytes.Buffer var tplBuff bytes.Buffer
err := templateCache.ExecuteTemplate(&tplBuff, "interface.tpl", gin.H{ err := templateCache.ExecuteTemplate(&tplBuff, "interface.tpl", gin.H{
"Peers": peers, "Peers": peers,
"Interface": d, "Interface": d,
"FriendlyNames": friendlyNames,
}) })
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to execute server template") return nil, errors.Wrap(err, "failed to execute server template")

View File

@@ -56,6 +56,9 @@ PostDown = {{ .Interface.PostDown }}
# -WGP- PrivateKey: {{.PrivateKey}} # -WGP- PrivateKey: {{.PrivateKey}}
{{- end}} {{- end}}
[Peer] [Peer]
{{- if $.FriendlyNames}}
# friendly_name = {{ .Identifier }}
{{- end}}
PublicKey = {{ .PublicKey }} PublicKey = {{ .PublicKey }}
{{- if .PresharedKey}} {{- if .PresharedKey}}
PresharedKey = {{ .PresharedKey }} PresharedKey = {{ .PresharedKey }}