mirror of
https://github.com/ngoduykhanh/wireguard-ui.git
synced 2025-04-19 19:59:13 +03:00
Adjustment in New Client form to have Allocation IP from suggestion API
This commit is contained in:
parent
7aec01deed
commit
15703b9185
4 changed files with 213 additions and 21 deletions
|
@ -3,6 +3,7 @@ package handler
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -26,11 +27,6 @@ func WireGuardClients() echo.HandlerFunc {
|
||||||
log.Error("Cannot initialize the database: ", err)
|
log.Error("Cannot initialize the database: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
records, err := db.ReadAll("clients")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Cannot fetch clients from database: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read server information
|
// read server information
|
||||||
serverInterface := model.ServerInterface{}
|
serverInterface := model.ServerInterface{}
|
||||||
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||||
|
@ -53,6 +49,11 @@ func WireGuardClients() echo.HandlerFunc {
|
||||||
server.KeyPair = &serverKeyPair
|
server.KeyPair = &serverKeyPair
|
||||||
|
|
||||||
// read client information and build a client list
|
// read client information and build a client list
|
||||||
|
records, err := db.ReadAll("clients")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Cannot fetch clients from database: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
clientDataList := []model.ClientData{}
|
clientDataList := []model.ClientData{}
|
||||||
for _, f := range records {
|
for _, f := range records {
|
||||||
client := model.Client{}
|
client := model.Client{}
|
||||||
|
@ -76,7 +77,7 @@ func WireGuardClients() echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "clients.html", map[string]interface{}{
|
return c.Render(http.StatusOK, "clients.html", map[string]interface{}{
|
||||||
"baseData": model.BaseData{""},
|
"baseData": model.BaseData{Active: ""},
|
||||||
"clientDataList": clientDataList,
|
"clientDataList": clientDataList,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -88,6 +89,13 @@ func NewClient() echo.HandlerFunc {
|
||||||
client := new(model.Client)
|
client := new(model.Client)
|
||||||
c.Bind(client)
|
c.Bind(client)
|
||||||
|
|
||||||
|
// validate the input Allocation IPs
|
||||||
|
// TODO: validate if they are really available
|
||||||
|
if util.ValidateCIDRList(client.AllocatedIPs) == false {
|
||||||
|
log.Warnf("Invalid allocation ip input from user: %v", client.AllocatedIPs)
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "IP allocation is not valid"})
|
||||||
|
}
|
||||||
|
|
||||||
// validate the input AllowedIPs
|
// validate the input AllowedIPs
|
||||||
if util.ValidateAllowedIPs(client.AllowedIPs) == false {
|
if util.ValidateAllowedIPs(client.AllowedIPs) == false {
|
||||||
log.Warnf("Invalid Allowed IPs input from user: %v", client.AllowedIPs)
|
log.Warnf("Invalid Allowed IPs input from user: %v", client.AllowedIPs)
|
||||||
|
@ -167,7 +175,7 @@ func WireGuardServer() echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "server.html", map[string]interface{}{
|
return c.Render(http.StatusOK, "server.html", map[string]interface{}{
|
||||||
"baseData": model.BaseData{"wg-server"},
|
"baseData": model.BaseData{Active: "wg-server"},
|
||||||
"serverInterface": serverInterface,
|
"serverInterface": serverInterface,
|
||||||
"serverKeyPair": serverKeyPair,
|
"serverKeyPair": serverKeyPair,
|
||||||
})
|
})
|
||||||
|
@ -247,7 +255,7 @@ func GlobalSettings() echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "global_settings.html", map[string]interface{}{
|
return c.Render(http.StatusOK, "global_settings.html", map[string]interface{}{
|
||||||
"baseData": model.BaseData{"global-settings"},
|
"baseData": model.BaseData{Active: "global-settings"},
|
||||||
"globalSettings": globalSettings,
|
"globalSettings": globalSettings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -302,3 +310,41 @@ func MachineIPAddresses() echo.HandlerFunc {
|
||||||
return c.JSON(http.StatusOK, interfaceList)
|
return c.JSON(http.StatusOK, interfaceList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SuggestIPAllocation handler to get the list of ip address for client
|
||||||
|
func SuggestIPAllocation() echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
// initialize database directory
|
||||||
|
dir := "./db"
|
||||||
|
db, err := scribble.New(dir, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Cannot initialize the database: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read server information
|
||||||
|
serverInterface := model.ServerInterface{}
|
||||||
|
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||||
|
log.Error("Cannot fetch server interface config from database: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the list of suggestedIPs
|
||||||
|
// we take the first available ip address from
|
||||||
|
// each server's network addresses.
|
||||||
|
suggestedIPs := make([]string, 0)
|
||||||
|
allocatedIPs, err := util.GetAllocatedIPs()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Cannot suggest ip allocation. Failed to get list of allocated ip addresses: ", err)
|
||||||
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot suggest ip allocation: failed to get list of allocated ip addresses"})
|
||||||
|
}
|
||||||
|
for _, cidr := range serverInterface.Addresses {
|
||||||
|
ip, err := util.GetAvailableIP(cidr, allocatedIPs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to get available ip from a CIDR: ", err)
|
||||||
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr)})
|
||||||
|
}
|
||||||
|
suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/32", ip))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, suggestedIPs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
main.go
1
main.go
|
@ -17,5 +17,6 @@ func main() {
|
||||||
app.GET("/global-settings", handler.GlobalSettings())
|
app.GET("/global-settings", handler.GlobalSettings())
|
||||||
app.POST("/global-settings", handler.GlobalSettingSubmit())
|
app.POST("/global-settings", handler.GlobalSettingSubmit())
|
||||||
app.GET("/api/machine-ips", handler.MachineIPAddresses())
|
app.GET("/api/machine-ips", handler.MachineIPAddresses())
|
||||||
|
app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation())
|
||||||
app.Logger.Fatal(app.Start("127.0.0.1:5000"))
|
app.Logger.Fatal(app.Start("127.0.0.1:5000"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,16 +137,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="client_allocated_ips" class="control-label">IP Allocation</label>
|
<label for="client_allocated_ips" class="control-label">IP Allocation</label>
|
||||||
<select id="client_allocated_ips" class=" select2" data-placeholder="Select an IP address"
|
<input type="text" data-role="tagsinput" class="form-control" id="client_allocated_ips">
|
||||||
style="width: 100%;">
|
|
||||||
<option>192.168.1.1</option>
|
|
||||||
<option>192.168.1.2</option>
|
|
||||||
<option>192.168.1.3</option>
|
|
||||||
<option>192.168.1.4</option>
|
|
||||||
<option>192.168.1.5</option>
|
|
||||||
<option>192.168.1.6</option>
|
|
||||||
<option>192.168.1.7</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="client_allowed_ips" class="control-label">Allowed IPs</label>
|
<label for="client_allowed_ips" class="control-label">Allowed IPs</label>
|
||||||
|
@ -228,10 +219,9 @@
|
||||||
<script>
|
<script>
|
||||||
// submitNewClient function for new client form submition
|
// submitNewClient function for new client form submition
|
||||||
function submitNewClient() {
|
function submitNewClient() {
|
||||||
// TODO: Get allocated_ips and allowed_ips as multiple value in array
|
|
||||||
var name = $("#client_name").val();
|
var name = $("#client_name").val();
|
||||||
var email = $("#client_email").val();
|
var email = $("#client_email").val();
|
||||||
var allocated_ips = $("#client_allocated_ips").select2('val');
|
var allocated_ips = $("#client_allocated_ips").val().split(",");
|
||||||
var allowed_ips = $("#client_allowed_ips").val().split(",");
|
var allowed_ips = $("#client_allowed_ips").val().split(",");
|
||||||
var enabled = false;
|
var enabled = false;
|
||||||
|
|
||||||
|
@ -239,7 +229,7 @@
|
||||||
enabled = true;
|
enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {"name": name, "email": email, "allocated_ips": [allocated_ips], "allowed_ips": allowed_ips,
|
var data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips,
|
||||||
"enabled": enabled};
|
"enabled": enabled};
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
|
@ -261,11 +251,44 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateIPAllocationSuggestion function for automatically fill
|
||||||
|
// the IP Allocation input with suggested ip addresses
|
||||||
|
function updateIPAllocationSuggestion() {
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/suggest-client-ips',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function(data) {
|
||||||
|
data.forEach(function (item, index) {
|
||||||
|
$('#client_allocated_ips').addTag(item);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
error: function(jqXHR, exception) {
|
||||||
|
var responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
|
toastr.error(responseJson['message']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
//Initialize Select2 Elements
|
//Initialize Select2 Elements
|
||||||
$('.select2').select2()
|
$('.select2').select2()
|
||||||
|
|
||||||
|
// IP Allocation tag input
|
||||||
|
$('#client_allocated_ips').tagsInput({
|
||||||
|
'width': '100%',
|
||||||
|
'height': '75%',
|
||||||
|
'interactive': true,
|
||||||
|
'defaultText': 'Add More',
|
||||||
|
'removeWithBackspace': true,
|
||||||
|
'minChars': 0,
|
||||||
|
'maxChars': 18,
|
||||||
|
'placeholderColor': '#666666'
|
||||||
|
});
|
||||||
|
|
||||||
// AllowedIPs tag input
|
// AllowedIPs tag input
|
||||||
$('#client_allowed_ips').tagsInput({
|
$('#client_allowed_ips').tagsInput({
|
||||||
'width': '100%',
|
'width': '100%',
|
||||||
|
@ -317,6 +340,14 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// New Client modal event
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('#modal_new_client').on('shown.bs.modal', function (e) {
|
||||||
|
$('#client_allocated_ips').importTags('');
|
||||||
|
updateIPAllocationSuggestion();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- START: On page script -->
|
<!-- START: On page script -->
|
||||||
|
|
114
util/util.go
114
util/util.go
|
@ -1,6 +1,8 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -8,6 +10,7 @@ import (
|
||||||
|
|
||||||
externalip "github.com/glendc/go-external-ip"
|
externalip "github.com/glendc/go-external-ip"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||||
|
"github.com/sdomino/scribble"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildClientConfig to create wireguard client config string
|
// BuildClientConfig to create wireguard client config string
|
||||||
|
@ -154,3 +157,114 @@ func GetPublicIP() (model.Interface, error) {
|
||||||
|
|
||||||
return publicInterface, err
|
return publicInterface, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIPFromCIDR get ip from CIDR
|
||||||
|
func GetIPFromCIDR(cidr string) (string, error) {
|
||||||
|
ip, _, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllocatedIPs to get all ip addresses allocated to clients and server
|
||||||
|
func GetAllocatedIPs() ([]string, error) {
|
||||||
|
allocatedIPs := make([]string, 0)
|
||||||
|
|
||||||
|
// initialize database directory
|
||||||
|
dir := "./db"
|
||||||
|
db, err := scribble.New(dir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// read server information
|
||||||
|
serverInterface := model.ServerInterface{}
|
||||||
|
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// append server's addresses to the result
|
||||||
|
for _, cidr := range serverInterface.Addresses {
|
||||||
|
ip, err := GetIPFromCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allocatedIPs = append(allocatedIPs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read client information
|
||||||
|
records, err := db.ReadAll("clients")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// append client's addresses to the result
|
||||||
|
for _, f := range records {
|
||||||
|
client := model.Client{}
|
||||||
|
if err := json.Unmarshal([]byte(f), &client); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cidr := range client.AllocatedIPs {
|
||||||
|
ip, err := GetIPFromCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allocatedIPs = append(allocatedIPs, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allocatedIPs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inc from https://play.golang.org/p/m8TNTtygK0
|
||||||
|
func inc(ip net.IP) {
|
||||||
|
for j := len(ip) - 1; j >= 0; j-- {
|
||||||
|
ip[j]++
|
||||||
|
if ip[j] > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBroadcastIP func to get the broadcast ip address of a network
|
||||||
|
func GetBroadcastIP(n *net.IPNet) net.IP {
|
||||||
|
var broadcast net.IP
|
||||||
|
if len(n.IP) == 4 {
|
||||||
|
broadcast = net.ParseIP("0.0.0.0").To4()
|
||||||
|
} else {
|
||||||
|
broadcast = net.ParseIP("::")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(n.IP); i++ {
|
||||||
|
broadcast[i] = n.IP[i] | ^n.Mask[i]
|
||||||
|
}
|
||||||
|
return broadcast
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvailableIP get the ip address that can be allocated from an CIDR
|
||||||
|
func GetAvailableIP(cidr string, allocatedList []string) (string, error) {
|
||||||
|
ip, net, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastAddr := GetBroadcastIP(net).String()
|
||||||
|
networkAddr := net.IP.String()
|
||||||
|
|
||||||
|
for ip := ip.Mask(net.Mask); net.Contains(ip); inc(ip) {
|
||||||
|
available := true
|
||||||
|
suggestedAddr := ip.String()
|
||||||
|
for _, allocatedAddr := range allocatedList {
|
||||||
|
if suggestedAddr == allocatedAddr {
|
||||||
|
available = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if available && suggestedAddr != networkAddr && suggestedAddr != broadcastAddr {
|
||||||
|
return suggestedAddr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No more available ip address")
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue