diff --git a/handler/routes.go b/handler/routes.go index d4e0ffb..b4dbdd4 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -389,3 +389,66 @@ func SuggestIPAllocation() echo.HandlerFunc { return c.JSON(http.StatusOK, suggestedIPs) } } + +// ApplyServerConfig handler to write config file and restart Wireguard server +func ApplyServerConfig() 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) + } + + serverKeyPair := model.ServerKeypair{} + if err := db.Read("server", "keypair", &serverKeyPair); err != nil { + log.Error("Cannot fetch server key pair from database: ", err) + } + + server := model.Server{} + server.Interface = &serverInterface + server.KeyPair = &serverKeyPair + + // read global settings + globalSettings := model.GlobalSetting{} + if err := db.Read("server", "global_settings", &globalSettings); err != nil { + log.Error("Cannot fetch global settings from database: ", err) + } + + // 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{} + for _, f := range records { + client := model.Client{} + clientData := model.ClientData{} + + // get client info + if err := json.Unmarshal([]byte(f), &client); err != nil { + log.Error("Cannot decode client json structure: ", err) + } + clientData.Client = &client + + // create the list of clients and their qrcode data + clientDataList = append(clientDataList, clientData) + } + + // Write config file + err = util.WriteWireGuardServerConfig(server, clientDataList, globalSettings) + if err != nil { + log.Error("Cannot apply server config: ", err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, fmt.Sprintf("Cannot apply server config: %v", err)}) + } + + return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Applied server config successfully"}) + } +} diff --git a/main.go b/main.go index bcd4056..5adb242 100644 --- a/main.go +++ b/main.go @@ -19,5 +19,6 @@ func main() { app.POST("/global-settings", handler.GlobalSettingSubmit()) app.GET("/api/machine-ips", handler.MachineIPAddresses()) app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation()) + app.GET("/api/apply-wg-config", handler.ApplyServerConfig()) app.Logger.Fatal(app.Start("127.0.0.1:5000")) } diff --git a/model/setting.go b/model/setting.go index 4d8791b..a316172 100644 --- a/model/setting.go +++ b/model/setting.go @@ -10,5 +10,6 @@ type GlobalSetting struct { DNSServers []string `json:"dns_servers"` MTU int `json:"mtu,string"` PersistentKeepalive int `json:"persistent_keepalive,string"` + ConfigFilePath string `json:"config_file_path"` UpdatedAt time.Time `json:"updated_at"` } diff --git a/templates/base.html b/templates/base.html index 28d1705..99322bd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -58,9 +58,12 @@ + + +
@@ -352,6 +378,27 @@ updateIPAllocationSuggestion(); }); }); + + // apply_config_confirm button event + $(document).ready(function () { + $('#apply_config_confirm').click(function () { + $.ajax({ + cache: false, + method: 'GET', + url: '/api/apply-wg-config', + dataType: 'json', + contentType: "application/json", + success: function(data) { + $('#modal_apply_config').modal('hide'); + toastr.success('Applied config successfully'); + }, + error: function(jqXHR, exception) { + var responseJson = jQuery.parseJSON(jqXHR.responseText); + toastr.error(responseJson['message']); + } + }); + }); + }); diff --git a/templates/global_settings.html b/templates/global_settings.html index 59a13c1..e54bc22 100644 --- a/templates/global_settings.html +++ b/templates/global_settings.html @@ -55,6 +55,12 @@ Global Settings name="persistent_keepalive" placeholder="Persistent Keepalive" value="{{ .globalSettings.PersistentKeepalive }}">
+
+ + +
@@ -104,7 +110,8 @@ Global Settings var dns_servers = $("#dns_servers").val().split(","); var mtu = $("#mtu").val(); var persistent_keepalive = $("#persistent_keepalive").val(); - var data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive}; + var config_file_path = $("#config_file_path").val(); + var data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive, "config_file_path": config_file_path}; $.ajax({ cache: false, @@ -173,6 +180,9 @@ Global Settings persistent_keepalive: { required: true, digits: true + }, + config_file_path: { + required: true } }, messages: { @@ -184,6 +194,9 @@ Global Settings persistent_keepalive: { required: "Please enter a Persistent Keepalive value", digits: "Persistent keepalive must be an integer" + }, + config_file_path: { + required: "Please enter WireGuard config file path" } }, errorElement: 'span', diff --git a/templates/wg.conf b/templates/wg.conf new file mode 100644 index 0000000..2bb3953 --- /dev/null +++ b/templates/wg.conf @@ -0,0 +1,21 @@ +# This file was generated using wireguard-ui (https://github.com/ngoduykhanh/wireguard-ui) +# Please don't modify it manually, otherwise your change might got replaced. + +# Address updated at: {{ .serverConfig.Interface.UpdatedAt }} +# Private Key updated at: {{ .serverConfig.KeyPair.UpdatedAt }} +[Interface] +Address = {{$first :=true}}{{range .serverConfig.Interface.Addresses }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}} +ListenPort = {{ .serverConfig.Interface.ListenPort }} +PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }} +MTU = {{ .globalSettings.MTU }} + +{{range .clientDataList}} +# ID: {{ .Client.ID }} +# Name: {{ .Client.Name }} +# Email: {{ .Client.Email }} +# Created at: {{ .Client.CreatedAt }} +# Update at: {{ .Client.UpdatedAt }} +[Peer] +PublicKey = {{ .Client.PublicKey }} +AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}} +{{end}} diff --git a/util/util.go b/util/util.go index e57d3bc..507db29 100644 --- a/util/util.go +++ b/util/util.go @@ -5,7 +5,9 @@ import ( "errors" "fmt" "net" + "os" "strings" + "text/template" "time" externalip "github.com/glendc/go-external-ip" @@ -308,3 +310,30 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip return true, nil } + +// WriteWireGuardServerConfig to write Wireguard server config. e.g. wg0.conf +func WriteWireGuardServerConfig(serverConfig model.Server, clientDataList []model.ClientData, globalSettings model.GlobalSetting) error { + t, err := template.ParseFiles("templates/wg.conf") + if err != nil { + return err + } + + f, err := os.Create(globalSettings.ConfigFilePath) + if err != nil { + return err + } + + config := map[string]interface{}{ + "serverConfig": serverConfig, + "clientDataList": clientDataList, + "globalSettings": globalSettings, + } + + err = t.Execute(f, config) + if err != nil { + return err + } + f.Close() + + return nil +}