wip: create/update/...
This commit is contained in:
parent
461a339ada
commit
e084a8aa66
|
@ -47,4 +47,8 @@
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-peer {
|
||||||
|
color: #d03131;
|
||||||
}
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
|
<title>{{ .Static.WebsiteTitle }} - Admin</title>
|
||||||
|
<meta name="description" content="{{ .Static.WebsiteTitle }}">
|
||||||
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
|
<!--link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"-->
|
||||||
|
<link rel="stylesheet" href="/fonts/fontawesome-all.min.css">
|
||||||
|
<link rel="stylesheet" href="/css/custom.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
{{template "prt_nav.html" .}}
|
||||||
|
<div class="container">
|
||||||
|
{{if eq .Peer.UID ""}}
|
||||||
|
<h1>Create a new client</h1>
|
||||||
|
{{else}}
|
||||||
|
<h1>Edit client <strong>{{.Peer.Identifier}}</strong></h1>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if $.Alerts.HasAlert}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="alert alert-{{$.Alerts.Type}}" role="alert">
|
||||||
|
{{$.Alerts.Message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="uid" value="{{.Peer.UID}}">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputServerPublicKey">Public Key</label>
|
||||||
|
<input type="text" name="pkey" disabled class="form-control" id="inputServerPublicKey" value="{{.Peer.PublicKey}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputIdentifier">Client Friendly Name</label>
|
||||||
|
<input type="text" name="identifier" class="form-control" id="inputIdentifier" value="{{.Peer.Identifier}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputEmail">Client Email Address</label>
|
||||||
|
<input type="email" name="mail" class="form-control" id="inputEmail" value="{{.Peer.Email}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputIP">Client IP Address</label>
|
||||||
|
<input type="text" name="ip" class="form-control" id="inputIP" value="{{.Peer.IPsStr}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputAllowedIP">Allowed IPs</label>
|
||||||
|
<input type="text" name="allowedip" class="form-control" id="inputAllowedIP" value="{{.Peer.AllowedIPsStr}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" name="isdisabled" type="checkbox" value="true" id="inputDisabled" {{if .Peer.DeactivatedAt}}checked{{end}}>
|
||||||
|
<label class="form-check-label" for="inputDisabled">
|
||||||
|
Disabled
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" name="ignorekeepalive" type="checkbox" value="true" id="inputIgnoreKeepalive" {{if .Peer.IgnorePersistentKeepalive}}checked{{end}}>
|
||||||
|
<label class="form-check-label" for="inputIgnoreKeepalive">
|
||||||
|
Ignore persistent keepalive
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
|
<a href="/admin" class="btn btn-secondary">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{template "prt_footer.html"}}
|
||||||
|
<script src="/js/jquery.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="/js/jquery.easing.js"></script>
|
||||||
|
<script src="/js/custom.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,114 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||||
|
<title>{{ .Static.WebsiteTitle }} - Admin</title>
|
||||||
|
<meta name="description" content="{{ .Static.WebsiteTitle }}">
|
||||||
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
|
<!--link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"-->
|
||||||
|
<link rel="stylesheet" href="/fonts/fontawesome-all.min.css">
|
||||||
|
<link rel="stylesheet" href="/css/custom.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="page-top">
|
||||||
|
{{template "prt_nav.html" .}}
|
||||||
|
<div class="container">
|
||||||
|
<h1>Edit interface <strong>{{.Device.DeviceName}}</strong></h1>
|
||||||
|
|
||||||
|
{{if $.Alerts.HasAlert}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="alert alert-{{$.Alerts.Type}}" role="alert">
|
||||||
|
{{$.Alerts.Message}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="device" value="{{.Device.DeviceName}}">
|
||||||
|
<h3>Server's interface configuration</h3>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputServerPublicKey">Public Key</label>
|
||||||
|
<input type="text" name="pubkey" disabled class="form-control" id="inputServerPublicKey" value="{{.Device.PublicKey}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputListenPort">Listen port</label>
|
||||||
|
<input type="number" name="port" class="form-control" id="inputListenPort" placeholder="51820" value="{{.Device.ListenPort}}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputIPs">Server IP address</label>
|
||||||
|
<input type="text" name="ip" class="form-control" id="inputIPs" placeholder="10.6.6.1/24" value="{{.Device.IPsStr}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Client's global configuration</h3>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputPublicEndpoint">Public Enpoint for Clients</label>
|
||||||
|
<input type="text" name="endpoint" class="form-control" id="inputPublicEndpoint" placeholder="vpn.company.com:51820" value="{{.Device.Endpoint}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputDNS">DNS Servers</label>
|
||||||
|
<input type="text" name="dns" class="form-control" id="inputDNS" placeholder="1.1.1.1" value="{{.Device.DNSStr}}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputAllowedIP">Default allowed IPs</label>
|
||||||
|
<input type="text" name="allowedip" class="form-control" id="inputAllowedIP" placeholder="10.6.6.0/24" value="{{.Device.AllowedIPsStr}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputMTU">Global MTU</label>
|
||||||
|
<input type="number" name="mtu" class="form-control" id="inputMTU" placeholder="0" value="{{.Device.Mtu}}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="inputPersistentKeepalive">Persistent Keepalive</label>
|
||||||
|
<input type="number" name="keepalive" class="form-control" id="inputPersistentKeepalive" placeholder="16" value="{{.Device.PersistentKeepalive}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Interface configuration hooks</h3>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputPreUp">Pre Up</label>
|
||||||
|
<input type="text" name="preup" class="form-control" id="inputPreUp" value="{{.Device.PreUp}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputPostUp">Post Up</label>
|
||||||
|
<input type="text" name="postup" class="form-control" id="inputPostUp" value="{{.Device.PostUp}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputPreDown">Pre Down</label>
|
||||||
|
<input type="text" name="predown" class="form-control" id="inputPreDown" value="{{.Device.PreDown}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label for="inputPostDown">Post Down</label>
|
||||||
|
<input type="text" name="postdown" class="form-control" id="inputPostDown" value="{{.Device.PostDown}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
|
<a href="/admin" class="btn btn-secondary">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{template "prt_footer.html"}}
|
||||||
|
<script src="/js/jquery.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="/js/jquery.easing.js"></script>
|
||||||
|
<script src="/js/custom.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -95,7 +95,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range $i, $p :=.Peers}}
|
{{range $i, $p :=.Peers}}
|
||||||
<tr id="user-pos-{{$i}}">
|
<tr id="user-pos-{{$i}}" {{if $p.DeactivatedAt}}class="disabled-peer"{{end}}>
|
||||||
<th scope="row" class="list-image-cell">
|
<th scope="row" class="list-image-cell">
|
||||||
<a href="#{{$p.UID}}" data-toggle="collapse" class="collapse-indicator collapsed"></a>
|
<a href="#{{$p.UID}}" data-toggle="collapse" class="collapse-indicator collapsed"></a>
|
||||||
<!-- online check -->
|
<!-- online check -->
|
||||||
|
@ -104,11 +104,11 @@
|
||||||
<td>{{$p.PublicKey}}</td>
|
<td>{{$p.PublicKey}}</td>
|
||||||
<td>{{$p.Email}}</td>
|
<td>{{$p.Email}}</td>
|
||||||
<td>{{$p.IPsStr}}</td>
|
<td>{{$p.IPsStr}}</td>
|
||||||
<td>{{$p.Peer.ReceiveBytes}} / {{$p.Peer.TransmitBytes}}</td>
|
<td>{{if $p.DeactivatedAt}}-{{else}}{{$p.Peer.ReceiveBytes}} / {{$p.Peer.TransmitBytes}}{{end}}</td>
|
||||||
<td>{{$p.Peer.LastHandshakeTime}}</td>
|
<td>{{if $p.DeactivatedAt}}-{{else}}{{$p.Peer.LastHandshakeTime}}{{end}}</td>
|
||||||
<td>
|
<td>
|
||||||
{{if eq $.Session.IsAdmin true}}
|
{{if eq $.Session.IsAdmin true}}
|
||||||
<a href="/admin/user/edit?pos={{$i}}"><i class="fas fa-cog"></i></a>
|
<a href="/admin/peer/edit?pkey={{$p.PublicKey}}"><i class="fas fa-cog"></i></a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
<div class="col-md-6 leftBorder">
|
<div class="col-md-6 leftBorder">
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" data-toggle="tab" href="#t1{{$p.UID}}">General</a>
|
<a class="nav-link active" data-toggle="tab" href="#t1{{$p.UID}}">Personal</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-toggle="tab" href="#t2{{$p.UID}}">Configuration</a>
|
<a class="nav-link" data-toggle="tab" href="#t2{{$p.UID}}">Configuration</a>
|
||||||
|
@ -130,9 +130,17 @@
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content" id="tabContent{{$p.UID}}">
|
<div class="tab-content" id="tabContent{{$p.UID}}">
|
||||||
<div id="t1{{$p.UID}}" class="tab-pane fade active show">
|
<div id="t1{{$p.UID}}" class="tab-pane fade active show">
|
||||||
<ul>
|
{{if not $p.LdapUser}}
|
||||||
<li>0</li>
|
<p>No LDAP user-information available...</p>
|
||||||
</ul>
|
{{else}}
|
||||||
|
<ul>
|
||||||
|
<li>Firstname: {{$p.LdapUser.Firstname}}</li>
|
||||||
|
<li>Lastname: {{$p.LdapUser.Lastname}}</li>
|
||||||
|
<li>Phone: {{$p.UID}}</li>
|
||||||
|
<li>Mail: {{$p.LdapUser.Mail}}</li>
|
||||||
|
<li>Department: {{$p.UID}}</li>
|
||||||
|
</ul>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div id="t2{{$p.UID}}" class="tab-pane fade">
|
<div id="t2{{$p.UID}}" class="tab-pane fade">
|
||||||
<pre>{{$p.Config}}</pre>
|
<pre>{{$p.Config}}</pre>
|
||||||
|
|
|
@ -90,12 +90,12 @@ func (s *Server) Setup() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup user manager
|
// Setup user manager
|
||||||
s.users = NewUserManager()
|
if s.users = NewUserManager(s.wg, s.ldapUsers); s.users == nil {
|
||||||
if s.users == nil {
|
|
||||||
return errors.New("unable to setup user manager")
|
return errors.New("unable to setup user manager")
|
||||||
}
|
}
|
||||||
s.users.InitWithDevice(s.wg.GetDeviceInfo())
|
if err := s.users.InitFromCurrentInterface(); err != nil {
|
||||||
s.users.InitWithPeers(s.wg.GetPeerList())
|
return errors.New("unable to initialize user manager")
|
||||||
|
}
|
||||||
|
|
||||||
dir := s.getExecutableDirectory()
|
dir := s.getExecutableDirectory()
|
||||||
rDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
rDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
|
|
@ -2,7 +2,10 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
@ -32,24 +35,9 @@ func (s *Server) HandleError(c *gin.Context, code int, message, details string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetAdminIndex(c *gin.Context) {
|
func (s *Server) GetAdminIndex(c *gin.Context) {
|
||||||
dev, err := s.wg.GetDeviceInfo()
|
|
||||||
if err != nil {
|
|
||||||
s.HandleError(c, http.StatusInternalServerError, "WireGuard error", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
peers, err := s.wg.GetPeerList()
|
|
||||||
if err != nil {
|
|
||||||
s.HandleError(c, http.StatusInternalServerError, "WireGuard error", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
device := s.users.GetDevice()
|
device := s.users.GetDevice()
|
||||||
device.Interface = dev
|
users := s.users.GetAllUsers()
|
||||||
|
|
||||||
users := make([]User, len(peers))
|
|
||||||
for i, peer := range peers {
|
|
||||||
users[i] = s.users.GetOrCreateUserForPeer(peer)
|
|
||||||
}
|
|
||||||
c.HTML(http.StatusOK, "admin_index.html", struct {
|
c.HTML(http.StatusOK, "admin_index.html", struct {
|
||||||
Route string
|
Route string
|
||||||
Session SessionData
|
Session SessionData
|
||||||
|
@ -65,8 +53,332 @@ func (s *Server) GetAdminIndex(c *gin.Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetAdminEditInterface(c *gin.Context) {
|
||||||
|
device := s.users.GetDevice()
|
||||||
|
users := s.users.GetAllUsers()
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "admin_edit_interface.html", struct {
|
||||||
|
Route string
|
||||||
|
Alerts AlertData
|
||||||
|
Session SessionData
|
||||||
|
Static StaticData
|
||||||
|
Peers []User
|
||||||
|
Device Device
|
||||||
|
}{
|
||||||
|
Route: c.Request.URL.Path,
|
||||||
|
Alerts: s.getAlertData(c),
|
||||||
|
Session: s.getSessionData(c),
|
||||||
|
Static: s.getStaticData(),
|
||||||
|
Peers: users,
|
||||||
|
Device: device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) PostAdminEditInterface(c *gin.Context) {
|
||||||
|
device := s.users.GetDevice()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
device.ListenPort, err = strconv.Atoi(c.PostForm("port"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid port: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipField := c.PostForm("ip")
|
||||||
|
ips := strings.Split(ipField, ",")
|
||||||
|
validatedIPs := make([]string, 0, len(ips))
|
||||||
|
for i := range ips {
|
||||||
|
ips[i] = strings.TrimSpace(ips[i])
|
||||||
|
if ips[i] != "" {
|
||||||
|
validatedIPs = append(validatedIPs, ips[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(validatedIPs) == 0 {
|
||||||
|
s.setAlert(c, "invalid ip address", "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
device.IPs = validatedIPs
|
||||||
|
|
||||||
|
device.Endpoint = c.PostForm("endpoint")
|
||||||
|
|
||||||
|
dnsField := c.PostForm("dns")
|
||||||
|
dns := strings.Split(dnsField, ",")
|
||||||
|
validatedDNS := make([]string, 0, len(dns))
|
||||||
|
for i := range dns {
|
||||||
|
dns[i] = strings.TrimSpace(dns[i])
|
||||||
|
if dns[i] != "" {
|
||||||
|
validatedDNS = append(validatedDNS, dns[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device.DNS = validatedDNS
|
||||||
|
|
||||||
|
allowedIPField := c.PostForm("allowedip")
|
||||||
|
allowedIP := strings.Split(allowedIPField, ",")
|
||||||
|
validatedAllowedIP := make([]string, 0, len(allowedIP))
|
||||||
|
for i := range allowedIP {
|
||||||
|
allowedIP[i] = strings.TrimSpace(allowedIP[i])
|
||||||
|
if allowedIP[i] != "" {
|
||||||
|
validatedAllowedIP = append(validatedAllowedIP, allowedIP[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device.AllowedIPs = validatedAllowedIP
|
||||||
|
|
||||||
|
device.Mtu, err = strconv.Atoi(c.PostForm("mtu"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid MTU: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device.PersistentKeepalive, err = strconv.Atoi(c.PostForm("keepalive"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid PersistentKeepalive: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update WireGuard device
|
||||||
|
err = s.wg.UpdateDevice(device.DeviceName, device.GetDeviceConfig())
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update device in WireGuard: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = s.users.UpdateDevice(device)
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update device in database: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setAlert(c, "changes applied successfully", "success")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetAdminEditPeer(c *gin.Context) {
|
||||||
|
device := s.users.GetDevice()
|
||||||
|
user := s.users.GetUserByKey(c.Query("pkey"))
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "admin_edit_client.html", struct {
|
||||||
|
Route string
|
||||||
|
Alerts AlertData
|
||||||
|
Session SessionData
|
||||||
|
Static StaticData
|
||||||
|
Peer User
|
||||||
|
Device Device
|
||||||
|
}{
|
||||||
|
Route: c.Request.URL.Path,
|
||||||
|
Alerts: s.getAlertData(c),
|
||||||
|
Session: s.getSessionData(c),
|
||||||
|
Static: s.getStaticData(),
|
||||||
|
Peer: user,
|
||||||
|
Device: device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) PostAdminEditPeer(c *gin.Context) {
|
||||||
|
user := s.users.GetUserByKey(c.Query("pkey"))
|
||||||
|
urlEncodedKey := url.QueryEscape(c.Query("pkey"))
|
||||||
|
var err error
|
||||||
|
|
||||||
|
user.Identifier = c.PostForm("identifier")
|
||||||
|
if user.Identifier == "" {
|
||||||
|
s.setAlert(c, "invalid identifier, must not be empty", "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Email = c.PostForm("mail")
|
||||||
|
if user.Email == "" {
|
||||||
|
s.setAlert(c, "invalid email, must not be empty", "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipField := c.PostForm("ip")
|
||||||
|
ips := strings.Split(ipField, ",")
|
||||||
|
validatedIPs := make([]string, 0, len(ips))
|
||||||
|
for i := range ips {
|
||||||
|
ips[i] = strings.TrimSpace(ips[i])
|
||||||
|
if ips[i] != "" {
|
||||||
|
validatedIPs = append(validatedIPs, ips[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(validatedIPs) == 0 {
|
||||||
|
s.setAlert(c, "invalid ip address", "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.IPs = validatedIPs
|
||||||
|
|
||||||
|
allowedIPField := c.PostForm("allowedip")
|
||||||
|
allowedIP := strings.Split(allowedIPField, ",")
|
||||||
|
validatedAllowedIP := make([]string, 0, len(allowedIP))
|
||||||
|
for i := range allowedIP {
|
||||||
|
allowedIP[i] = strings.TrimSpace(allowedIP[i])
|
||||||
|
if allowedIP[i] != "" {
|
||||||
|
validatedAllowedIP = append(validatedAllowedIP, allowedIP[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.AllowedIPs = validatedAllowedIP
|
||||||
|
|
||||||
|
user.IgnorePersistentKeepalive = c.PostForm("ignorekeepalive") != ""
|
||||||
|
disabled := c.PostForm("isdisabled") != ""
|
||||||
|
now := time.Now()
|
||||||
|
if disabled && user.DeactivatedAt == nil {
|
||||||
|
user.DeactivatedAt = &now
|
||||||
|
} else if !disabled {
|
||||||
|
user.DeactivatedAt = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update WireGuard device
|
||||||
|
if user.DeactivatedAt == &now {
|
||||||
|
err = s.wg.RemovePeer(user.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to remove peer in WireGuard: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if user.DeactivatedAt == nil && user.Peer != nil {
|
||||||
|
err = s.wg.UpdatePeer(user.GetPeerConfig())
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update peer in WireGuard: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if user.DeactivatedAt == nil && user.Peer == nil {
|
||||||
|
err = s.wg.AddPeer(user.GetPeerConfig())
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to add peer in WireGuard: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = s.users.UpdateUser(user)
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update user in database: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setAlert(c, "changes applied successfully", "success")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/peer/edit?pkey="+urlEncodedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetAdminCreatePeer(c *gin.Context) {
|
||||||
|
device := s.users.GetDevice()
|
||||||
|
user := s.users.GetUserByKey(c.Query("pkey"))
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "admin_edit_client.html", struct {
|
||||||
|
Route string
|
||||||
|
Alerts AlertData
|
||||||
|
Session SessionData
|
||||||
|
Static StaticData
|
||||||
|
Peer User
|
||||||
|
Device Device
|
||||||
|
}{
|
||||||
|
Route: c.Request.URL.Path,
|
||||||
|
Alerts: s.getAlertData(c),
|
||||||
|
Session: s.getSessionData(c),
|
||||||
|
Static: s.getStaticData(),
|
||||||
|
Peer: user,
|
||||||
|
Device: device,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) PostAdminCreatePeer(c *gin.Context) {
|
||||||
|
device := s.users.GetDevice()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
device.ListenPort, err = strconv.Atoi(c.PostForm("port"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid port: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipField := c.PostForm("ip")
|
||||||
|
ips := strings.Split(ipField, ",")
|
||||||
|
validatedIPs := make([]string, 0, len(ips))
|
||||||
|
for i := range ips {
|
||||||
|
ips[i] = strings.TrimSpace(ips[i])
|
||||||
|
if ips[i] != "" {
|
||||||
|
validatedIPs = append(validatedIPs, ips[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(validatedIPs) == 0 {
|
||||||
|
s.setAlert(c, "invalid ip address", "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
device.IPs = validatedIPs
|
||||||
|
|
||||||
|
device.Endpoint = c.PostForm("endpoint")
|
||||||
|
|
||||||
|
dnsField := c.PostForm("dns")
|
||||||
|
dns := strings.Split(dnsField, ",")
|
||||||
|
validatedDNS := make([]string, 0, len(dns))
|
||||||
|
for i := range dns {
|
||||||
|
dns[i] = strings.TrimSpace(dns[i])
|
||||||
|
if dns[i] != "" {
|
||||||
|
validatedDNS = append(validatedDNS, dns[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device.DNS = validatedDNS
|
||||||
|
|
||||||
|
allowedIPField := c.PostForm("allowedip")
|
||||||
|
allowedIP := strings.Split(allowedIPField, ",")
|
||||||
|
validatedAllowedIP := make([]string, 0, len(allowedIP))
|
||||||
|
for i := range allowedIP {
|
||||||
|
allowedIP[i] = strings.TrimSpace(allowedIP[i])
|
||||||
|
if allowedIP[i] != "" {
|
||||||
|
validatedAllowedIP = append(validatedAllowedIP, allowedIP[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device.AllowedIPs = validatedAllowedIP
|
||||||
|
|
||||||
|
device.Mtu, err = strconv.Atoi(c.PostForm("mtu"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid MTU: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device.PersistentKeepalive, err = strconv.Atoi(c.PostForm("keepalive"))
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "invalid PersistentKeepalive: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update WireGuard device
|
||||||
|
err = s.wg.UpdateDevice(device.DeviceName, device.GetDeviceConfig())
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update device in WireGuard: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = s.users.UpdateDevice(device)
|
||||||
|
if err != nil {
|
||||||
|
s.setAlert(c, "failed to update device in database: "+err.Error(), "danger")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setAlert(c, "changes applied successfully", "success")
|
||||||
|
c.Redirect(http.StatusSeeOther, "/admin/device/edit")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) GetUserQRCode(c *gin.Context) {
|
func (s *Server) GetUserQRCode(c *gin.Context) {
|
||||||
user := s.users.GetUser(c.Param("pkey"))
|
user := s.users.GetUserByKey(c.Query("pkey"))
|
||||||
png, err := user.GetQRCode()
|
png, err := user.GetQRCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.HandleError(c, http.StatusInternalServerError, "QRCode error", err.Error())
|
s.HandleError(c, http.StatusInternalServerError, "QRCode error", err.Error())
|
||||||
|
|
|
@ -20,6 +20,12 @@ func SetupRoutes(s *Server) {
|
||||||
admin := s.server.Group("/admin")
|
admin := s.server.Group("/admin")
|
||||||
admin.Use(s.RequireAuthentication(s.config.AdminLdapGroup))
|
admin.Use(s.RequireAuthentication(s.config.AdminLdapGroup))
|
||||||
admin.GET("/", s.GetAdminIndex)
|
admin.GET("/", s.GetAdminIndex)
|
||||||
|
admin.GET("/device/edit", s.GetAdminEditInterface)
|
||||||
|
admin.POST("/device/edit", s.PostAdminEditInterface)
|
||||||
|
admin.GET("/peer/edit", s.GetAdminEditPeer)
|
||||||
|
admin.POST("/peer/edit", s.PostAdminEditPeer)
|
||||||
|
admin.GET("/peer/create", s.GetAdminCreatePeer)
|
||||||
|
admin.POST("/peer/create", s.PostAdminCreatePeer)
|
||||||
|
|
||||||
// User routes
|
// User routes
|
||||||
user := s.server.Group("/user")
|
user := s.server.Group("/user")
|
||||||
|
|
|
@ -22,10 +22,14 @@ import (
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// USER ----------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Peer wgtypes.Peer `gorm:"-"`
|
Peer *wgtypes.Peer `gorm:"-"`
|
||||||
User *ldap.UserCacheHolderEntry `gorm:"-"` // optional, it is still possible to have users without ldap
|
LdapUser *ldap.UserCacheHolderEntry `gorm:"-"` // optional, it is still possible to have users without ldap
|
||||||
Config string `gorm:"-"`
|
Config string `gorm:"-"`
|
||||||
|
|
||||||
UID string // uid for html identification
|
UID string // uid for html identification
|
||||||
IsOnline bool `gorm:"-"`
|
IsOnline bool `gorm:"-"`
|
||||||
|
@ -48,6 +52,28 @@ type User struct {
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u User) GetClientConfigFile(device Device) ([]byte, error) {
|
||||||
|
tpl, err := template.New("client").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(wireguard.ClientCfgTpl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tplBuff bytes.Buffer
|
||||||
|
|
||||||
|
err = tpl.Execute(&tplBuff, struct {
|
||||||
|
Client User
|
||||||
|
Server Device
|
||||||
|
}{
|
||||||
|
Client: u,
|
||||||
|
Server: device,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tplBuff.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u User) GetPeerConfig() wgtypes.PeerConfig {
|
func (u User) GetPeerConfig() wgtypes.PeerConfig {
|
||||||
publicKey, _ := wgtypes.ParseKey(u.PublicKey)
|
publicKey, _ := wgtypes.ParseKey(u.PublicKey)
|
||||||
var presharedKey *wgtypes.Key
|
var presharedKey *wgtypes.Key
|
||||||
|
@ -87,6 +113,18 @@ func (u User) GetQRCode() ([]byte, error) {
|
||||||
return png, nil
|
return png, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u User) IsValid() bool {
|
||||||
|
if u.PublicKey == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DEVICE --------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
Interface *wgtypes.Device `gorm:"-"`
|
Interface *wgtypes.Device `gorm:"-"`
|
||||||
|
|
||||||
|
@ -112,6 +150,9 @@ type Device struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Device) IsValid() bool {
|
func (d Device) IsValid() bool {
|
||||||
|
if d.PublicKey == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if len(d.IPs) == 0 {
|
if len(d.IPs) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -122,12 +163,33 @@ func (d Device) IsValid() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserManager struct {
|
func (d Device) GetDeviceConfig() wgtypes.Config {
|
||||||
db *gorm.DB
|
var privateKey *wgtypes.Key
|
||||||
|
if d.PrivateKey != "" {
|
||||||
|
pKey, _ := wgtypes.ParseKey(d.PrivateKey)
|
||||||
|
privateKey = &pKey
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := wgtypes.Config{
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
ListenPort: &d.ListenPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserManager() *UserManager {
|
//
|
||||||
um := &UserManager{}
|
// USER-MANAGER --------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
type UserManager struct {
|
||||||
|
db *gorm.DB
|
||||||
|
wg *wireguard.Manager
|
||||||
|
ldapUsers *ldap.SynchronizedUserCacheHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserManager(wg *wireguard.Manager, ldapUsers *ldap.SynchronizedUserCacheHolder) *UserManager {
|
||||||
|
um := &UserManager{wg: wg, ldapUsers: ldapUsers}
|
||||||
var err error
|
var err error
|
||||||
um.db, err = gorm.Open(sqlite.Open("wg_portal.db"), &gorm.Config{})
|
um.db, err = gorm.Open(sqlite.Open("wg_portal.db"), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -144,52 +206,32 @@ func NewUserManager() *UserManager {
|
||||||
return um
|
return um
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserManager) InitWithPeers(peers []wgtypes.Peer, err error) {
|
func (u *UserManager) InitFromCurrentInterface() error {
|
||||||
|
peers, err := u.wg.GetPeerList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to init user-manager from peers: %v", err)
|
log.Errorf("failed to init user-manager from peers: %v", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
for _, peer := range peers {
|
device, err := u.wg.GetDeviceInfo()
|
||||||
u.GetOrCreateUserForPeer(peer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserManager) InitWithDevice(dev *wgtypes.Device, err error) {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to init user-manager from device: %v", err)
|
log.Errorf("failed to init user-manager from device: %v", err)
|
||||||
return
|
return err
|
||||||
}
|
|
||||||
u.GetOrCreateDevice(*dev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserManager) GetAllUsers() []User {
|
|
||||||
users := make([]User, 0)
|
|
||||||
u.db.Find(&users)
|
|
||||||
|
|
||||||
for i := range users {
|
|
||||||
users[i].AllowedIPs = strings.Split(users[i].AllowedIPsStr, ", ")
|
|
||||||
users[i].IPs = strings.Split(users[i].IPsStr, ", ")
|
|
||||||
tmpCfg, _ := u.GetPeerConfigFile(users[i])
|
|
||||||
users[i].Config = string(tmpCfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return users
|
// Check if entries already exist in database, if not create them
|
||||||
}
|
for _, peer := range peers {
|
||||||
|
if err := u.validateOrCreateUserForPeer(peer); err != nil {
|
||||||
func (u *UserManager) GetDevice() Device {
|
return err
|
||||||
devices := make([]Device, 0, 1)
|
}
|
||||||
u.db.Find(&devices)
|
}
|
||||||
|
if err := u.validateOrCreateDevice(*device); err != nil {
|
||||||
for i := range devices {
|
return err
|
||||||
devices[i].AllowedIPs = strings.Split(devices[i].AllowedIPsStr, ", ")
|
|
||||||
devices[i].IPs = strings.Split(devices[i].IPsStr, ", ")
|
|
||||||
devices[i].DNS = strings.Split(devices[i].DNSStr, ", ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return devices[0]
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserManager) GetOrCreateUserForPeer(peer wgtypes.Peer) User {
|
func (u *UserManager) validateOrCreateUserForPeer(peer wgtypes.Peer) error {
|
||||||
user := User{}
|
user := User{}
|
||||||
u.db.Where("public_key = ?", peer.PublicKey.String()).FirstOrInit(&user)
|
u.db.Where("public_key = ?", peer.PublicKey.String()).FirstOrInit(&user)
|
||||||
|
|
||||||
|
@ -215,25 +257,92 @@ func (u *UserManager) GetOrCreateUserForPeer(peer wgtypes.Peer) User {
|
||||||
res := u.db.Create(&user)
|
res := u.db.Create(&user)
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log.Errorf("failed to create autodetected peer: %v", res.Error)
|
log.Errorf("failed to create autodetected peer: %v", res.Error)
|
||||||
|
return res.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.IPs = strings.Split(user.IPsStr, ", ")
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) validateOrCreateDevice(dev wgtypes.Device) error {
|
||||||
|
device := Device{}
|
||||||
|
u.db.Where("device_name = ?", dev.Name).FirstOrInit(&device)
|
||||||
|
|
||||||
|
if device.PublicKey == "" { // device not found, create
|
||||||
|
device.PublicKey = dev.PublicKey.String()
|
||||||
|
device.PrivateKey = dev.PrivateKey.String()
|
||||||
|
device.DeviceName = dev.Name
|
||||||
|
device.ListenPort = dev.ListenPort
|
||||||
|
device.Mtu = 0
|
||||||
|
device.PersistentKeepalive = 16 // Default
|
||||||
|
|
||||||
|
res := u.db.Create(&device)
|
||||||
|
if res.Error != nil {
|
||||||
|
log.Errorf("failed to create autodetected device: %v", res.Error)
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) populateUserData(user *User) {
|
||||||
user.AllowedIPs = strings.Split(user.AllowedIPsStr, ", ")
|
user.AllowedIPs = strings.Split(user.AllowedIPsStr, ", ")
|
||||||
tmpCfg, _ := u.GetPeerConfigFile(user)
|
user.IPs = strings.Split(user.IPsStr, ", ")
|
||||||
|
// Set config file
|
||||||
|
tmpCfg, _ := user.GetClientConfigFile(u.GetDevice())
|
||||||
user.Config = string(tmpCfg)
|
user.Config = string(tmpCfg)
|
||||||
|
|
||||||
|
// set data from WireGuard interface
|
||||||
|
user.Peer, _ = u.wg.GetPeer(user.PublicKey)
|
||||||
|
user.IsOnline = false // todo: calculate online status
|
||||||
|
|
||||||
|
// set ldap data
|
||||||
|
user.LdapUser = u.ldapUsers.GetUserData(u.ldapUsers.GetUserDNByMail(user.Email))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) populateDeviceData(device *Device) {
|
||||||
|
device.AllowedIPs = strings.Split(device.AllowedIPsStr, ", ")
|
||||||
|
device.IPs = strings.Split(device.IPsStr, ", ")
|
||||||
|
device.DNS = strings.Split(device.DNSStr, ", ")
|
||||||
|
|
||||||
|
// set data from WireGuard interface
|
||||||
|
device.Interface, _ = u.wg.GetDeviceInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) GetAllUsers() []User {
|
||||||
|
users := make([]User, 0)
|
||||||
|
u.db.Find(&users)
|
||||||
|
|
||||||
|
for i := range users {
|
||||||
|
u.populateUserData(&users[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) GetDevice() Device {
|
||||||
|
devices := make([]Device, 0, 1)
|
||||||
|
u.db.Find(&devices)
|
||||||
|
|
||||||
|
for i := range devices {
|
||||||
|
u.populateDeviceData(&devices[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices[0] // use first device for now... more to come?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) GetUserByKey(publicKey string) User {
|
||||||
|
user := User{}
|
||||||
|
u.db.Where("public_key = ?", publicKey).FirstOrInit(&user)
|
||||||
|
u.populateUserData(&user)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserManager) GetUser(publicKey string) User {
|
func (u *UserManager) GetUserByMail(mail string) User {
|
||||||
user := User{}
|
user := User{}
|
||||||
u.db.Where("public_key = ?", publicKey).FirstOrInit(&user)
|
u.db.Where("email = ?", mail).FirstOrInit(&user)
|
||||||
|
u.populateUserData(&user)
|
||||||
user.IPs = strings.Split(user.IPsStr, ", ")
|
|
||||||
user.AllowedIPs = strings.Split(user.AllowedIPsStr, ", ")
|
|
||||||
tmpCfg, _ := u.GetPeerConfigFile(user)
|
|
||||||
user.Config = string(tmpCfg)
|
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
@ -268,6 +377,21 @@ func (u *UserManager) UpdateUser(user User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserManager) UpdateDevice(device Device) error {
|
||||||
|
device.UpdatedAt = time.Now()
|
||||||
|
device.AllowedIPsStr = strings.Join(device.AllowedIPs, ", ")
|
||||||
|
device.IPsStr = strings.Join(device.IPs, ", ")
|
||||||
|
device.DNSStr = strings.Join(device.DNS, ", ")
|
||||||
|
|
||||||
|
res := u.db.Save(&device)
|
||||||
|
if res.Error != nil {
|
||||||
|
log.Errorf("failed to update device: %v", res.Error)
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserManager) GetAllReservedIps() ([]string, error) {
|
func (u *UserManager) GetAllReservedIps() ([]string, error) {
|
||||||
reservedIps := make([]string, 0)
|
reservedIps := make([]string, 0)
|
||||||
users := u.GetAllUsers()
|
users := u.GetAllUsers()
|
||||||
|
@ -328,50 +452,3 @@ func (u *UserManager) GetAvailableIp(cidr string, reserved []string) (string, er
|
||||||
|
|
||||||
return "", errors.New("no more available address from cidr")
|
return "", errors.New("no more available address from cidr")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserManager) GetOrCreateDevice(dev wgtypes.Device) Device {
|
|
||||||
device := Device{}
|
|
||||||
u.db.Where("device_name = ?", dev.Name).FirstOrInit(&device)
|
|
||||||
|
|
||||||
if device.PublicKey == "" { // device not found, create
|
|
||||||
device.PublicKey = dev.PublicKey.String()
|
|
||||||
device.PrivateKey = dev.PrivateKey.String()
|
|
||||||
device.DeviceName = dev.Name
|
|
||||||
device.ListenPort = dev.ListenPort
|
|
||||||
device.Mtu = 0
|
|
||||||
device.PersistentKeepalive = 16 // Default
|
|
||||||
|
|
||||||
res := u.db.Create(&device)
|
|
||||||
if res.Error != nil {
|
|
||||||
log.Errorf("failed to create autodetected device: %v", res.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
device.IPs = strings.Split(device.IPsStr, ", ")
|
|
||||||
device.AllowedIPs = strings.Split(device.AllowedIPsStr, ", ")
|
|
||||||
device.DNS = strings.Split(device.DNSStr, ", ")
|
|
||||||
|
|
||||||
return device
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UserManager) GetPeerConfigFile(user User) ([]byte, error) {
|
|
||||||
tpl, err := template.New("client").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(wireguard.ClientCfgTpl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var tplBuff bytes.Buffer
|
|
||||||
|
|
||||||
err = tpl.Execute(&tplBuff, struct {
|
|
||||||
Client User
|
|
||||||
Server Device
|
|
||||||
}{
|
|
||||||
Client: user,
|
|
||||||
Server: u.GetDevice(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tplBuff.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -80,6 +80,19 @@ func (m *Manager) AddPeer(cfg wgtypes.PeerConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) UpdatePeer(cfg wgtypes.PeerConfig) error {
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
|
||||||
|
cfg.UpdateOnly = true
|
||||||
|
err := m.wg.ConfigureDevice(m.Cfg.DeviceName, wgtypes.Config{Peers: []wgtypes.PeerConfig{cfg}})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not configure WireGuard device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) RemovePeer(pubKey string) error {
|
func (m *Manager) RemovePeer(pubKey string) error {
|
||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
defer m.mux.Unlock()
|
defer m.mux.Unlock()
|
||||||
|
@ -101,3 +114,7 @@ func (m *Manager) RemovePeer(pubKey string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) UpdateDevice(name string, cfg wgtypes.Config) error {
|
||||||
|
return m.wg.ConfigureDevice(name, cfg)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue