Add Server config page
Handle server ip addresses input and store TODO: Key pair form
This commit is contained in:
		
							parent
							
								
									20fcdbafa5
								
							
						
					
					
						commit
						febf075f8d
					
				|  | @ -1,4 +0,0 @@ | ||||||
| { |  | ||||||
| 	"privateKey": "xyz", |  | ||||||
| 	"pulbicKey": "123" |  | ||||||
| } |  | ||||||
|  | @ -16,8 +16,8 @@ import ( | ||||||
| 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Home handler
 | // WireGuardClients handler
 | ||||||
| func Home() echo.HandlerFunc { | func WireGuardClients() echo.HandlerFunc { | ||||||
| 	return func(c echo.Context) error { | 	return func(c echo.Context) error { | ||||||
| 		// initialize database directory
 | 		// initialize database directory
 | ||||||
| 		dir := "./db" | 		dir := "./db" | ||||||
|  | @ -53,7 +53,7 @@ func Home() echo.HandlerFunc { | ||||||
| 			clientDataList = append(clientDataList, clientData) | 			clientDataList = append(clientDataList, clientData) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return c.Render(http.StatusOK, "home.html", map[string]interface{}{ | 		return c.Render(http.StatusOK, "clients.html", map[string]interface{}{ | ||||||
| 			"name":           "Khanh", | 			"name":           "Khanh", | ||||||
| 			"clientDataList": clientDataList, | 			"clientDataList": clientDataList, | ||||||
| 		}) | 		}) | ||||||
|  | @ -68,7 +68,7 @@ func NewClient() echo.HandlerFunc { | ||||||
| 
 | 
 | ||||||
| 		// validate the input AllowedIPs
 | 		// validate the input AllowedIPs
 | ||||||
| 		if util.ValidateAllowedIPs(client.AllowedIPs) == false { | 		if util.ValidateAllowedIPs(client.AllowedIPs) == false { | ||||||
| 			log.Warn("Invalid Allowed IPs input from user: %v", client.AllowedIPs) | 			log.Warnf("Invalid Allowed IPs input from user: %v", client.AllowedIPs) | ||||||
| 			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"}) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -122,3 +122,54 @@ func RemoveClient() echo.HandlerFunc { | ||||||
| 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Client removed"}) | 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Client removed"}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // WireGuardServer handler
 | ||||||
|  | func WireGuardServer() 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) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		serverInterface := model.ServerInterface{} | ||||||
|  | 		if err := db.Read("server", "interfaces", &serverInterface); err != nil { | ||||||
|  | 			log.Error("Cannot fetch server interface config from database: ", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return c.Render(http.StatusOK, "server.html", map[string]interface{}{ | ||||||
|  | 			"name":            "Khanh", | ||||||
|  | 			"serverInterface": serverInterface, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // WireGuardServerInterfaces handler
 | ||||||
|  | func WireGuardServerInterfaces() echo.HandlerFunc { | ||||||
|  | 	return func(c echo.Context) error { | ||||||
|  | 		serverInterface := new(model.ServerInterface) | ||||||
|  | 		c.Bind(serverInterface) | ||||||
|  | 
 | ||||||
|  | 		// validate the input addresses
 | ||||||
|  | 		if util.ValidateServerAddresses(serverInterface.Addresses) == false { | ||||||
|  | 			log.Warnf("Invalid server interface addresses input from user: %v", serverInterface.Addresses) | ||||||
|  | 			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Interface IP addresses must be in CIDR format"}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		serverInterface.UpdatedAt = time.Now().UTC() | ||||||
|  | 
 | ||||||
|  | 		// write config to the database
 | ||||||
|  | 		dir := "./db" | ||||||
|  | 		db, err := scribble.New(dir, nil) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Error("Cannot initialize the database: ", err) | ||||||
|  | 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"}) | ||||||
|  | 		} | ||||||
|  | 		db.Write("server", "interfaces", serverInterface) | ||||||
|  | 		log.Infof("Updated wireguard server interfaces settings: %v", serverInterface) | ||||||
|  | 
 | ||||||
|  | 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated interface addresses successfully"}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								main.go
								
								
								
								
							
							
						
						
									
										5
									
								
								main.go
								
								
								
								
							|  | @ -8,9 +8,10 @@ import ( | ||||||
| func main() { | func main() { | ||||||
| 	app := router.New() | 	app := router.New() | ||||||
| 
 | 
 | ||||||
| 	app.GET("/", handler.Home()) | 	app.GET("/", handler.WireGuardClients()) | ||||||
| 	app.POST("/new-client", handler.NewClient()) | 	app.POST("/new-client", handler.NewClient()) | ||||||
| 	app.POST("/remove-client", handler.RemoveClient()) | 	app.POST("/remove-client", handler.RemoveClient()) | ||||||
| 
 | 	app.GET("/wg-server", handler.WireGuardServer()) | ||||||
|  | 	app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces()) | ||||||
| 	app.Logger.Fatal(app.Start("127.0.0.1:5000")) | 	app.Logger.Fatal(app.Start("127.0.0.1:5000")) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | package model | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Server model
 | ||||||
|  | type Server struct { | ||||||
|  | 	KeyPair   *ServerKeypair | ||||||
|  | 	Interface *ServerInterface | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ServerKeypair model
 | ||||||
|  | type ServerKeypair struct { | ||||||
|  | 	PrivateKey string    `json:"private_key"` | ||||||
|  | 	PublicKey  string    `json:"pulbic_key"` | ||||||
|  | 	UpdatedAt  time.Time `json:"updated_at"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ServerInterface model
 | ||||||
|  | type ServerInterface struct { | ||||||
|  | 	Addresses  []string  `json:"addresses"` | ||||||
|  | 	ListenPort int       `json:"listen_port,string"` // ,string to get listen_port string input as int
 | ||||||
|  | 	UpdatedAt  time.Time `json:"updated_at"` | ||||||
|  | } | ||||||
|  | @ -29,7 +29,8 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c | ||||||
| func New() *echo.Echo { | func New() *echo.Echo { | ||||||
| 	e := echo.New() | 	e := echo.New() | ||||||
| 	templates := make(map[string]*template.Template) | 	templates := make(map[string]*template.Template) | ||||||
| 	templates["home.html"] = template.Must(template.ParseFiles("templates/home.html", "templates/base.html")) | 	templates["clients.html"] = template.Must(template.ParseFiles("templates/clients.html", "templates/base.html")) | ||||||
|  | 	templates["server.html"] = template.Must(template.ParseFiles("templates/server.html", "templates/base.html")) | ||||||
| 
 | 
 | ||||||
| 	e.Logger.SetLevel(log.DEBUG) | 	e.Logger.SetLevel(log.DEBUG) | ||||||
| 	e.Pre(middleware.RemoveTrailingSlash()) | 	e.Pre(middleware.RemoveTrailingSlash()) | ||||||
|  |  | ||||||
|  | @ -86,18 +86,26 @@ | ||||||
|                 <nav class="mt-2"> |                 <nav class="mt-2"> | ||||||
|                     <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false"> |                     <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false"> | ||||||
|                         <li class="nav-item"> |                         <li class="nav-item"> | ||||||
|                             <a href="#" class="nav-link active"> |                             <a href="/" class="nav-link active"> | ||||||
|                                 <i class="nav-icon fas fa-th"></i> |                                 <i class="nav-icon fas fa-user-secret"></i> | ||||||
|                                 <p> |                                 <p> | ||||||
|                                     Dashboard |                                     Wireguard Clients | ||||||
|                                 </p> |                                 </p> | ||||||
|                             </a> |                             </a> | ||||||
|                         </li> |                         </li> | ||||||
|                         <li class="nav-item"> |                         <li class="nav-item"> | ||||||
|                             <a href="#" class="nav-link"> |                             <a href="/wg-server" class="nav-link"> | ||||||
|                                 <i class="nav-icon fas fa-server"></i> |                                 <i class="nav-icon fas fa-server"></i> | ||||||
|                                 <p> |                                 <p> | ||||||
|                                     Server Config |                                     Wireguard Server | ||||||
|  |                                 </p> | ||||||
|  |                             </a> | ||||||
|  |                         </li> | ||||||
|  |                         <li class="nav-item"> | ||||||
|  |                             <a href="/golbal-settings" class="nav-link"> | ||||||
|  |                                 <i class="nav-icon fas fa-cog"></i> | ||||||
|  |                                 <p> | ||||||
|  |                                     Global Settings | ||||||
|                                 </p> |                                 </p> | ||||||
|                             </a> |                             </a> | ||||||
|                         </li> |                         </li> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| {{define "title"}} | {{define "title"}} | ||||||
| Home | Wireguard Clients | ||||||
| {{end}} | {{end}} | ||||||
| 
 | 
 | ||||||
| {{define "username"}} | {{define "username"}} | ||||||
|  | @ -7,13 +7,13 @@ Home | ||||||
| {{end}} | {{end}} | ||||||
| 
 | 
 | ||||||
| {{define "page_title"}} | {{define "page_title"}} | ||||||
| Dashboard | Wireguard Clients | ||||||
| {{end}} | {{end}} | ||||||
| 
 | 
 | ||||||
| {{define "page_content"}} | {{define "page_content"}} | ||||||
| <section class="content"> | <section class="content"> | ||||||
|     <div class="container-fluid"> |     <div class="container-fluid"> | ||||||
|         <h5 class="mt-4 mb-2">Wireguard Clients</h5> |         <!-- <h5 class="mt-4 mb-2">Wireguard Clients</h5> --> | ||||||
|         <div class="row"> |         <div class="row"> | ||||||
|             {{range .clientDataList}} |             {{range .clientDataList}} | ||||||
|             <div class="col-sm-6"> |             <div class="col-sm-6"> | ||||||
|  | @ -0,0 +1,165 @@ | ||||||
|  | {{define "title"}} | ||||||
|  | Wireguard Server | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "username"}} | ||||||
|  | {{index . "name"}} | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "page_title"}} | ||||||
|  | Wireguard Server Settings | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "page_content"}} | ||||||
|  | <section class="content"> | ||||||
|  |     <div class="container-fluid"> | ||||||
|  |         <!-- <h5 class="mt-4 mb-2">Wireguard Server</h5> --> | ||||||
|  |         <div class="row"> | ||||||
|  |             <!-- left column --> | ||||||
|  |             <div class="col-md-6"> | ||||||
|  |                 <div class="card card-success"> | ||||||
|  |                     <div class="card-header"> | ||||||
|  |                         <h3 class="card-title">Interfaces</h3> | ||||||
|  |                     </div> | ||||||
|  |                     <!-- /.card-header --> | ||||||
|  |                     <!-- form start --> | ||||||
|  |                     <form role="form" id="frm_server_interface" name="frm_server_interface"> | ||||||
|  |                         <div class="card-body"> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="addresses" class="control-label">Server Interface Addresses</label> | ||||||
|  |                                 <input type="text" data-role="tagsinput" class="form-control" id="addresses" value=""> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="listen_port">Listen Port</label> | ||||||
|  |                                 <input type="text" class="form-control" id="listen_port" name="listen_port" | ||||||
|  |                                     placeholder="Listen Port" value="{{ .serverInterface.ListenPort }}"> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                         <!-- /.card-body --> | ||||||
|  | 
 | ||||||
|  |                         <div class="card-footer"> | ||||||
|  |                             <button type="submit" class="btn btn-success">Save</button> | ||||||
|  |                         </div> | ||||||
|  |                     </form> | ||||||
|  |                 </div> | ||||||
|  |                 <!-- /.card --> | ||||||
|  |             </div> | ||||||
|  |             <!-- right column --> | ||||||
|  |             <div class="col-md-6"> | ||||||
|  |                 <div class="card card-danger"> | ||||||
|  |                     <div class="card-header"> | ||||||
|  |                         <h3 class="card-title">Key Pair</h3> | ||||||
|  |                     </div> | ||||||
|  |                     <!-- /.card-header --> | ||||||
|  |                     <!-- form start --> | ||||||
|  |                     <form role="form"> | ||||||
|  |                         <div class="card-body"> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="private_key">Private Key</label> | ||||||
|  |                                 <div class="input-group input-group"> | ||||||
|  |                                     <input type="text" class="form-control" id="private_key" placeholder="Private Key"> | ||||||
|  |                                     <span class="input-group-append"> | ||||||
|  |                                         <button type="button" class="btn btn-danger btn-flat">Show</button> | ||||||
|  |                                     </span> | ||||||
|  |                                 </div> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="form-group"> | ||||||
|  |                                 <label for="public_key">Public Key</label> | ||||||
|  |                                 <input type="text" class="form-control" id="public_key" placeholder="Public Key"> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                         <!-- /.card-body --> | ||||||
|  | 
 | ||||||
|  |                         <div class="card-footer"> | ||||||
|  |                             <button type="submit" class="btn btn-danger">Regenerate</button> | ||||||
|  |                         </div> | ||||||
|  |                     </form> | ||||||
|  |                 </div> | ||||||
|  |                 <!-- /.card --> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <!-- /.row --> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "bottom_js"}} | ||||||
|  |     <script> | ||||||
|  |         function submitServerInterfaceSetting() { | ||||||
|  |             var addresses = $("#addresses").val().split(","); | ||||||
|  |             var listen_port = $("#listen_port").val(); | ||||||
|  |             var data = {"addresses": addresses, "listen_port": listen_port}; | ||||||
|  | 
 | ||||||
|  |             $.ajax({ | ||||||
|  |                 cache: false, | ||||||
|  |                 method: 'POST', | ||||||
|  |                 url: '/wg-server/interfaces', | ||||||
|  |                 dataType: 'json', | ||||||
|  |                 contentType: "application/json", | ||||||
|  |                 data: JSON.stringify(data), | ||||||
|  |                 success: function(data) { | ||||||
|  |                     $('#modal_new_client').modal('hide'); | ||||||
|  |                     toastr.success('Updated Wireguard server interface addresses successfully'); | ||||||
|  |                 }, | ||||||
|  |                 error: function(jqXHR, exception) { | ||||||
|  |                     var responseJson = jQuery.parseJSON(jqXHR.responseText); | ||||||
|  |                     toastr.error(responseJson['message']); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  |     <script> | ||||||
|  |         // Wireguard Interface Addresses tag input | ||||||
|  |         $('#addresses').tagsInput({ | ||||||
|  |             'width': '100%', | ||||||
|  |             // 'height': '75%', | ||||||
|  |             'interactive': true, | ||||||
|  |             'defaultText': 'Add More', | ||||||
|  |             'removeWithBackspace': true, | ||||||
|  |             'minChars': 0, | ||||||
|  |             'maxChars': 18, | ||||||
|  |             'placeholderColor': '#666666' | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Load server addresses to the form | ||||||
|  |         {{range .serverInterface.Addresses}} | ||||||
|  |         $('#addresses').addTag('{{.}}'); | ||||||
|  |         {{end}} | ||||||
|  | 
 | ||||||
|  |         // Wireguard Interface Addresses form validation | ||||||
|  |         $(document).ready(function () { | ||||||
|  |             $.validator.setDefaults({ | ||||||
|  |                 submitHandler: function () { | ||||||
|  |                     submitServerInterfaceSetting(); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             $('#frm_server_interface').validate({ | ||||||
|  |                 rules: { | ||||||
|  |                     listen_port: { | ||||||
|  |                         required: true, | ||||||
|  |                         digits: true, | ||||||
|  |                         range: [1, 65535] | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 messages: { | ||||||
|  |                     listen_port: { | ||||||
|  |                         required: "Please enter a port", | ||||||
|  |                         digits: "Port must be an integer", | ||||||
|  |                         range: "Port must be in range 1..65535" | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 errorElement: 'span', | ||||||
|  |                 errorPlacement: function (error, element) { | ||||||
|  |                     error.addClass('invalid-feedback'); | ||||||
|  |                     element.closest('.form-group').append(error); | ||||||
|  |                 }, | ||||||
|  |                 highlight: function (element, errorClass, validClass) { | ||||||
|  |                     $(element).addClass('is-invalid'); | ||||||
|  |                 }, | ||||||
|  |                 unhighlight: function (element, errorClass, validClass) { | ||||||
|  |                     $(element).removeClass('is-invalid'); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     </script> | ||||||
|  | {{end}} | ||||||
							
								
								
									
										22
									
								
								util/util.go
								
								
								
								
							
							
						
						
									
										22
									
								
								util/util.go
								
								
								
								
							|  | @ -40,7 +40,7 @@ func BuildClientConfig(client model.Client) string { | ||||||
| 	return strConfig | 	return strConfig | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ValidateCIDR to validate an network CIDR
 | // ValidateCIDR to validate a network CIDR
 | ||||||
| func ValidateCIDR(cidr string) bool { | func ValidateCIDR(cidr string) bool { | ||||||
| 	_, _, err := net.ParseCIDR(cidr) | 	_, _, err := net.ParseCIDR(cidr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -49,8 +49,8 @@ func ValidateCIDR(cidr string) bool { | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ValidateAllowedIPs to validate allowed ip addresses in CIDR format.
 | // ValidateCIDRList to validate a list of network CIDR
 | ||||||
| func ValidateAllowedIPs(cidrs []string) bool { | func ValidateCIDRList(cidrs []string) bool { | ||||||
| 	for _, cidr := range cidrs { | 	for _, cidr := range cidrs { | ||||||
| 		if ValidateCIDR(cidr) == false { | 		if ValidateCIDR(cidr) == false { | ||||||
| 			return false | 			return false | ||||||
|  | @ -58,3 +58,19 @@ func ValidateAllowedIPs(cidrs []string) bool { | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ValidateAllowedIPs to validate allowed ip addresses in CIDR format
 | ||||||
|  | func ValidateAllowedIPs(cidrs []string) bool { | ||||||
|  | 	if ValidateCIDRList(cidrs) == false { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ValidateServerAddresses to validate allowed ip addresses in CIDR format
 | ||||||
|  | func ValidateServerAddresses(cidrs []string) bool { | ||||||
|  | 	if ValidateCIDRList(cidrs) == false { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue