Merge 24aa2a6cec into b55543f424
				
					
				
			This commit is contained in:
		
						commit
						fa905d5f22
					
				
							
								
								
									
										17
									
								
								README.md
								
								
								
								
							
							
						
						
									
										17
									
								
								README.md
								
								
								
								
							|  | @ -1,4 +1,4 @@ | |||
|  | ||||
|  | ||||
| 
 | ||||
| # wireguard-ui | ||||
| 
 | ||||
|  | @ -53,7 +53,11 @@ docker-compose up | |||
| | `WGUI_TABLE`                | The default WireGuard table value settings                                                                                                                   | `auto`                             | | ||||
| | `WGUI_CONFIG_FILE_PATH`     | The default WireGuard config file path used in global settings                                                                                               | `/etc/wireguard/wg0.conf`          | | ||||
| | `WGUI_LOG_LEVEL`            | The default log level. Possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `OFF`                                                                              | `INFO`                             | | ||||
| | `WG_CONF_TEMPLATE`          | The custom `wg.conf` config file template. Please refer to our [default template](https://github.com/ngoduykhanh/wireguard-ui/blob/master/templates/wg.conf) | N/A                                | | ||||
| | `WGUI_BRAND_TEXT`          | The brand text of the web application                                                                                                                        | `WireGuard UI`                     | | ||||
| | `WGUI_ACCENT_COLOR`        | The color of the interface sidebar                                                                                                                           | `#343a40`                          | | ||||
| | `WGUI_LOGO_FILE_PATH`      | The file path of the website logo                                                                                                                            | Embedded WireGuard logo            | | ||||
| | `WGUI_PAGE_TITLE_PREFIX`   | The HTML title prefix for all pages                                                                                                                          | N/A                                | | ||||
| | `WG_CONF_TEMPLATE`          | The custom `wg.conf` config file template. Please refer to our [default template](https://github.com/idressos/wireguard-ui/blob/master/templates/wg.conf) | N/A                                | | ||||
| | `EMAIL_FROM_ADDRESS`        | The sender email address                                                                                                                                     | N/A                                | | ||||
| | `EMAIL_FROM_NAME`           | The sender name                                                                                                                                              | `WireGuard UI`                     | | ||||
| | `SENDGRID_API_KEY`          | The SendGrid api key                                                                                                                                         | N/A                                | | ||||
|  | @ -63,6 +67,7 @@ docker-compose up | |||
| | `SMTP_PASSWORD`             | The SMTP user password                                                                                                                                       | N/A                                | | ||||
| | `SMTP_AUTH_TYPE`            | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE`                                                                                      | `NONE`                             | | ||||
| | `SMTP_ENCRYPTION`           | the encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS`                                                                           | `STARTTLS`                         | | ||||
| | `HELLO_HOSTNAME`            | Hostname to use for the hello message. smtp-relay.gmail.com needs this set to anything but `localhost`                                                       | `localhost`                        | | ||||
| 
 | ||||
| ### Defaults for server configuration | ||||
| 
 | ||||
|  | @ -181,9 +186,9 @@ rc-update add wgui default | |||
| 
 | ||||
| ### Using Docker | ||||
| 
 | ||||
| Set `WGUI_MANAGE_RESTART=true` to manage Wireguard interface restarts. | ||||
| Using `WGUI_MANAGE_START=true` can also replace the function of `wg-quick@wg0` service, to start Wireguard at boot, by | ||||
| running the container with `restart: unless-stopped`. These settings can also pick up changes to Wireguard Config File | ||||
| Set `WGUI_MANAGE_RESTART=true` to manage WireGuard interface restarts. | ||||
| Using `WGUI_MANAGE_START=true` can also replace the function of `wg-quick@wg0` service, to start WireGuard at boot, by | ||||
| running the container with `restart: unless-stopped`. These settings can also pick up changes to WireGuard Config File | ||||
| Path, after restarting the container. Please make sure you have `--cap-add=NET_ADMIN` in your container config to make | ||||
| this | ||||
| feature work. | ||||
|  | @ -224,7 +229,7 @@ go build -o wireguard-ui | |||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| MIT. See [LICENSE](https://github.com/ngoduykhanh/wireguard-ui/blob/master/LICENSE). | ||||
| MIT. See [LICENSE](https://github.com/idressos/wireguard-ui/blob/master/LICENSE). | ||||
| 
 | ||||
| ## Support | ||||
| 
 | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.6 KiB | 
|  | @ -13,6 +13,7 @@ type SmtpMail struct { | |||
| 	port       int | ||||
| 	username   string | ||||
| 	password   string | ||||
| 	helloHostName string | ||||
| 	authType   mail.AuthType | ||||
| 	encryption mail.Encryption | ||||
| 	noTLSCheck bool | ||||
|  | @ -46,8 +47,8 @@ func encryptionType(encryptionType string) mail.Encryption { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func NewSmtpMail(hostname string, port int, username string, password string, noTLSCheck bool, auth string, fromName, from string, encryption string) *SmtpMail { | ||||
| 	ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: authType(auth), encryption: encryptionType(encryption)} | ||||
| func NewSmtpMail(hostname string, port int, username string, password string, helloHostName string, noTLSCheck bool, auth string, fromName, from string, encryption string) *SmtpMail { | ||||
| 	ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, helloHostName: helloHostName, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: authType(auth), encryption: encryptionType(encryption)} | ||||
| 	return &ans | ||||
| } | ||||
| 
 | ||||
|  | @ -66,6 +67,7 @@ func (o *SmtpMail) Send(toName string, to string, subject string, content string | |||
| 	server.Authentication = o.authType | ||||
| 	server.Username = o.username | ||||
| 	server.Password = o.password | ||||
| 	server.Helo = o.helloHostName | ||||
| 	server.Encryption = o.encryption | ||||
| 	server.KeepAlive = false | ||||
| 	server.ConnectTimeout = 10 * time.Second | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| ### Kernel Module | ||||
| 
 | ||||
| Depending on if the Wireguard kernel module is available on your system you have more or less choices which example to use. | ||||
| Depending on if the WireGuard kernel module is available on your system you have more or less choices which example to use. | ||||
| 
 | ||||
| You can check if the kernel modules are available via the following command: | ||||
| ```shell | ||||
|  | @ -21,10 +21,10 @@ For security reasons it's highly recommended to change them before the first sta | |||
| ## Examples | ||||
| - **[system](system.yml)** | ||||
| 
 | ||||
|   If you have Wireguard already installed on your system and only want to run the UI in docker this might fit the most. | ||||
|   If you have WireGuard already installed on your system and only want to run the UI in docker this might fit the most. | ||||
| - **[linuxserver](linuxserver.yml)** | ||||
| 
 | ||||
|   If you have the Wireguard kernel modules installed (included in the mainline kernel since version 5.6) but want it running inside of docker, this might fit the most. | ||||
|   If you have the WireGuard kernel modules installed (included in the mainline kernel since version 5.6) but want it running inside of docker, this might fit the most. | ||||
| - **[boringtun](boringtun.yml)** | ||||
| 
 | ||||
|   If Wireguard kernel modules are not available, you can switch to an userspace implementation like [boringtun](https://github.com/cloudflare/boringtun). | ||||
|   If WireGuard kernel modules are not available, you can switch to an userspace implementation like [boringtun](https://github.com/cloudflare/boringtun). | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package handler | |||
| 
 | ||||
| import ( | ||||
| 	"crypto/subtle" | ||||
| 	"path/filepath" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | @ -42,6 +43,15 @@ func Favicon() echo.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Logo() echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 		if logo, ok := os.LookupEnv(util.LogoFilePathEnvVar); ok { | ||||
| 			return c.File(logo) | ||||
| 		} | ||||
| 		return c.Redirect(http.StatusFound, util.BasePath+"/static/custom/img/logo.png") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LoginPage handler
 | ||||
| func LoginPage() echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
|  | @ -337,7 +347,7 @@ func WireGuardClients(db store.IStore) echo.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetClients handler return a JSON list of Wireguard client data
 | ||||
| // GetClients handler return a JSON list of WireGuard client data
 | ||||
| func GetClients(db store.IStore) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 
 | ||||
|  | @ -352,7 +362,7 @@ func GetClients(db store.IStore) echo.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetClient handler returns a JSON object of Wireguard client data
 | ||||
| // GetClient handler returns a JSON object of WireGuard client data
 | ||||
| func GetClient(db store.IStore) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 
 | ||||
|  | @ -409,12 +419,12 @@ func NewClient(db store.IStore) echo.HandlerFunc { | |||
| 		guid := xid.New() | ||||
| 		client.ID = guid.String() | ||||
| 
 | ||||
| 		// gen Wireguard key pair
 | ||||
| 		// gen WireGuard key pair
 | ||||
| 		if client.PublicKey == "" { | ||||
| 			key, err := wgtypes.GeneratePrivateKey() | ||||
| 			if err != nil { | ||||
| 				log.Error("Cannot generate wireguard key pair: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) | ||||
| 			} | ||||
| 			client.PrivateKey = key.String() | ||||
| 			client.PublicKey = key.PublicKey().String() | ||||
|  | @ -422,7 +432,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { | |||
| 			_, err := wgtypes.ParseKey(client.PublicKey) | ||||
| 			if err != nil { | ||||
| 				log.Error("Cannot verify wireguard public key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify Wireguard public key"}) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify WireGuard public key"}) | ||||
| 			} | ||||
| 			// check for duplicates
 | ||||
| 			clients, err := db.GetClients(false) | ||||
|  | @ -444,7 +454,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { | |||
| 			if err != nil { | ||||
| 				log.Error("Cannot generated preshared key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ | ||||
| 					false, "Cannot generate Wireguard preshared key", | ||||
| 					false, "Cannot generate WireGuard preshared key", | ||||
| 				}) | ||||
| 			} | ||||
| 			client.PresharedKey = presharedKey.String() | ||||
|  | @ -455,7 +465,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { | |||
| 			_, err := wgtypes.ParseKey(client.PresharedKey) | ||||
| 			if err != nil { | ||||
| 				log.Error("Cannot verify wireguard preshared key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify Wireguard preshared key"}) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify WireGuard preshared key"}) | ||||
| 			} | ||||
| 		} | ||||
| 		client.CreatedAt = time.Now().UTC() | ||||
|  | @ -567,12 +577,12 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { | |||
| 			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"}) | ||||
| 		} | ||||
| 
 | ||||
| 		// update Wireguard Client PublicKey
 | ||||
| 		// update WireGuard Client PublicKey
 | ||||
| 		if client.PublicKey != _client.PublicKey && _client.PublicKey != "" { | ||||
| 			_, err := wgtypes.ParseKey(_client.PublicKey) | ||||
| 			if err != nil { | ||||
| 				log.Error("Cannot verify provided Wireguard public key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided Wireguard public key"}) | ||||
| 				log.Error("Cannot verify provided WireGuard public key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided WireGuard public key"}) | ||||
| 			} | ||||
| 			// check for duplicates
 | ||||
| 			clients, err := db.GetClients(false) | ||||
|  | @ -587,7 +597,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// When replacing any PublicKey, discard any locally stored Wireguard Client PrivateKey
 | ||||
| 			// When replacing any PublicKey, discard any locally stored WireGuard Client PrivateKey
 | ||||
| 			// Client PubKey no longer corresponds to locally stored PrivKey.
 | ||||
| 			// QR code (needs PrivateKey) for this client is no longer possible now.
 | ||||
| 
 | ||||
|  | @ -597,12 +607,12 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { | |||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		// update Wireguard Client PresharedKey
 | ||||
| 		// update WireGuard Client PresharedKey
 | ||||
| 		if client.PresharedKey != _client.PresharedKey && _client.PresharedKey != "" { | ||||
| 			_, err := wgtypes.ParseKey(_client.PresharedKey) | ||||
| 			if err != nil { | ||||
| 				log.Error("Cannot verify provided Wireguard preshared key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided Wireguard preshared key"}) | ||||
| 				log.Error("Cannot verify provided WireGuard preshared key: ", err) | ||||
| 				return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided WireGuard preshared key"}) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -614,6 +624,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { | |||
| 		client.AllocatedIPs = _client.AllocatedIPs | ||||
| 		client.AllowedIPs = _client.AllowedIPs | ||||
| 		client.ExtraAllowedIPs = _client.ExtraAllowedIPs | ||||
| 		client.Endpoint = _client.Endpoint | ||||
| 		client.PublicKey = _client.PublicKey | ||||
| 		client.PresharedKey = _client.PresharedKey | ||||
| 		client.UpdatedAt = time.Now().UTC() | ||||
|  | @ -689,7 +700,7 @@ func DownloadClient(db store.IStore) echo.HandlerFunc { | |||
| 
 | ||||
| 		// set response header for downloading
 | ||||
| 		c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s.conf", clientData.Client.Name)) | ||||
| 		return c.Stream(http.StatusOK, "text/plain", reader) | ||||
| 		return c.Stream(http.StatusOK, "text/conf", reader) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -759,11 +770,11 @@ func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc { | |||
| func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 
 | ||||
| 		// gen Wireguard key pair
 | ||||
| 		// gen WireGuard key pair
 | ||||
| 		key, err := wgtypes.GeneratePrivateKey() | ||||
| 		if err != nil { | ||||
| 			log.Error("Cannot generate wireguard key pair: ", err) | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) | ||||
| 		} | ||||
| 
 | ||||
| 		var serverKeyPair model.ServerKeypair | ||||
|  | @ -772,7 +783,7 @@ func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { | |||
| 		serverKeyPair.UpdatedAt = time.Now().UTC() | ||||
| 
 | ||||
| 		if err := db.SaveServerKeyPair(serverKeyPair); err != nil { | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) | ||||
| 		} | ||||
| 		log.Infof("Updated wireguard server interfaces settings: %v", serverKeyPair) | ||||
| 
 | ||||
|  | @ -796,6 +807,16 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func extractDeviceNameFromConfigPath(db store.IStore) string { | ||||
| 	settings, err := db.GetGlobalSettings() | ||||
| 	if err != nil { | ||||
| 		log.Error("Cannot get global settings: ", err) | ||||
| 	} | ||||
| 
 | ||||
|     base := filepath.Base(settings.ConfigFilePath) | ||||
|     return strings.TrimSuffix(base, filepath.Ext(base)) | ||||
| } | ||||
| 
 | ||||
| // Status handler
 | ||||
| func Status(db store.IStore) echo.HandlerFunc { | ||||
| 	type PeerVM struct { | ||||
|  | @ -826,6 +847,8 @@ func Status(db store.IStore) echo.HandlerFunc { | |||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 		deviceName := extractDeviceNameFromConfigPath(db) | ||||
| 
 | ||||
| 		devices, err := wgClient.Devices() | ||||
| 		if err != nil { | ||||
| 			return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{ | ||||
|  | @ -854,35 +877,37 @@ func Status(db store.IStore) echo.HandlerFunc { | |||
| 
 | ||||
| 			conv := map[bool]int{true: 1, false: 0} | ||||
| 			for i := range devices { | ||||
| 				devVm := DeviceVM{Name: devices[i].Name} | ||||
| 				for j := range devices[i].Peers { | ||||
| 					var allocatedIPs string | ||||
| 					for _, ip := range devices[i].Peers[j].AllowedIPs { | ||||
| 						if len(allocatedIPs) > 0 { | ||||
| 							allocatedIPs += "</br>" | ||||
| 				if devices[i].Name == deviceName { | ||||
| 					devVm := DeviceVM{Name: devices[i].Name} | ||||
| 					for j := range devices[i].Peers { | ||||
| 						var allocatedIPs string | ||||
| 						for _, ip := range devices[i].Peers[j].AllowedIPs { | ||||
| 							if len(allocatedIPs) > 0 { | ||||
| 								allocatedIPs += "</br>" | ||||
| 							} | ||||
| 							allocatedIPs += ip.String() | ||||
| 						} | ||||
| 						allocatedIPs += ip.String() | ||||
| 					} | ||||
| 					pVm := PeerVM{ | ||||
| 						PublicKey:         devices[i].Peers[j].PublicKey.String(), | ||||
| 						ReceivedBytes:     devices[i].Peers[j].ReceiveBytes, | ||||
| 						TransmitBytes:     devices[i].Peers[j].TransmitBytes, | ||||
| 						LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, | ||||
| 						LastHandshakeRel:  time.Since(devices[i].Peers[j].LastHandshakeTime), | ||||
| 						AllocatedIP:       allocatedIPs, | ||||
| 						Endpoint:          devices[i].Peers[j].Endpoint.String(), | ||||
| 					} | ||||
| 					pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. | ||||
| 						pVm := PeerVM{ | ||||
| 							PublicKey:         devices[i].Peers[j].PublicKey.String(), | ||||
| 							ReceivedBytes:     devices[i].Peers[j].ReceiveBytes, | ||||
| 							TransmitBytes:     devices[i].Peers[j].TransmitBytes, | ||||
| 							LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, | ||||
| 							LastHandshakeRel:  time.Since(devices[i].Peers[j].LastHandshakeTime), | ||||
| 							AllocatedIP:       allocatedIPs, | ||||
| 							Endpoint:          devices[i].Peers[j].Endpoint.String(), | ||||
| 						} | ||||
| 						pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. | ||||
| 
 | ||||
| 					if _client, ok := m[pVm.PublicKey]; ok { | ||||
| 						pVm.Name = _client.Name | ||||
| 						pVm.Email = _client.Email | ||||
| 						if _client, ok := m[pVm.PublicKey]; ok { | ||||
| 							pVm.Name = _client.Name | ||||
| 							pVm.Email = _client.Email | ||||
| 						} | ||||
| 						devVm.Peers = append(devVm.Peers, pVm) | ||||
| 					} | ||||
| 					devVm.Peers = append(devVm.Peers, pVm) | ||||
| 					sort.SliceStable(devVm.Peers, func(i, j int) bool { return devVm.Peers[i].Name < devVm.Peers[j].Name }) | ||||
| 					sort.SliceStable(devVm.Peers, func(i, j int) bool { return conv[devVm.Peers[i].Connected] > conv[devVm.Peers[j].Connected] }) | ||||
| 					devicesVm = append(devicesVm, devVm) | ||||
| 				} | ||||
| 				sort.SliceStable(devVm.Peers, func(i, j int) bool { return devVm.Peers[i].Name < devVm.Peers[j].Name }) | ||||
| 				sort.SliceStable(devVm.Peers, func(i, j int) bool { return conv[devVm.Peers[i].Connected] > conv[devVm.Peers[j].Connected] }) | ||||
| 				devicesVm = append(devicesVm, devVm) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -911,7 +936,7 @@ func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc { | |||
| 
 | ||||
| 		// write config to the database
 | ||||
| 		if err := db.SaveGlobalSettings(globalSettings); err != nil { | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) | ||||
| 			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) | ||||
| 		} | ||||
| 
 | ||||
| 		log.Infof("Updated global settings: %v", globalSettings) | ||||
|  | @ -985,7 +1010,7 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ApplyServerConfig handler to write config file and restart Wireguard server
 | ||||
| // ApplyServerConfig handler to write config file and restart WireGuard server
 | ||||
| func ApplyServerConfig(db store.IStore, tmplDir fs.FS) echo.HandlerFunc { | ||||
| 	return func(c echo.Context) error { | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								main.go
								
								
								
								
							
							
						
						
									
										34
									
								
								main.go
								
								
								
								
							|  | @ -30,6 +30,7 @@ var ( | |||
| 	flagBindAddress    string = "0.0.0.0:5000" | ||||
| 	flagSmtpHostname   string = "127.0.0.1" | ||||
| 	flagSmtpPort       int    = 25 | ||||
| 	flagHelloHostName  string = "localhost" | ||||
| 	flagSmtpUsername   string | ||||
| 	flagSmtpPassword   string | ||||
| 	flagSmtpAuthType   string = "NONE" | ||||
|  | @ -41,14 +42,18 @@ var ( | |||
| 	flagSessionSecret  string = util.RandomString(32) | ||||
| 	flagWgConfTemplate string | ||||
| 	flagBasePath       string | ||||
| 	flagBrandText       string = "WireGuard UI" | ||||
| 	flagAccentColor     string = "#343a40" | ||||
| 	flagPageTitlePrefix string | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	defaultEmailSubject = "Your wireguard configuration" | ||||
| 	defaultEmailContent = `Hi,</br> | ||||
| <p>In this email you can find your personal configuration for our wireguard server.</p> | ||||
| 	defaultEmailSubject = "Your VPN configuration" | ||||
| 
 | ||||
| <p>Best</p> | ||||
| 	defaultEmailContent = ` | ||||
| <p>Greetings,</p> | ||||
| <p>Please find attached your personal configuration for our VPN server.<br>You may find instructions on how to install the WireGuard VPN client <a href="https://www.wireguard.com/install/">here</a>.</p> | ||||
| <p>Best regards.</p> | ||||
| ` | ||||
| ) | ||||
| 
 | ||||
|  | @ -69,6 +74,7 @@ func init() { | |||
| 	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(&flagHelloHostName, "hello-hostname", util.LookupEnvOrString("HELLO_HOSTNAME", flagHelloHostName), "Hello HostName") | ||||
| 	flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Username") | ||||
| 	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.") | ||||
|  | @ -80,6 +86,10 @@ func init() { | |||
| 	flag.StringVar(&flagSessionSecret, "session-secret", util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret), "The key used to encrypt session cookies.") | ||||
| 	flag.StringVar(&flagWgConfTemplate, "wg-conf-template", util.LookupEnvOrString("WG_CONF_TEMPLATE", flagWgConfTemplate), "Path to custom wg.conf template.") | ||||
| 	flag.StringVar(&flagBasePath, "base-path", util.LookupEnvOrString("BASE_PATH", flagBasePath), "The base path of the URL") | ||||
| 	flag.StringVar(&flagBrandText, "brand-text", util.LookupEnvOrString("WGUI_BRAND_TEXT", flagBrandText), "The UI brand text or name") | ||||
| 	flag.StringVar(&flagAccentColor, "accent-color", util.LookupEnvOrString("WGUI_ACCENT_COLOR", flagAccentColor), "The UI accent color") | ||||
| 	flag.StringVar(&flagPageTitlePrefix, "page-title-prefix", util.LookupEnvOrString("WGUI_PAGE_TITLE_PREFIX", flagPageTitlePrefix), "The prefix of the page title") | ||||
| 
 | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	// update runtime config
 | ||||
|  | @ -87,6 +97,7 @@ func init() { | |||
| 	util.BindAddress = flagBindAddress | ||||
| 	util.SmtpHostname = flagSmtpHostname | ||||
| 	util.SmtpPort = flagSmtpPort | ||||
| 	util.HelloHostName = flagHelloHostName | ||||
| 	util.SmtpUsername = flagSmtpUsername | ||||
| 	util.SmtpPassword = flagSmtpPassword | ||||
| 	util.SmtpAuthType = flagSmtpAuthType | ||||
|  | @ -98,16 +109,19 @@ func init() { | |||
| 	util.SessionSecret = []byte(flagSessionSecret) | ||||
| 	util.WgConfTemplate = flagWgConfTemplate | ||||
| 	util.BasePath = util.ParseBasePath(flagBasePath) | ||||
| 
 | ||||
| 	util.BrandText = flagBrandText | ||||
| 	util.AccentColor = flagAccentColor | ||||
| 	util.PageTitlePrefix = flagPageTitlePrefix | ||||
| 	 | ||||
| 	// print only if log level is INFO or lower
 | ||||
| 	if lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")); lvl <= log.INFO { | ||||
| 		// print app information
 | ||||
| 		fmt.Println("Wireguard UI") | ||||
| 		fmt.Println("WireGuard UI") | ||||
| 		fmt.Println("App Version\t:", appVersion) | ||||
| 		fmt.Println("Git Commit\t:", gitCommit) | ||||
| 		fmt.Println("Git Ref\t\t:", gitRef) | ||||
| 		fmt.Println("Build Time\t:", buildTime) | ||||
| 		fmt.Println("Git Repo\t:", "https://github.com/ngoduykhanh/wireguard-ui") | ||||
| 		fmt.Println("Git Repo\t:", "https://github.com/idressos/wireguard-ui") | ||||
| 		fmt.Println("Authentication\t:", !util.DisableLogin) | ||||
| 		fmt.Println("Bind address\t:", util.BindAddress) | ||||
| 		//fmt.Println("Sendgrid key\t:", util.SendgridApiKey)
 | ||||
|  | @ -133,6 +147,9 @@ func main() { | |||
| 	extraData["gitCommit"] = gitCommit | ||||
| 	extraData["basePath"] = util.BasePath | ||||
| 	extraData["loginDisabled"] = flagDisableLogin | ||||
| 	extraData["brandText"] = flagBrandText; | ||||
| 	extraData["accentColor"] = flagAccentColor; | ||||
| 	extraData["pageTitlePrefix"] = flagPageTitlePrefix; | ||||
| 
 | ||||
| 	// strip the "templates/" prefix from the embedded directory so files can be read by their direct name (e.g.
 | ||||
| 	// "base.html" instead of "templates/base.html")
 | ||||
|  | @ -163,13 +180,14 @@ func main() { | |||
| 	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, util.SmtpEncryption) | ||||
| 		sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.HelloHostName, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption) | ||||
| 	} | ||||
| 
 | ||||
| 	app.GET(util.BasePath+"/test-hash", handler.GetHashesChanges(db), handler.ValidSession) | ||||
| 	app.GET(util.BasePath+"/about", handler.AboutPage()) | ||||
| 	app.GET(util.BasePath+"/_health", handler.Health()) | ||||
| 	app.GET(util.BasePath+"/favicon", handler.Favicon()) | ||||
| 	app.GET(util.BasePath+"/logo", handler.Logo()) | ||||
| 	app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||
| 	app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||
| 	app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ type Client struct { | |||
| 	AllocatedIPs    []string  `json:"allocated_ips"` | ||||
| 	AllowedIPs      []string  `json:"allowed_ips"` | ||||
| 	ExtraAllowedIPs []string  `json:"extra_allowed_ips"` | ||||
| 	Endpoint        string    `json:"endpoint"` | ||||
| 	UseServerDNS    bool      `json:"use_server_dns"` | ||||
| 	Enabled         bool      `json:"enabled"` | ||||
| 	CreatedAt       time.Time `json:"created_at"` | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "wireguard-ui", | ||||
|   "version": "1.0.0", | ||||
|   "description": "Wireguard web interface", | ||||
|   "description": "WireGuard web interface", | ||||
|   "main": "index.js", | ||||
|   "repository": "git@github.com:ngoduykhanh/wireguard-ui.git", | ||||
|   "author": "Khanh Ngo <k@ndk.name>", | ||||
|  |  | |||
|  | @ -68,7 +68,10 @@ func (o *JsonDB) Init() error { | |||
| 		serverInterface.PostDown = util.LookupEnvOrString(util.ServerPostDownScriptEnvVar, "") | ||||
| 		serverInterface.UpdatedAt = time.Now().UTC() | ||||
| 		o.conn.Write("server", "interfaces", serverInterface) | ||||
| 		os.Chmod(serverInterfacePath, 0600) | ||||
| 		err := util.ManagePerms(serverInterfacePath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// server's key pair
 | ||||
|  | @ -83,7 +86,10 @@ func (o *JsonDB) Init() error { | |||
| 		serverKeyPair.PublicKey = key.PublicKey().String() | ||||
| 		serverKeyPair.UpdatedAt = time.Now().UTC() | ||||
| 		o.conn.Write("server", "keypair", serverKeyPair) | ||||
| 		os.Chmod(serverKeyPairPath, 0600) | ||||
| 		err = util.ManagePerms(serverKeyPairPath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// global settings
 | ||||
|  | @ -108,7 +114,10 @@ func (o *JsonDB) Init() error { | |||
| 		globalSetting.ConfigFilePath = util.LookupEnvOrString(util.ConfigFilePathEnvVar, util.DefaultConfigFilePath) | ||||
| 		globalSetting.UpdatedAt = time.Now().UTC() | ||||
| 		o.conn.Write("server", "global_settings", globalSetting) | ||||
| 		os.Chmod(globalSettingPath, 0600) | ||||
| 		err := util.ManagePerms(globalSettingPath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// hashes
 | ||||
|  | @ -117,7 +126,10 @@ func (o *JsonDB) Init() error { | |||
| 		clientServerHashes.Client = "none" | ||||
| 		clientServerHashes.Server = "none" | ||||
| 		o.conn.Write("server", "hashes", clientServerHashes) | ||||
| 		os.Chmod(hashesPath, 0600) | ||||
| 		err := util.ManagePerms(hashesPath) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// user info
 | ||||
|  | @ -136,7 +148,10 @@ func (o *JsonDB) Init() error { | |||
| 			user.PasswordHash = hash | ||||
| 		} | ||||
| 		o.conn.Write("users", user.Username, user) | ||||
| 		os.Chmod(path.Join(path.Join(o.dbPath, "users"), user.Username+".json"), 0600) | ||||
| 		err = util.ManagePerms(path.Join(path.Join(o.dbPath, "users"), user.Username+".json")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
|  | @ -182,7 +197,10 @@ func (o *JsonDB) GetUserByName(username string) (model.User, error) { | |||
| func (o *JsonDB) SaveUser(user model.User) error { | ||||
| 	userPath := path.Join(path.Join(o.dbPath, "users"), user.Username+".json") | ||||
| 	output := o.conn.Write("users", user.Username, user) | ||||
| 	os.Chmod(userPath, 0600) | ||||
| 	err := util.ManagePerms(userPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
|  | @ -295,7 +313,10 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti | |||
| func (o *JsonDB) SaveClient(client model.Client) error { | ||||
| 	clientPath := path.Join(path.Join(o.dbPath, "clients"), client.ID+".json") | ||||
| 	output := o.conn.Write("clients", client.ID, client) | ||||
| 	os.Chmod(clientPath, 0600) | ||||
| 	err := util.ManagePerms(clientPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
|  | @ -306,21 +327,30 @@ func (o *JsonDB) DeleteClient(clientID string) error { | |||
| func (o *JsonDB) SaveServerInterface(serverInterface model.ServerInterface) error { | ||||
| 	serverInterfacePath := path.Join(path.Join(o.dbPath, "server"), "interfaces.json") | ||||
| 	output := o.conn.Write("server", "interfaces", serverInterface) | ||||
| 	os.Chmod(serverInterfacePath, 0600) | ||||
| 	err := util.ManagePerms(serverInterfacePath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
| func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error { | ||||
| 	serverKeyPairPath := path.Join(path.Join(o.dbPath, "server"), "keypair.json") | ||||
| 	output := o.conn.Write("server", "keypair", serverKeyPair) | ||||
| 	os.Chmod(serverKeyPairPath, 0600) | ||||
| 	err := util.ManagePerms(serverKeyPairPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
| func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error { | ||||
| 	globalSettingsPath := path.Join(path.Join(o.dbPath, "server"), "global_settings.json") | ||||
| 	output := o.conn.Write("server", "global_settings", globalSettings) | ||||
| 	os.Chmod(globalSettingsPath, 0600) | ||||
| 	err := util.ManagePerms(globalSettingsPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
|  | @ -336,6 +366,9 @@ func (o *JsonDB) GetHashes() (model.ClientServerHashes, error) { | |||
| func (o *JsonDB) SaveHashes(hashes model.ClientServerHashes) error { | ||||
| 	hashesPath := path.Join(path.Join(o.dbPath, "server"), "hashes.json") | ||||
| 	output := o.conn.Write("server", "hashes", hashes) | ||||
| 	os.Chmod(hashesPath, 0600) | ||||
| 	err := util.ManagePerms(hashesPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return output | ||||
| } | ||||
| } | ||||
|  | @ -3,10 +3,10 @@ package jsondb | |||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 
 | ||||
| 	"github.com/ngoduykhanh/wireguard-ui/model" | ||||
| 	"github.com/ngoduykhanh/wireguard-ui/util" | ||||
| ) | ||||
| 
 | ||||
| func (o *JsonDB) GetWakeOnLanHosts() ([]model.WakeOnLanHost, error) { | ||||
|  | @ -70,7 +70,11 @@ func (o *JsonDB) SaveWakeOnLanHost(host model.WakeOnLanHost) error { | |||
| 
 | ||||
| 	wakeOnLanHostPath := path.Join(path.Join(o.dbPath, model.WakeOnLanHostCollectionName), resourceName+".json") | ||||
| 	output := o.conn.Write(model.WakeOnLanHostCollectionName, resourceName, host) | ||||
| 	os.Chmod(wakeOnLanHostPath, 0600) | ||||
| 	err = util.ManagePerms(wakeOnLanHostPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return output | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ About | |||
|             <div class="col-md-6"> | ||||
|                 <div class="card card-success"> | ||||
|                     <div class="card-header"> | ||||
|                         <h3 class="card-title">About Wireguard-UI</h3> | ||||
|                         <h3 class="card-title">About WireGuard-UI</h3> | ||||
|                     </div> | ||||
|                     <!-- /.card-header --> | ||||
|                     <div class="card-body"> | ||||
|  | @ -63,7 +63,7 @@ About | |||
|                         </div> | ||||
|                         <strong>Copyright © | ||||
|                             <script>document.write(new Date().getFullYear())</script> | ||||
|                             <a href="https://github.com/ngoduykhanh/wireguard-ui">Wireguard UI</a>. | ||||
|                             <a href="https://github.com/idressos/wireguard-ui">WireGuard UI</a>. | ||||
|                         </strong> All rights reserved. | ||||
| 
 | ||||
|                     </div> | ||||
|  | @ -83,7 +83,7 @@ About | |||
|         $.ajax({ | ||||
|             cache: false, | ||||
|             method: 'GET', | ||||
|             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/releases/tags/' + $("#version").val(), | ||||
|             url: 'https://api.github.com/repos/idressos/wireguard-ui/releases/tags/' + $("#version").val(), | ||||
|             dataType: 'json', | ||||
|             contentType: "application/json", | ||||
|             success: function (data) { | ||||
|  | @ -99,7 +99,7 @@ About | |||
|         $.ajax({ | ||||
|             cache: false, | ||||
|             method: 'GET', | ||||
|             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/releases/latest', | ||||
|             url: 'https://api.github.com/repos/idressos/wireguard-ui/releases/latest', | ||||
|             dataType: 'json', | ||||
|             contentType: "application/json", | ||||
|             success: function (data) { | ||||
|  | @ -121,7 +121,7 @@ About | |||
|         $.ajax({ | ||||
|             cache: false, | ||||
|             method: 'GET', | ||||
|             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/contributors', | ||||
|             url: 'https://api.github.com/repos/idressos/wireguard-ui/contributors', | ||||
|             dataType: 'json', | ||||
|             contentType: "application/json", | ||||
|             success: function (data) { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <title>{{template "title" .}}</title> | ||||
|     <title>{{.pageTitlePrefix}}{{template "title" .}}</title> | ||||
|     <!-- Tell the browser to be responsive to screen width --> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <!-- Favicon --> | ||||
|  | @ -84,10 +84,10 @@ | |||
|         <!-- /.navbar --> | ||||
| 
 | ||||
|         <!-- Main Sidebar Container --> | ||||
|         <aside class="main-sidebar sidebar-dark-primary elevation-4"> | ||||
|         <aside class="main-sidebar sidebar-dark-primary elevation-4" style="background-color: {{.accentColor}};"> | ||||
|             <!-- Brand Logo --> | ||||
|             <a href="{{.basePath}}" class="brand-link"> | ||||
|                 <span class="brand-text">  WIREGUARD UI</span> | ||||
|                 <span class="brand-text">  {{.brandText}}</span> | ||||
|             </a> | ||||
| 
 | ||||
|             <!-- Sidebar --> | ||||
|  | @ -120,7 +120,7 @@ | |||
|                             <a href="{{.basePath}}/" class="nav-link {{if eq .baseData.Active ""}}active{{end}}"> | ||||
|                                 <i class="nav-icon fas fa-user-secret"></i> | ||||
|                                 <p> | ||||
|                                     Wireguard Clients | ||||
|                                     Clients | ||||
|                                 </p> | ||||
|                             </a> | ||||
|                         </li> | ||||
|  | @ -130,7 +130,7 @@ | |||
|                             <a href="{{.basePath}}/wg-server" class="nav-link {{if eq .baseData.Active "wg-server" }}active{{end}}"> | ||||
|                                 <i class="nav-icon fas fa-server"></i> | ||||
|                                 <p> | ||||
|                                     Wireguard Server | ||||
|                                     WireGuard Server | ||||
|                                 </p> | ||||
|                             </a> | ||||
|                         </li> | ||||
|  | @ -174,6 +174,8 @@ | |||
|                                 </p> | ||||
|                             </a> | ||||
|                         </li> | ||||
| 
 | ||||
|                         {{if .baseData.Admin}} | ||||
|                         <li class="nav-header">ABOUT</li> | ||||
|                         <li class="nav-item"> | ||||
|                             <a href="{{.basePath}}/about" class="nav-link {{if eq .baseData.Active "about" }}active{{end}}"> | ||||
|  | @ -183,6 +185,7 @@ | |||
|                             </p> | ||||
|                             </a> | ||||
|                         </li> | ||||
|                         {{end}} | ||||
|                     </ul> | ||||
|                 </nav> | ||||
|                 <!-- /.sidebar-menu --> | ||||
|  | @ -194,7 +197,7 @@ | |||
|             <div class="modal-dialog"> | ||||
|                 <div class="modal-content"> | ||||
|                     <div class="modal-header"> | ||||
|                         <h4 class="modal-title">New Wireguard Client</h4> | ||||
|                         <h4 class="modal-title">New WireGuard Client</h4> | ||||
|                         <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
|                             <span aria-hidden="true">×</span> | ||||
|                         </button> | ||||
|  | @ -232,6 +235,10 @@ | |||
|                                 </label> | ||||
|                                 <input type="text" data-role="tagsinput" class="form-control" id="client_extra_allowed_ips" value="{{ StringsJoin .client_defaults.ExtraAllowedIps "," }}"> | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <label for="client_endpoint" class="control-label">Endpoint</label> | ||||
|                                 <input type="text" class="form-control" id="client_endpoint" name="client_endpoint"> | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <div class="icheck-primary d-inline"> | ||||
|                                     <input type="checkbox" id="use_server_dns" {{ if .client_defaults.UseServerDNS }}checked{{ end }}> | ||||
|  | @ -328,7 +335,7 @@ | |||
|             <div class="float-right d-none d-sm-block"> | ||||
|                 <b>Version</b> {{ .appVersion }} | ||||
|             </div> | ||||
|             <strong>Copyright © <script>document.write(new Date().getFullYear())</script> <a href="https://github.com/ngoduykhanh/wireguard-ui">Wireguard UI</a>.</strong> All rights | ||||
|             <strong>Copyright © <script>document.write(new Date().getFullYear())</script> <a href="https://github.com/idressos/wireguard-ui">WireGuard UI</a>.</strong> All rights | ||||
|             reserved. | ||||
|         </footer> | ||||
|         --> | ||||
|  | @ -413,6 +420,7 @@ | |||
|             const email = $("#client_email").val(); | ||||
|             const allocated_ips = $("#client_allocated_ips").val().split(","); | ||||
|             const allowed_ips = $("#client_allowed_ips").val().split(","); | ||||
|             const endpoint = $("#client_endpoint").val(); | ||||
|             let use_server_dns = false; | ||||
|             let extra_allowed_ips = []; | ||||
| 
 | ||||
|  | @ -434,7 +442,7 @@ | |||
|             const preshared_key = $("#client_preshared_key").val(); | ||||
| 
 | ||||
|             const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips, | ||||
|                 "extra_allowed_ips": extra_allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled, | ||||
|                 "extra_allowed_ips": extra_allowed_ips, "endpoint": endpoint, "use_server_dns": use_server_dns, "enabled": enabled, | ||||
|                 "public_key": public_key, "preshared_key": preshared_key}; | ||||
| 
 | ||||
|             $.ajax({ | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| {{define "title"}} | ||||
| Wireguard Clients | ||||
| Clients | ||||
| {{end}} | ||||
| 
 | ||||
| {{define "top_css"}} | ||||
|  | @ -17,13 +17,13 @@ Wireguard Clients | |||
| {{end}} | ||||
| 
 | ||||
| {{define "page_title"}} | ||||
| Wireguard Clients | ||||
| Clients | ||||
| {{end}} | ||||
| 
 | ||||
| {{define "page_content"}} | ||||
| <section class="content"> | ||||
|     <div class="container-fluid"> | ||||
|         <!-- <h5 class="mt-4 mb-2">Wireguard Clients</h5> --> | ||||
|         <!-- <h5 class="mt-4 mb-2">Clients</h5> --> | ||||
|         <div class="row" id="client-list"> | ||||
|         </div> | ||||
|         <!-- /.row --> | ||||
|  | @ -113,6 +113,10 @@ Wireguard Clients | |||
|                         <input type="text" data-role="tagsinput" class="form-control" | ||||
|                                id="_client_extra_allowed_ips"> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="_client_endpoint" class="control-label">Endpoint</label> | ||||
|                         <input type="text" class="form-control" id="_client_endpoint" name="client_endpoint"> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <div class="icheck-primary d-inline"> | ||||
|                             <input type="checkbox" id="_use_server_dns"> | ||||
|  | @ -477,6 +481,8 @@ Wireguard Clients | |||
|                             modal.find("#_client_extra_allowed_ips").addTag(obj); | ||||
|                         }); | ||||
| 
 | ||||
|                         modal.find("#_client_endpoint").val(client.endpoint); | ||||
| 
 | ||||
|                         modal.find("#_use_server_dns").prop("checked", client.use_server_dns); | ||||
|                         modal.find("#_enabled").prop("checked", client.enabled); | ||||
| 
 | ||||
|  | @ -564,6 +570,8 @@ Wireguard Clients | |||
|                 extra_allowed_ips = $("#_client_extra_allowed_ips").val().split(","); | ||||
|             } | ||||
| 
 | ||||
|             const endpoint = $("#_client_endpoint").val(); | ||||
| 
 | ||||
|             if ($("#_use_server_dns").is(':checked')){ | ||||
|                 use_server_dns = true; | ||||
|             } | ||||
|  | @ -575,7 +583,8 @@ Wireguard Clients | |||
|             } | ||||
| 
 | ||||
|             const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips, | ||||
|                 "allowed_ips": allowed_ips, "extra_allowed_ips": extra_allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled, "public_key": public_key, "preshared_key": preshared_key}; | ||||
|                 "allowed_ips": allowed_ips, "extra_allowed_ips": extra_allowed_ips, "endpoint": endpoint, | ||||
|                 "use_server_dns": use_server_dns, "enabled": enabled, "public_key": public_key, "preshared_key": preshared_key}; | ||||
| 
 | ||||
|             $.ajax({ | ||||
|                 cache: false, | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ Global Settings | |||
|             <div class="col-md-6"> | ||||
|                 <div class="card card-success"> | ||||
|                     <div class="card-header"> | ||||
|                         <h3 class="card-title">Wireguard Global Settings</h3> | ||||
|                         <h3 class="card-title">WireGuard Global Settings</h3> | ||||
|                     </div> | ||||
|                     <!-- /.card-header --> | ||||
|                     <!-- form start --> | ||||
|  | @ -68,7 +68,7 @@ Global Settings | |||
|                                     value="{{ .globalSettings.Table }}"> | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <label for="config_file_path">Wireguard Config File Path</label> | ||||
|                                 <label for="config_file_path">WireGuard Config File Path</label> | ||||
|                                 <input type="text" class="form-control" id="config_file_path" | ||||
|                                     name="config_file_path" placeholder="E.g. /etc/wireguard/wg0.conf" | ||||
|                                     value="{{ .globalSettings.ConfigFilePath }}"> | ||||
|  | @ -92,7 +92,7 @@ Global Settings | |||
|                     <div class="card-body"> | ||||
|                         <dl> | ||||
|                             <dt>1. Endpoint Address</dt> | ||||
|                             <dd>The public IP address of your Wireguard server that the client will connect to. Click on | ||||
|                             <dd>The public IP address of your WireGuard server that the client will connect to. Click on | ||||
|                                 <strong>Suggest</strong> button to auto detect the public IP address of your server.</dd> | ||||
|                             <dt>2. DNS Servers</dt> | ||||
|                             <dd>The DNS servers will be set to client config.</dd> | ||||
|  | @ -110,8 +110,8 @@ Global Settings | |||
|                             <dd>Add a matching <code>fwmark</code> on all packets going out of a WireGuard non-default-route tunnel. Default value: <code>0xca6c</code></dd> | ||||
|                             <dt>6. Table</dt> | ||||
|                             <dd>Value for the <code>Table</code> setting in the wg conf file. Default value: <code>auto</code></dd>                             | ||||
|                             <dt>7. Wireguard Config File Path</dt> | ||||
|                             <dd>The path of your Wireguard server config file. Please make sure the parent directory | ||||
|                             <dt>7. WireGuard Config File Path</dt> | ||||
|                             <dd>The path of your WireGuard server config file. Please make sure the parent directory | ||||
|                                 exists and is writable.</dd> | ||||
|                         </dl> | ||||
|                     </div> | ||||
|  | @ -195,7 +195,7 @@ Global Settings | |||
|         } | ||||
|     </script> | ||||
|     <script> | ||||
|         // Wireguard Interface DNS server tag input | ||||
|         // WireGuard Interface DNS server tag input | ||||
|         $("#dns_servers").tagsInput({ | ||||
|             'width': '100%', | ||||
|             'height': '75%', | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <title>WireGuard UI</title> | ||||
|     <title>{{.brandText}}</title> | ||||
|     <!-- Tell the browser to be responsive to screen width --> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <!-- Favicon --> | ||||
|  | @ -24,8 +24,8 @@ | |||
| 
 | ||||
| <body class="hold-transition login-page"> | ||||
|     <div class="login-box"> | ||||
|         <div class="login-logo"> | ||||
|             <a href="https://github.com/ngoduykhanh/wireguard-ui">WireGuard UI</a> | ||||
|         <div class="login-logo pb-3"> | ||||
|             <img class="img-fluid" src="{{.basePath}}/logo"> | ||||
|         </div> | ||||
|         <!-- /.login-logo --> | ||||
|         <div class="card"> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| {{define "title"}} | ||||
| Wireguard Server | ||||
| WireGuard Server | ||||
| {{end}} | ||||
| 
 | ||||
| {{define "top_css"}} | ||||
|  | @ -10,13 +10,13 @@ Wireguard Server | |||
| {{end}} | ||||
| 
 | ||||
| {{define "page_title"}} | ||||
| Wireguard Server Settings | ||||
| WireGuard Server Settings | ||||
| {{end}} | ||||
| 
 | ||||
| {{define "page_content"}} | ||||
| <section class="content"> | ||||
|     <div class="container-fluid"> | ||||
|         <!-- <h5 class="mt-4 mb-2">Wireguard Server</h5> --> | ||||
|         <!-- <h5 class="mt-4 mb-2">WireGuard Server</h5> --> | ||||
|         <div class="row"> | ||||
|             <!-- left column --> | ||||
|             <div class="col-md-6"> | ||||
|  | @ -39,13 +39,13 @@ Wireguard Server Settings | |||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <label for="post_up">Post Up Script</label> | ||||
|                                 <input type="text" class="form-control" id="post_up" name="post_up" | ||||
|                                        placeholder="Post Up Script" value="{{ .serverInterface.PostUp }}"> | ||||
|                                 <textarea class="form-control" id="post_up" name="post_up" | ||||
|                                        placeholder="Post Up Script">{{ .serverInterface.PostUp }}</textarea> | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <label for="post_down">Post Down Script</label> | ||||
|                                 <input type="text" class="form-control" id="post_down" name="post_down" | ||||
|                                        placeholder="Post Down Script" value="{{ .serverInterface.PostDown }}"> | ||||
|                                 <textarea class="form-control" id="post_down" name="post_down" | ||||
|                                        placeholder="Post Down Script">{{ .serverInterface.PostDown }}</textarea> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <!-- /.card-body --> | ||||
|  | @ -109,7 +109,7 @@ Wireguard Server Settings | |||
|                 </button> | ||||
|             </div> | ||||
|             <div class="modal-body"> | ||||
|                 <p>Are you sure to generate a new key pair for the Wireguard server?<br/> | ||||
|                 <p>Are you sure to generate a new key pair for the WireGuard server?<br/> | ||||
|                 The existing Client's peer public key need to be updated to keep the connection working.</p> | ||||
|             </div> | ||||
|             <div class="modal-footer justify-content-between"> | ||||
|  | @ -142,7 +142,7 @@ Wireguard Server Settings | |||
|                 data: JSON.stringify(data), | ||||
|                 success: function(data) { | ||||
|                     $("#modal_new_client").modal('hide'); | ||||
|                     toastr.success('Updated Wireguard server interface addresses successfully'); | ||||
|                     toastr.success('Updated WireGuard server interface addresses successfully'); | ||||
|                 }, | ||||
|                 error: function(jqXHR, exception) { | ||||
|                     const responseJson = jQuery.parseJSON(jqXHR.responseText); | ||||
|  | @ -152,7 +152,7 @@ Wireguard Server Settings | |||
|         } | ||||
|     </script> | ||||
|     <script> | ||||
|         // Wireguard Interface Addresses tag input | ||||
|         // WireGuard Interface Addresses tag input | ||||
|         $("#addresses").tagsInput({ | ||||
|             'width': '100%', | ||||
|             // 'height': '75%', | ||||
|  | @ -169,7 +169,7 @@ Wireguard Server Settings | |||
|         $("#addresses").addTag('{{.}}'); | ||||
|         {{end}} | ||||
| 
 | ||||
|         // Wireguard Interface Addresses form validation | ||||
|         // WireGuard Interface Addresses form validation | ||||
|         $(document).ready(function () { | ||||
|             $.validator.setDefaults({ | ||||
|                 submitHandler: function () { | ||||
|  | @ -205,7 +205,7 @@ Wireguard Server Settings | |||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         // Wireguard Key Pair generation confirmation button | ||||
|         // WireGuard Key Pair generation confirmation button | ||||
|         $(document).ready(function () { | ||||
|             $("#btn_generate_confirm").click(function () { | ||||
|                 $.ajax({ | ||||
|  |  | |||
|  | @ -35,7 +35,6 @@ Connected Peers | |||
|         {{ end}} | ||||
|         {{ range $dev := .devices }} | ||||
|             <table class="table table-sm"> | ||||
|                 <caption>List of connected peers for device with name {{ $dev.Name }} </caption> | ||||
|               <thead> | ||||
|                 <tr> | ||||
|                   <th scope="col">#</th> | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| # This file was generated using wireguard-ui (https://github.com/ngoduykhanh/wireguard-ui) | ||||
| # This file was generated using wireguard-ui (https://github.com/idressos/wireguard-ui) | ||||
| # Please don't modify it manually, otherwise your change might get replaced. | ||||
| 
 | ||||
| # Address updated at:     {{ .serverConfig.Interface.UpdatedAt }} | ||||
|  | @ -7,10 +7,11 @@ | |||
| Address = {{$first :=true}}{{range .serverConfig.Interface.Addresses }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}} | ||||
| ListenPort = {{ .serverConfig.Interface.ListenPort }} | ||||
| PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }} | ||||
| {{if .globalSettings.FirewallMark }}FwMark = {{ .globalSettings.FirewallMark }}{{end}} | ||||
| {{if .globalSettings.MTU}}MTU = {{ .globalSettings.MTU }}{{end}} | ||||
| PostUp = {{ .serverConfig.Interface.PostUp }} | ||||
| PostDown = {{ .serverConfig.Interface.PostDown }} | ||||
| Table = {{ .globalSettings.Table }} | ||||
| {{if .serverConfig.Interface.PostUp }}PostUp = {{ .serverConfig.Interface.PostUp }}{{end}} | ||||
| {{if .serverConfig.Interface.PostDown }}PostDown = {{ .serverConfig.Interface.PostDown }}{{end}} | ||||
| {{if .globalSettings.Table}}Table = {{ .globalSettings.Table }}{{end}} | ||||
| 
 | ||||
| {{range .clientDataList}}{{if eq .Client.Enabled true}} | ||||
| # ID:           {{ .Client.ID }} | ||||
|  | @ -20,6 +21,8 @@ Table = {{ .globalSettings.Table }} | |||
| # Update at:    {{ .Client.UpdatedAt }} | ||||
| [Peer] | ||||
| PublicKey = {{ .Client.PublicKey }} | ||||
| {{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }} | ||||
| {{end}}AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}} | ||||
| {{end}}{{end}} | ||||
| {{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}{{end}} | ||||
| AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}}{{end}} | ||||
| {{if $.globalSettings.PersistentKeepalive}}PersistentKeepalive = {{ $.globalSettings.PersistentKeepalive }}{{end}} | ||||
| {{if .Client.Endpoint }}Endpoint = {{ .Client.Endpoint }}{{end}} | ||||
| {{end}} | ||||
|  | @ -10,6 +10,7 @@ var ( | |||
| 	SmtpPort       int | ||||
| 	SmtpUsername   string | ||||
| 	SmtpPassword   string | ||||
| 	HelloHostName  string | ||||
| 	SmtpNoTLSCheck bool | ||||
| 	SmtpEncryption string | ||||
| 	SmtpAuthType   string | ||||
|  | @ -19,6 +20,9 @@ var ( | |||
| 	SessionSecret  []byte | ||||
| 	WgConfTemplate string | ||||
| 	BasePath       string | ||||
| 	BrandText string | ||||
| 	AccentColor string | ||||
| 	PageTitlePrefix string | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | @ -53,6 +57,10 @@ const ( | |||
| 	DefaultClientExtraAllowedIpsEnvVar     = "WGUI_DEFAULT_CLIENT_EXTRA_ALLOWED_IPS" | ||||
| 	DefaultClientUseServerDNSEnvVar        = "WGUI_DEFAULT_CLIENT_USE_SERVER_DNS" | ||||
| 	DefaultClientEnableAfterCreationEnvVar = "WGUI_DEFAULT_CLIENT_ENABLE_AFTER_CREATION" | ||||
| 	BrandTextEnvVar                        = "WGUI_BRAND_TEXT" | ||||
| 	AccentColorEnvVar                      = "WGUI_ACCENT_COLOR" | ||||
| 	PageTitlePrefixEnvVar                  = "WGUI_PAGE_TITLE_PREFIX" | ||||
| 	LogoFilePathEnvVar                     = "WGUI_LOGO_FILE_PATH" | ||||
| ) | ||||
| 
 | ||||
| func ParseBasePath(basePath string) string { | ||||
|  |  | |||
							
								
								
									
										12
									
								
								util/util.go
								
								
								
								
							
							
						
						
									
										12
									
								
								util/util.go
								
								
								
								
							|  | @ -4,8 +4,6 @@ import ( | |||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/ngoduykhanh/wireguard-ui/store" | ||||
| 	"golang.org/x/mod/sumdb/dirhash" | ||||
| 	"io" | ||||
| 	"io/fs" | ||||
| 	"io/ioutil" | ||||
|  | @ -19,6 +17,9 @@ import ( | |||
| 	"text/template" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ngoduykhanh/wireguard-ui/store" | ||||
| 	"golang.org/x/mod/sumdb/dirhash" | ||||
| 
 | ||||
| 	externalip "github.com/glendc/go-external-ip" | ||||
| 	"github.com/labstack/gommon/log" | ||||
| 	"github.com/ngoduykhanh/wireguard-ui/model" | ||||
|  | @ -382,7 +383,7 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip | |||
| 	return true, nil | ||||
| } | ||||
| 
 | ||||
| // WriteWireGuardServerConfig to write Wireguard server config. e.g. wg0.conf
 | ||||
| // WriteWireGuardServerConfig to write WireGuard server config. e.g. wg0.conf
 | ||||
| func WriteWireGuardServerConfig(tmplDir fs.FS, serverConfig model.Server, clientDataList []model.ClientData, usersList []model.User, globalSettings model.GlobalSetting) error { | ||||
| 	var tmplWireguardConf string | ||||
| 
 | ||||
|  | @ -540,3 +541,8 @@ func RandomString(length int) string { | |||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
| 
 | ||||
| func ManagePerms(path string) error { | ||||
| 	err := os.Chmod(path, 0600) | ||||
| 	return err | ||||
| } | ||||
		Loading…
	
		Reference in New Issue