Merge branch 'master' into preshared-key-optional

This commit is contained in:
Khanh Ngo 2022-01-29 09:10:21 +01:00 committed by GitHub
commit bbc8bd341d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 276 additions and 26 deletions

View file

@ -33,7 +33,9 @@ wireguard interface stats. See the `cap_add` and `network_mode` options on the d
Set the `SESSION_SECRET` environment variable to a random value. Set the `SESSION_SECRET` environment variable to a random value.
In order to sent the wireguard configuration to clients via email (using sendgrid api) set the following environment variables In order to sent the wireguard configuration to clients via email, set the following environment variables:
- using SendGrid API
``` ```
SENDGRID_API_KEY: Your sendgrid api key SENDGRID_API_KEY: Your sendgrid api key
@ -41,6 +43,18 @@ EMAIL_FROM_ADDRESS: the email address you registered on sendgrid
EMAIL_FROM_NAME: the sender's email address EMAIL_FROM_NAME: the sender's email address
``` ```
- using SMTP
```
SMTP_HOSTNAME
SMTP_PORT
SMTP_USERNAME
SMTP_PASSWORD
SMTP_AUTH_TYPE
EMAIL_FROM_ADDRESS: the sender's email address
EMAIL_FROM_NAME: the sender's name
```
### Using binary file ### Using binary file
Download the binary file from the release and run it with command: Download the binary file from the release and run it with command:

View file

@ -24,11 +24,15 @@ function renderClientList(data) {
<div class="overlay" id="paused_${obj.Client.id}"` + clientStatusHtml <div class="overlay" id="paused_${obj.Client.id}"` + clientStatusHtml
+ `<i class="paused-client fas fa-3x fa-play" onclick="resumeClient('${obj.Client.id}')"></i> + `<i class="paused-client fas fa-3x fa-play" onclick="resumeClient('${obj.Client.id}')"></i>
</div> </div>
<img src="${obj.QRCode}" />
<div class="info-box-content"> <div class="info-box-content">
<div class="btn-group"> <div class="btn-group">
<a href="/download?clientid=${obj.Client.id}" class="btn btn-outline-success btn-sm">Download</a> <a href="/download?clientid=${obj.Client.id}" class="btn btn-outline-success btn-sm">Download</a>
</div> </div>
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary btn-sm" data-toggle="modal"
data-target="#modal_qr_client" data-clientid="${obj.Client.id}"
data-clientname="${obj.Client.name}">Scan</button>
</div>
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-outline-secondary btn-sm" data-toggle="modal" <button type="button" class="btn btn-outline-secondary btn-sm" data-toggle="modal"
data-target="#modal_email_client" data-clientid="${obj.Client.id}" data-target="#modal_email_client" data-clientid="${obj.Client.id}"

81
emailer/smtp.go Normal file
View file

@ -0,0 +1,81 @@
package emailer
import (
"crypto/tls"
"fmt"
"time"
mail "github.com/xhit/go-simple-mail/v2"
)
type SmtpMail struct {
hostname string
port int
username string
password string
authType mail.AuthType
noTLSCheck bool
fromName string
from string
}
func authType(authType string) mail.AuthType {
switch authType {
case "PLAIN":
return mail.AuthPlain
case "LOGIN":
return mail.AuthLogin
default:
return mail.AuthNone
}
}
func NewSmtpMail(hostname string, port int, username string, password string, noTLSCheck bool, auth string, fromName, from string) *SmtpMail {
ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: authType(auth)}
return &ans
}
func addressField(address string, name string) string {
if name == "" {
return address
}
return fmt.Sprintf("%s <%s>", name, address)
}
func (o *SmtpMail) Send(toName string, to string, subject string, content string, attachments []Attachment) error {
server := mail.NewSMTPClient()
server.Host = o.hostname
server.Port = o.port
server.Authentication = o.authType
server.Username = o.username
server.Password = o.password
server.Encryption = mail.EncryptionSTARTTLS
server.KeepAlive = false
server.ConnectTimeout = 10 * time.Second
server.SendTimeout = 10 * time.Second
if o.noTLSCheck {
server.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
smtpClient, err := server.Connect()
if err != nil {
return err
}
email := mail.NewMSG()
email.SetFrom("From "+addressField(o.from, o.fromName)).
AddTo(addressField(to, toName)).
SetSubject(subject).
SetBody(mail.TextHTML, content)
for _, v := range attachments {
email.Attach(&mail.File{Name: v.Name, Data: v.Data})
}
err = email.Send(smtpClient)
return err
}

1
go.mod
View file

@ -17,6 +17,7 @@ require (
github.com/sendgrid/rest v2.6.4+incompatible // indirect github.com/sendgrid/rest v2.6.4+incompatible // indirect
github.com/sendgrid/sendgrid-go v3.10.0+incompatible github.com/sendgrid/sendgrid-go v3.10.0+incompatible
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
github.com/xhit/go-simple-mail/v2 v2.10.0
golang.zx2c4.com/wireguard v0.0.20200121 // indirect golang.zx2c4.com/wireguard v0.0.20200121 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect

2
go.sum
View file

@ -147,6 +147,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/xhit/go-simple-mail/v2 v2.10.0 h1:nib6RaJ4qVh5HD9UE9QJqnUZyWp3upv+Z6CFxaMj0V8=
github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

View file

@ -160,6 +160,12 @@ func NewClient(db store.IStore) echo.HandlerFunc {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Allowed IPs must be in CIDR format"}) return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Allowed IPs must be in CIDR format"})
} }
// validate extra AllowedIPs
if util.ValidateExtraAllowedIPs(client.ExtraAllowedIPs) == false {
log.Warnf("Invalid Extra AllowedIPs input from user: %v", client.ExtraAllowedIPs)
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra AllowedIPs must be in CIDR format"})
}
// gen ID // gen ID
guid := xid.New() guid := xid.New()
client.ID = guid.String() client.ID = guid.String()
@ -274,6 +280,11 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Allowed IPs must be in CIDR format"}) return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Allowed IPs must be in CIDR format"})
} }
if util.ValidateExtraAllowedIPs(_client.ExtraAllowedIPs) == false {
log.Warnf("Invalid Allowed IPs input from user: %v", _client.ExtraAllowedIPs)
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"})
}
// map new data // map new data
client.Name = _client.Name client.Name = _client.Name
client.Email = _client.Email client.Email = _client.Email
@ -281,6 +292,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
client.UseServerDNS = _client.UseServerDNS client.UseServerDNS = _client.UseServerDNS
client.AllocatedIPs = _client.AllocatedIPs client.AllocatedIPs = _client.AllocatedIPs
client.AllowedIPs = _client.AllowedIPs client.AllowedIPs = _client.AllowedIPs
client.ExtraAllowedIPs = _client.ExtraAllowedIPs
client.UpdatedAt = time.Now().UTC() client.UpdatedAt = time.Now().UTC()
// write to the database // write to the database
@ -628,7 +640,7 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc {
fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr), fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr),
}) })
} }
if (strings.Contains(ip, ":")) { if strings.Contains(ip, ":") {
suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip)) suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip))
} else { } else {
suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/32", ip)) suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/32", ip))

25
main.go
View file

@ -25,6 +25,12 @@ var (
// configuration variables // configuration variables
flagDisableLogin bool = false flagDisableLogin bool = false
flagBindAddress string = "0.0.0.0:5000" flagBindAddress string = "0.0.0.0:5000"
flagSmtpHostname string = "127.0.0.1"
flagSmtpPort int = 25
flagSmtpUsername string
flagSmtpPassword string
flagSmtpAuthType string = "None"
flagSmtpNoTLSCheck bool = false
flagSendgridApiKey string flagSendgridApiKey string
flagEmailFrom string flagEmailFrom string
flagEmailFromName string = "WireGuard UI" flagEmailFromName string = "WireGuard UI"
@ -45,6 +51,12 @@ func init() {
// command-line flags and env variables // command-line flags and env variables
flag.BoolVar(&flagDisableLogin, "disable-login", util.LookupEnvOrBool("DISABLE_LOGIN", flagDisableLogin), "Disable authentication on the app. This is potentially dangerous.") flag.BoolVar(&flagDisableLogin, "disable-login", util.LookupEnvOrBool("DISABLE_LOGIN", flagDisableLogin), "Disable authentication on the app. This is potentially dangerous.")
flag.StringVar(&flagBindAddress, "bind-address", util.LookupEnvOrString("BIND_ADDRESS", flagBindAddress), "Address:Port to which the app will be bound.") flag.StringVar(&flagBindAddress, "bind-address", util.LookupEnvOrString("BIND_ADDRESS", flagBindAddress), "Address:Port to which the app will be bound.")
flag.StringVar(&flagSmtpHostname, "smtp-hostname", util.LookupEnvOrString("SMTP_HOSTNAME", flagSmtpHostname), "SMTP Hostname")
flag.IntVar(&flagSmtpPort, "smtp-port", util.LookupEnvOrInt("SMTP_PORT", flagSmtpPort), "SMTP Port")
flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Password")
flag.StringVar(&flagSmtpPassword, "smtp-password", util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword), "SMTP Password")
flag.BoolVar(&flagSmtpNoTLSCheck, "smtp-no-tls-check", util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", flagSmtpNoTLSCheck), "Disable TLS verification for SMTP. This is potentially dangerous.")
flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : Plain or None.")
flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey), "Your sendgrid api key.") flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey), "Your sendgrid api key.")
flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.") flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.")
flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.") flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")
@ -54,6 +66,12 @@ func init() {
// update runtime config // update runtime config
util.DisableLogin = flagDisableLogin util.DisableLogin = flagDisableLogin
util.BindAddress = flagBindAddress util.BindAddress = flagBindAddress
util.SmtpHostname = flagSmtpHostname
util.SmtpPort = flagSmtpPort
util.SmtpUsername = flagSmtpUsername
util.SmtpPassword = flagSmtpPassword
util.SmtpAuthType = flagSmtpAuthType
util.SmtpNoTLSCheck = flagSmtpNoTLSCheck
util.SendgridApiKey = flagSendgridApiKey util.SendgridApiKey = flagSendgridApiKey
util.EmailFrom = flagEmailFrom util.EmailFrom = flagEmailFrom
util.EmailFromName = flagEmailFromName util.EmailFromName = flagEmailFromName
@ -103,7 +121,12 @@ func main() {
app.POST("/login", handler.Login(db)) app.POST("/login", handler.Login(db))
} }
sendmail := emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom) var sendmail emailer.Emailer
if util.SendgridApiKey != "" {
sendmail = emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom)
} else {
sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom)
}
app.GET("/_health", handler.Health()) app.GET("/_health", handler.Health())
app.GET("/logout", handler.Logout(), handler.ValidSession) app.GET("/logout", handler.Logout(), handler.ValidSession)

View file

@ -14,6 +14,7 @@ type Client struct {
Email string `json:"email"` Email string `json:"email"`
AllocatedIPs []string `json:"allocated_ips"` AllocatedIPs []string `json:"allocated_ips"`
AllowedIPs []string `json:"allowed_ips"` AllowedIPs []string `json:"allowed_ips"`
ExtraAllowedIPs []string `json:"extra_allowed_ips"`
UseServerDNS bool `json:"use_server_dns"` UseServerDNS bool `json:"use_server_dns"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`

View file

@ -163,6 +163,11 @@
<input type="text" data-role="tagsinput" class="form-control" id="client_allowed_ips" <input type="text" data-role="tagsinput" class="form-control" id="client_allowed_ips"
value="0.0.0.0/0"> value="0.0.0.0/0">
</div> </div>
<div class="form-group">
<label for="client_extra_allowed_ips" class="control-label">Extra Allowed IPs</label>
<input type="text" data-role="tagsinput" class="form-control"
id="client_extra_allowed_ips">
</div>
<div class="form-group"> <div class="form-group">
<div class="icheck-primary d-inline"> <div class="icheck-primary d-inline">
<input type="checkbox" id="use_server_dns" checked> <input type="checkbox" id="use_server_dns" checked>
@ -293,6 +298,12 @@
const allocated_ips = $("#client_allocated_ips").val().split(","); const allocated_ips = $("#client_allocated_ips").val().split(",");
const allowed_ips = $("#client_allowed_ips").val().split(","); const allowed_ips = $("#client_allowed_ips").val().split(",");
let use_server_dns = false; let use_server_dns = false;
let extra_allowed_ips = [];
if ($("#client_extra_allowed_ips").val() !== "") {
extra_allowed_ips = $("#client_extra_allowed_ips").val().split(",");
}
if ($("#use_server_dns").is(':checked')){ if ($("#use_server_dns").is(':checked')){
use_server_dns = true; use_server_dns = true;
@ -305,7 +316,7 @@
} }
const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips, const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips,
"use_server_dns": use_server_dns, "enabled": enabled}; "extra_allowed_ips": extra_allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled};
$.ajax({ $.ajax({
cache: false, cache: false,
@ -376,6 +387,16 @@
'placeholderColor': '#666666' 'placeholderColor': '#666666'
}); });
$("#client_extra_allowed_ips").tagsInput({
'width': '100%',
'height': '75%',
'interactive': true,
'defaultText': 'Add More',
'removeWithBackspace': true,
'minChars': 0,
'placeholderColor': '#666666'
});
// New client form validation // New client form validation
$(document).ready(function () { $(document).ready(function () {
$.validator.setDefaults({ $.validator.setDefaults({
@ -414,6 +435,7 @@
$("#client_name").val(""); $("#client_name").val("");
$("#client_email").val(""); $("#client_email").val("");
$("#client_allocated_ips").importTags(''); $("#client_allocated_ips").importTags('');
$("#client_extra_allowed_ips").importTags('');
updateIPAllocationSuggestion(); updateIPAllocationSuggestion();
}); });
}); });

View file

@ -57,6 +57,24 @@ Wireguard Clients
</div> </div>
<!-- /.modal-dialog --> <!-- /.modal-dialog -->
</div> </div>
<!-- /.modal -->
<div class="modal fade" id="modal_qr_client">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">QR Code</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<img id="qr_code" class="w-100" style="image-rendering: pixelated;" src="" alt="QR code" />
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
<div class="modal fade" id="modal_edit_client"> <div class="modal fade" id="modal_edit_client">
<div class="modal-dialog"> <div class="modal-dialog">
@ -86,6 +104,11 @@ Wireguard Clients
<label for="_client_allowed_ips" class="control-label">Allowed IPs</label> <label for="_client_allowed_ips" class="control-label">Allowed IPs</label>
<input type="text" data-role="tagsinput" class="form-control" id="_client_allowed_ips"> <input type="text" data-role="tagsinput" class="form-control" id="_client_allowed_ips">
</div> </div>
<div class="form-group">
<label for="_client_extra_allowed_ips" class="control-label">Extra Allowed IPs</label>
<input type="text" data-role="tagsinput" class="form-control"
id="_client_extra_allowed_ips">
</div>
<div class="form-group"> <div class="form-group">
<div class="icheck-primary d-inline"> <div class="icheck-primary d-inline">
<input type="checkbox" id="_use_server_dns"> <input type="checkbox" id="_use_server_dns">
@ -275,7 +298,7 @@ Wireguard Clients
// Edit client modal event // Edit client modal event
$(document).ready(function () { $(document).ready(function () {
$("#modal_edit_client").on('shown.bs.modal', function (event) { $("#modal_edit_client").on('show.bs.modal', function (event) {
let modal = $(this); let modal = $(this);
const button = $(event.relatedTarget); const button = $(event.relatedTarget);
const client_id = button.data('clientid'); const client_id = button.data('clientid');
@ -302,6 +325,16 @@ Wireguard Clients
'placeholderColor': '#666666' 'placeholderColor': '#666666'
}); });
modal.find("#_client_extra_allowed_ips").tagsInput({
'width': '100%',
'height': '75%',
'interactive': true,
'defaultText': 'Add More',
'removeWithBackspace' : true,
'minChars': 0,
'placeholderColor': '#666666'
})
// update client modal data // update client modal data
$.ajax({ $.ajax({
cache: false, cache: false,
@ -327,6 +360,11 @@ Wireguard Clients
modal.find("#_client_allowed_ips").addTag(obj); modal.find("#_client_allowed_ips").addTag(obj);
}); });
modal.find("#_client_extra_allowed_ips").importTags('');
client.extra_allowed_ips.forEach(function (obj) {
modal.find("#_client_extra_allowed_ips").addTag(obj);
});
modal.find("#_use_server_dns").prop("checked", client.use_server_dns); modal.find("#_use_server_dns").prop("checked", client.use_server_dns);
modal.find("#_enabled").prop("checked", client.enabled); modal.find("#_enabled").prop("checked", client.enabled);
}, },
@ -371,6 +409,11 @@ Wireguard Clients
const allocated_ips = $("#_client_allocated_ips").val().split(","); const allocated_ips = $("#_client_allocated_ips").val().split(",");
const allowed_ips = $("#_client_allowed_ips").val().split(","); const allowed_ips = $("#_client_allowed_ips").val().split(",");
let use_server_dns = false; let use_server_dns = false;
let extra_allowed_ips = [];
if( $("#_client_extra_allowed_ips").val() !== "" ) {
extra_allowed_ips = $("#_client_extra_allowed_ips").val().split(",");
}
if ($("#_use_server_dns").is(':checked')){ if ($("#_use_server_dns").is(':checked')){
use_server_dns = true; use_server_dns = true;
@ -383,7 +426,7 @@ Wireguard Clients
} }
const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips, const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips,
"allowed_ips": allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled}; "allowed_ips": allowed_ips, "extra_allowed_ips": extra_allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled};
$.ajax({ $.ajax({
cache: false, cache: false,
@ -415,7 +458,7 @@ Wireguard Clients
} }
} }
$("#modal_email_client").on('shown.bs.modal', function (event) { $("#modal_email_client").on('show.bs.modal', function (event) {
let modal = $(this); let modal = $(this);
const button = $(event.relatedTarget); const button = $(event.relatedTarget);
const client_id = button.data('clientid'); const client_id = button.data('clientid');
@ -439,6 +482,31 @@ Wireguard Clients
}); });
}); });
$("#modal_qr_client").on('show.bs.modal', function (event) {
let modal = $(this);
const button = $(event.relatedTarget);
const client_id = button.data('clientid');
const QRCodeImg = modal.find("#qr_code");
QRCodeImg.hide();
$.ajax({
cache: false,
method: 'GET',
url: '/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {
const client = resp.Client;
modal.find(".modal-title").text("QR Code for " + client.name);
QRCodeImg.attr('src', resp.QRCode).show();
},
error: function (jqXHR, exception) {
const responseJson = jQuery.parseJSON(jqXHR.responseText);
toastr.error(responseJson['message']);
}
});
});
$(document).ready(function () { $(document).ready(function () {
$.validator.setDefaults({ $.validator.setDefaults({
submitHandler: function (form) { submitHandler: function (form) {

View file

@ -20,5 +20,5 @@ PostDown = {{ .serverConfig.Interface.PostDown }}
[Peer] [Peer]
PublicKey = {{ .Client.PublicKey }} PublicKey = {{ .Client.PublicKey }}
{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }} {{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}
{{end}}AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}} {{end}}AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}}
{{end}}{{end}} {{end}}{{end}}

View file

@ -4,6 +4,12 @@ package util
var ( var (
DisableLogin bool DisableLogin bool
BindAddress string BindAddress string
SmtpHostname string
SmtpPort int
SmtpUsername string
SmtpPassword string
SmtpNoTLSCheck bool
SmtpAuthType string
SendgridApiKey string SendgridApiKey string
EmailFrom string EmailFrom string
EmailFromName string EmailFromName string

View file

@ -80,18 +80,34 @@ func ValidateCIDR(cidr string) bool {
} }
// ValidateCIDRList to validate a list of network CIDR // ValidateCIDRList to validate a list of network CIDR
func ValidateCIDRList(cidrs []string) bool { func ValidateCIDRList(cidrs []string, allowEmpty bool) bool {
for _, cidr := range cidrs { for _, cidr := range cidrs {
if allowEmpty {
if len(cidr) > 0 {
if ValidateCIDR(cidr) == false { if ValidateCIDR(cidr) == false {
return false return false
} }
} }
} else {
if ValidateCIDR(cidr) == false {
return false
}
}
}
return true return true
} }
// ValidateAllowedIPs to validate allowed ip addresses in CIDR format // ValidateAllowedIPs to validate allowed ip addresses in CIDR format
func ValidateAllowedIPs(cidrs []string) bool { func ValidateAllowedIPs(cidrs []string) bool {
if ValidateCIDRList(cidrs) == false { if ValidateCIDRList(cidrs, false) == false {
return false
}
return true
}
// ValidateExtraAllowedIPs to validate extra Allowed ip addresses, allowing empty strings
func ValidateExtraAllowedIPs(cidrs []string) bool {
if ValidateCIDRList(cidrs, true) == false {
return false return false
} }
return true return true
@ -99,7 +115,7 @@ func ValidateAllowedIPs(cidrs []string) bool {
// ValidateServerAddresses to validate allowed ip addresses in CIDR format // ValidateServerAddresses to validate allowed ip addresses in CIDR format
func ValidateServerAddresses(cidrs []string) bool { func ValidateServerAddresses(cidrs []string) bool {
if ValidateCIDRList(cidrs) == false { if ValidateCIDRList(cidrs, false) == false {
return false return false
} }
return true return true