mirror of
https://github.com/ngoduykhanh/wireguard-ui.git
synced 2025-04-19 19:59:13 +03:00
Merge branch 'ngoduykhanh:master' into master
This commit is contained in:
commit
0a2d3fb212
16 changed files with 385 additions and 76 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -21,6 +21,9 @@ node_modules/
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
.*.sw[op]
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
examples/docker-compose/config
|
examples/docker-compose/config
|
||||||
examples/docker-compose/db
|
examples/docker-compose/db
|
||||||
|
|
59
README.md
59
README.md
|
@ -36,33 +36,38 @@ docker-compose up
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------|
|
|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------|
|
||||||
| `BASE_PATH` | Set this variable if you run wireguard-ui under a subpath of your reverse proxy virtual host (e.g. /wireguard)) | N/A |
|
| `BASE_PATH` | Set this variable if you run wireguard-ui under a subpath of your reverse proxy virtual host (e.g. /wireguard) | N/A |
|
||||||
| `BIND_ADDRESS` | The addresses that can access to the web interface and the port | 0.0.0.0:80 |
|
| `BIND_ADDRESS` | The addresses that can access to the web interface and the port, use unix:///abspath/to/file.socket for unix domain socket. | 0.0.0.0:80 |
|
||||||
| `SESSION_SECRET` | The secret key used to encrypt the session cookies. Set this to a random value | N/A |
|
| `SESSION_SECRET` | The secret key used to encrypt the session cookies. Set this to a random value | N/A |
|
||||||
| `WGUI_USERNAME` | The username for the login page. Used for db initialization only | `admin` |
|
| `SESSION_SECRET_FILE` | Optional filepath for the secret key used to encrypt the session cookies. Leave `SESSION_SECRET` blank to take effect | N/A |
|
||||||
| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically. Used for db initialization only | `admin` |
|
| `WGUI_USERNAME` | The username for the login page. Used for db initialization only | `admin` |
|
||||||
| `WGUI_PASSWORD_HASH` | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`). Used for db initialization only | N/A |
|
| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically. Used for db initialization only | `admin` |
|
||||||
| `WGUI_ENDPOINT_ADDRESS` | The default endpoint address used in global settings where clients should connect to | Resolved to your public ip address |
|
| `WGUI_PASSWORD_FILE` | Optional filepath for the user login password. Will be hashed automatically. Used for db initialization only. Leave `WGUI_PASSWORD` blank to take effect | N/A |
|
||||||
| `WGUI_FAVICON_FILE_PATH` | The file path used as website favicon | Embedded WireGuard logo |
|
| `WGUI_PASSWORD_HASH` | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`). Used for db initialization only | N/A |
|
||||||
| `WGUI_DNS` | The default DNS servers (comma-separated-list) used in the global settings | `1.1.1.1` |
|
| `WGUI_PASSWORD_HASH_FILE` | Optional filepath for the user login password hash. (alternative to `WGUI_PASSWORD_FILE`). Used for db initialization only. Leave `WGUI_PASSWORD_HASH` blank to take effect | N/A |
|
||||||
| `WGUI_MTU` | The default MTU used in global settings | `1450` |
|
| `WGUI_ENDPOINT_ADDRESS` | The default endpoint address used in global settings where clients should connect to | Resolved to your public ip address |
|
||||||
| `WGUI_PERSISTENT_KEEPALIVE` | The default persistent keepalive for WireGuard in global settings | `15` |
|
| `WGUI_FAVICON_FILE_PATH` | The file path used as website favicon | Embedded WireGuard logo |
|
||||||
| `WGUI_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) |
|
| `WGUI_DNS` | The default DNS servers (comma-separated-list) used in the global settings | `1.1.1.1` |
|
||||||
| `WGUI_TABLE` | The default WireGuard table value settings | `auto` |
|
| `WGUI_MTU` | The default MTU used in global settings | `1450` |
|
||||||
| `WGUI_CONFIG_FILE_PATH` | The default WireGuard config file path used in global settings | `/etc/wireguard/wg0.conf` |
|
| `WGUI_PERSISTENT_KEEPALIVE` | The default persistent keepalive for WireGuard in global settings | `15` |
|
||||||
| `WGUI_LOG_LEVEL` | The default log level. Possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `OFF` | `INFO` |
|
| `WGUI_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) |
|
||||||
| `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_TABLE` | The default WireGuard table value settings | `auto` |
|
||||||
| `EMAIL_FROM_ADDRESS` | The sender email address | N/A |
|
| `WGUI_CONFIG_FILE_PATH` | The default WireGuard config file path used in global settings | `/etc/wireguard/wg0.conf` |
|
||||||
| `EMAIL_FROM_NAME` | The sender name | `WireGuard UI` |
|
| `WGUI_LOG_LEVEL` | The default log level. Possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `OFF` | `INFO` |
|
||||||
| `SENDGRID_API_KEY` | The SendGrid api key | N/A |
|
| `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 |
|
||||||
| `SMTP_HOSTNAME` | The SMTP IP address or hostname | `127.0.0.1` |
|
| `EMAIL_FROM_ADDRESS` | The sender email address | N/A |
|
||||||
| `SMTP_PORT` | The SMTP port | `25` |
|
| `EMAIL_FROM_NAME` | The sender name | `WireGuard UI` |
|
||||||
| `SMTP_USERNAME` | The SMTP username | N/A |
|
| `SENDGRID_API_KEY` | The SendGrid api key | N/A |
|
||||||
| `SMTP_PASSWORD` | The SMTP user password | N/A |
|
| `SENDGRID_API_KEY_FILE` | Optional filepath for the SendGrid api key. Leave `SENDGRID_API_KEY` blank to take effect | N/A |
|
||||||
| `SMTP_AUTH_TYPE` | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE` | `NONE` |
|
| `SMTP_HOSTNAME` | The SMTP IP address or hostname | `127.0.0.1` |
|
||||||
| `SMTP_ENCRYPTION` | the encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS` | `STARTTLS` |
|
| `SMTP_PORT` | The SMTP port | `25` |
|
||||||
|
| `SMTP_USERNAME` | The SMTP username | N/A |
|
||||||
|
| `SMTP_PASSWORD` | The SMTP user password | N/A |
|
||||||
|
| `SMTP_PASSWORD_FILE` | Optional filepath for the SMTP user password. Leave `SMTP_PASSWORD` blank to take effect | 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` |
|
||||||
|
|
||||||
### Defaults for server configuration
|
### Defaults for server configuration
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,6 +27,8 @@ import (
|
||||||
"github.com/ngoduykhanh/wireguard-ui/util"
|
"github.com/ngoduykhanh/wireguard-ui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var usernameRegexp = regexp.MustCompile("^\\w[\\w\\-.]*$")
|
||||||
|
|
||||||
// Health check handler
|
// Health check handler
|
||||||
func Health() echo.HandlerFunc {
|
func Health() echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
@ -63,6 +66,10 @@ func Login(db store.IStore) echo.HandlerFunc {
|
||||||
password := data["password"].(string)
|
password := data["password"].(string)
|
||||||
rememberMe := data["rememberMe"].(bool)
|
rememberMe := data["rememberMe"].(bool)
|
||||||
|
|
||||||
|
if !usernameRegexp.MatchString(username) {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
|
}
|
||||||
|
|
||||||
dbuser, err := db.GetUserByName(username)
|
dbuser, err := db.GetUserByName(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
|
||||||
|
@ -135,9 +142,12 @@ func GetUsers(db store.IStore) echo.HandlerFunc {
|
||||||
// GetUser handler returns a JSON object of single user
|
// GetUser handler returns a JSON object of single user
|
||||||
func GetUser(db store.IStore) echo.HandlerFunc {
|
func GetUser(db store.IStore) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
||||||
username := c.Param("username")
|
username := c.Param("username")
|
||||||
|
|
||||||
|
if !usernameRegexp.MatchString(username) {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
|
}
|
||||||
|
|
||||||
if !isAdmin(c) && (username != currentUser(c)) {
|
if !isAdmin(c) && (username != currentUser(c)) {
|
||||||
return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "Manager cannot access other user data"})
|
return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "Manager cannot access other user data"})
|
||||||
}
|
}
|
||||||
|
@ -200,12 +210,16 @@ func UpdateUser(db store.IStore) echo.HandlerFunc {
|
||||||
admin = false
|
admin = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !usernameRegexp.MatchString(previousUsername) {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
|
}
|
||||||
|
|
||||||
user, err := db.GetUserByName(previousUsername)
|
user, err := db.GetUserByName(previousUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
|
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
if username == "" {
|
if username == "" || !usernameRegexp.MatchString(username) {
|
||||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
} else {
|
} else {
|
||||||
user.Username = username
|
user.Username = username
|
||||||
|
@ -261,7 +275,7 @@ func CreateUser(db store.IStore) echo.HandlerFunc {
|
||||||
password := data["password"].(string)
|
password := data["password"].(string)
|
||||||
admin := data["admin"].(bool)
|
admin := data["admin"].(bool)
|
||||||
|
|
||||||
if username == "" {
|
if username == "" || !usernameRegexp.MatchString(username) {
|
||||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
} else {
|
} else {
|
||||||
user.Username = username
|
user.Username = username
|
||||||
|
@ -303,6 +317,10 @@ func RemoveUser(db store.IStore) echo.HandlerFunc {
|
||||||
|
|
||||||
username := data["username"].(string)
|
username := data["username"].(string)
|
||||||
|
|
||||||
|
if !usernameRegexp.MatchString(username) {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid username"})
|
||||||
|
}
|
||||||
|
|
||||||
if username == currentUser(c) {
|
if username == currentUser(c) {
|
||||||
return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "User cannot delete itself"})
|
return c.JSON(http.StatusForbidden, jsonHTTPResponse{false, "User cannot delete itself"})
|
||||||
}
|
}
|
||||||
|
@ -357,6 +375,11 @@ func GetClient(db store.IStore) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
||||||
clientID := c.Param("id")
|
clientID := c.Param("id")
|
||||||
|
|
||||||
|
if _, err := xid.FromString(clientID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
qrCodeSettings := model.QRCodeSettings{
|
qrCodeSettings := model.QRCodeSettings{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
IncludeDNS: true,
|
IncludeDNS: true,
|
||||||
|
@ -485,6 +508,10 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
|
||||||
c.Bind(&payload)
|
c.Bind(&payload)
|
||||||
// TODO validate email
|
// TODO validate email
|
||||||
|
|
||||||
|
if _, err := xid.FromString(payload.ID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
qrCodeSettings := model.QRCodeSettings{
|
qrCodeSettings := model.QRCodeSettings{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
IncludeDNS: true,
|
IncludeDNS: true,
|
||||||
|
@ -536,6 +563,10 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
|
||||||
var _client model.Client
|
var _client model.Client
|
||||||
c.Bind(&_client)
|
c.Bind(&_client)
|
||||||
|
|
||||||
|
if _, err := xid.FromString(_client.ID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
// validate client existence
|
// validate client existence
|
||||||
clientData, err := db.GetClientByID(_client.ID, model.QRCodeSettings{Enabled: false})
|
clientData, err := db.GetClientByID(_client.ID, model.QRCodeSettings{Enabled: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -567,6 +598,45 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
|
||||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"})
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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"})
|
||||||
|
}
|
||||||
|
// check for duplicates
|
||||||
|
clients, err := db.GetClients(false)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Cannot get client list for duplicate public key check")
|
||||||
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get client list for duplicate public key check"})
|
||||||
|
}
|
||||||
|
for _, other := range clients {
|
||||||
|
if other.Client.PublicKey == _client.PublicKey {
|
||||||
|
log.Error("Duplicate Public Key")
|
||||||
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Duplicate Public Key"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
if client.PrivateKey != "" {
|
||||||
|
client.PrivateKey = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// map new data
|
// map new data
|
||||||
client.Name = _client.Name
|
client.Name = _client.Name
|
||||||
client.Email = _client.Email
|
client.Email = _client.Email
|
||||||
|
@ -575,6 +645,9 @@ func UpdateClient(db store.IStore) echo.HandlerFunc {
|
||||||
client.AllocatedIPs = _client.AllocatedIPs
|
client.AllocatedIPs = _client.AllocatedIPs
|
||||||
client.AllowedIPs = _client.AllowedIPs
|
client.AllowedIPs = _client.AllowedIPs
|
||||||
client.ExtraAllowedIPs = _client.ExtraAllowedIPs
|
client.ExtraAllowedIPs = _client.ExtraAllowedIPs
|
||||||
|
client.Endpoint = _client.Endpoint
|
||||||
|
client.PublicKey = _client.PublicKey
|
||||||
|
client.PresharedKey = _client.PresharedKey
|
||||||
client.UpdatedAt = time.Now().UTC()
|
client.UpdatedAt = time.Now().UTC()
|
||||||
|
|
||||||
// write to the database
|
// write to the database
|
||||||
|
@ -601,6 +674,10 @@ func SetClientStatus(db store.IStore) echo.HandlerFunc {
|
||||||
clientID := data["id"].(string)
|
clientID := data["id"].(string)
|
||||||
status := data["status"].(bool)
|
status := data["status"].(bool)
|
||||||
|
|
||||||
|
if _, err := xid.FromString(clientID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
|
clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
|
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
|
||||||
|
@ -626,6 +703,10 @@ func DownloadClient(db store.IStore) echo.HandlerFunc {
|
||||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Missing clientid parameter"})
|
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Missing clientid parameter"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := xid.FromString(clientID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
|
clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err)
|
log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err)
|
||||||
|
@ -648,7 +729,7 @@ func DownloadClient(db store.IStore) echo.HandlerFunc {
|
||||||
|
|
||||||
// set response header for downloading
|
// set response header for downloading
|
||||||
c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s.conf", clientData.Client.Name))
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,6 +740,10 @@ func RemoveClient(db store.IStore) echo.HandlerFunc {
|
||||||
client := new(model.Client)
|
client := new(model.Client)
|
||||||
c.Bind(client)
|
c.Bind(client)
|
||||||
|
|
||||||
|
if _, err := xid.FromString(client.ID); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Please provide a valid client ID"})
|
||||||
|
}
|
||||||
|
|
||||||
// delete client from database
|
// delete client from database
|
||||||
|
|
||||||
if err := db.DeleteClient(client.ID); err != nil {
|
if err := db.DeleteClient(client.ID); err != nil {
|
||||||
|
|
70
main.go
70
main.go
|
@ -4,13 +4,17 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
"github.com/labstack/gommon/log"
|
|
||||||
"github.com/ngoduykhanh/wireguard-ui/store"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
"strings"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/gommon/log"
|
||||||
|
"github.com/ngoduykhanh/wireguard-ui/store"
|
||||||
|
|
||||||
"github.com/ngoduykhanh/wireguard-ui/emailer"
|
"github.com/ngoduykhanh/wireguard-ui/emailer"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/handler"
|
"github.com/ngoduykhanh/wireguard-ui/handler"
|
||||||
|
@ -38,7 +42,7 @@ var (
|
||||||
flagSendgridApiKey string
|
flagSendgridApiKey string
|
||||||
flagEmailFrom string
|
flagEmailFrom string
|
||||||
flagEmailFromName string = "WireGuard UI"
|
flagEmailFromName string = "WireGuard UI"
|
||||||
flagSessionSecret string
|
flagSessionSecret string = util.RandomString(32)
|
||||||
flagWgConfTemplate string
|
flagWgConfTemplate string
|
||||||
flagBasePath string
|
flagBasePath string
|
||||||
)
|
)
|
||||||
|
@ -70,16 +74,41 @@ func init() {
|
||||||
flag.StringVar(&flagSmtpHostname, "smtp-hostname", util.LookupEnvOrString("SMTP_HOSTNAME", flagSmtpHostname), "SMTP Hostname")
|
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.IntVar(&flagSmtpPort, "smtp-port", util.LookupEnvOrInt("SMTP_PORT", flagSmtpPort), "SMTP Port")
|
||||||
flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Username")
|
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.")
|
flag.BoolVar(&flagSmtpNoTLSCheck, "smtp-no-tls-check", util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", flagSmtpNoTLSCheck), "Disable TLS verification for SMTP. This is potentially dangerous.")
|
||||||
flag.StringVar(&flagSmtpEncryption, "smtp-encryption", util.LookupEnvOrString("SMTP_ENCRYPTION", flagSmtpEncryption), "SMTP Encryption : NONE, SSL, SSLTLS, TLS or STARTTLS (by default)")
|
flag.StringVar(&flagSmtpEncryption, "smtp-encryption", util.LookupEnvOrString("SMTP_ENCRYPTION", flagSmtpEncryption), "SMTP Encryption : NONE, SSL, SSLTLS, TLS or STARTTLS (by default)")
|
||||||
flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : PLAIN, LOGIN or NONE.")
|
flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : PLAIN, LOGIN or NONE.")
|
||||||
flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey), "Your sendgrid api key.")
|
|
||||||
flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.")
|
flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.")
|
||||||
flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")
|
flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")
|
||||||
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(&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(&flagBasePath, "base-path", util.LookupEnvOrString("BASE_PATH", flagBasePath), "The base path of the URL")
|
||||||
|
|
||||||
|
var (
|
||||||
|
smtpPasswordLookup = util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword)
|
||||||
|
sengridApiKeyLookup = util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey)
|
||||||
|
sessionSecretLookup = util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret)
|
||||||
|
)
|
||||||
|
|
||||||
|
// check empty smtpPassword env var
|
||||||
|
if smtpPasswordLookup != "" {
|
||||||
|
flag.StringVar(&flagSmtpPassword, "smtp-password", smtpPasswordLookup, "SMTP Password")
|
||||||
|
} else {
|
||||||
|
flag.StringVar(&flagSmtpPassword, "smtp-password", util.LookupEnvOrFile("SMTP_PASSWORD_FILE", flagSmtpPassword), "SMTP Password File")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check empty sengridApiKey env var
|
||||||
|
if sengridApiKeyLookup != "" {
|
||||||
|
flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", sengridApiKeyLookup, "Your sendgrid api key.")
|
||||||
|
} else {
|
||||||
|
flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrFile("SENDGRID_API_KEY_FILE", flagSendgridApiKey), "File containing your sendgrid api key.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check empty sessionSecret env var
|
||||||
|
if sessionSecretLookup != "" {
|
||||||
|
flag.StringVar(&flagSessionSecret, "session-secret", sessionSecretLookup, "The key used to encrypt session cookies.")
|
||||||
|
} else {
|
||||||
|
flag.StringVar(&flagSessionSecret, "session-secret", util.LookupEnvOrFile("SESSION_SECRET_FILE", flagSessionSecret), "File containing the key used to encrypt session cookies.")
|
||||||
|
}
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// update runtime config
|
// update runtime config
|
||||||
|
@ -146,15 +175,19 @@ func main() {
|
||||||
|
|
||||||
app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession)
|
app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession)
|
||||||
|
|
||||||
|
// Important: Make sure that all non-GET routes check the request content type using handler.ContentTypeJson to
|
||||||
|
// mitigate CSRF attacks. This is effective, because browsers don't allow setting the Content-Type header on
|
||||||
|
// cross-origin requests.
|
||||||
|
|
||||||
if !util.DisableLogin {
|
if !util.DisableLogin {
|
||||||
app.GET(util.BasePath+"/login", handler.LoginPage())
|
app.GET(util.BasePath+"/login", handler.LoginPage())
|
||||||
app.POST(util.BasePath+"/login", handler.Login(db))
|
app.POST(util.BasePath+"/login", handler.Login(db), handler.ContentTypeJson)
|
||||||
app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession)
|
app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession)
|
||||||
app.GET(util.BasePath+"/profile", handler.LoadProfile(db), handler.ValidSession)
|
app.GET(util.BasePath+"/profile", handler.LoadProfile(db), handler.ValidSession)
|
||||||
app.GET(util.BasePath+"/users-settings", handler.UsersSettings(db), handler.ValidSession, handler.NeedsAdmin)
|
app.GET(util.BasePath+"/users-settings", handler.UsersSettings(db), handler.ValidSession, handler.NeedsAdmin)
|
||||||
app.POST(util.BasePath+"/update-user", handler.UpdateUser(db), handler.ValidSession)
|
app.POST(util.BasePath+"/update-user", handler.UpdateUser(db), handler.ValidSession, handler.ContentTypeJson)
|
||||||
app.POST(util.BasePath+"/create-user", handler.CreateUser(db), handler.ValidSession, handler.NeedsAdmin)
|
app.POST(util.BasePath+"/create-user", handler.CreateUser(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||||
app.POST(util.BasePath+"/remove-user", handler.RemoveUser(db), handler.ValidSession, handler.NeedsAdmin)
|
app.POST(util.BasePath+"/remove-user", handler.RemoveUser(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||||
app.GET(util.BasePath+"/getusers", handler.GetUsers(db), handler.ValidSession, handler.NeedsAdmin)
|
app.GET(util.BasePath+"/getusers", handler.GetUsers(db), handler.ValidSession, handler.NeedsAdmin)
|
||||||
app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession)
|
app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession)
|
||||||
}
|
}
|
||||||
|
@ -199,7 +232,20 @@ func main() {
|
||||||
// serves other static files
|
// serves other static files
|
||||||
app.GET(util.BasePath+"/static/*", echo.WrapHandler(http.StripPrefix(util.BasePath+"/static/", assetHandler)))
|
app.GET(util.BasePath+"/static/*", echo.WrapHandler(http.StripPrefix(util.BasePath+"/static/", assetHandler)))
|
||||||
|
|
||||||
app.Logger.Fatal(app.Start(util.BindAddress))
|
if strings.HasPrefix(util.BindAddress, "unix://") {
|
||||||
|
// Listen on unix domain socket.
|
||||||
|
// https://github.com/labstack/echo/issues/830
|
||||||
|
syscall.Unlink(util.BindAddress[6:])
|
||||||
|
l, err := net.Listen("unix", util.BindAddress[6:])
|
||||||
|
if err != nil {
|
||||||
|
app.Logger.Fatal(err)
|
||||||
|
}
|
||||||
|
app.Listener = l
|
||||||
|
app.Logger.Fatal(app.Start(""))
|
||||||
|
} else {
|
||||||
|
// Listen on TCP socket
|
||||||
|
app.Logger.Fatal(app.Start(util.BindAddress))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initServerConfig(db store.IStore, tmplDir fs.FS) {
|
func initServerConfig(db store.IStore, tmplDir fs.FS) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ type Client struct {
|
||||||
AllocatedIPs []string `json:"allocated_ips"`
|
AllocatedIPs []string `json:"allocated_ips"`
|
||||||
AllowedIPs []string `json:"allowed_ips"`
|
AllowedIPs []string `json:"allowed_ips"`
|
||||||
ExtraAllowedIPs []string `json:"extra_allowed_ips"`
|
ExtraAllowedIPs []string `json:"extra_allowed_ips"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
UseServerDNS bool `json:"use_server_dns"`
|
UseServerDNS bool `json:"use_server_dns"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
|
@ -23,5 +23,6 @@ type ServerInterface struct {
|
||||||
ListenPort int `json:"listen_port,string"` // ,string to get listen_port string input as int
|
ListenPort int `json:"listen_port,string"` // ,string to get listen_port string input as int
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
PostUp string `json:"post_up"`
|
PostUp string `json:"post_up"`
|
||||||
|
PreDown string `json:"pre_down"`
|
||||||
PostDown string `json:"post_down"`
|
PostDown string `json:"post_down"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -18,7 +19,13 @@ func (host WakeOnLanHost) ResolveResourceName() (string, error) {
|
||||||
return "", errors.New("mac Address is Empty")
|
return "", errors.New("mac Address is Empty")
|
||||||
}
|
}
|
||||||
resourceName = strings.ToUpper(resourceName)
|
resourceName = strings.ToUpper(resourceName)
|
||||||
return strings.ReplaceAll(resourceName, ":", "-"), nil
|
resourceName = strings.ReplaceAll(resourceName, ":", "-")
|
||||||
|
|
||||||
|
if _, err := net.ParseMAC(resourceName); err != nil {
|
||||||
|
return "", errors.New("invalid mac address")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const WakeOnLanHostCollectionName = "wake_on_lan_hosts"
|
const WakeOnLanHostCollectionName = "wake_on_lan_hosts"
|
||||||
|
|
|
@ -38,12 +38,12 @@ func New(dbPath string) (*JsonDB, error) {
|
||||||
func (o *JsonDB) Init() error {
|
func (o *JsonDB) Init() error {
|
||||||
var clientPath string = path.Join(o.dbPath, "clients")
|
var clientPath string = path.Join(o.dbPath, "clients")
|
||||||
var serverPath string = path.Join(o.dbPath, "server")
|
var serverPath string = path.Join(o.dbPath, "server")
|
||||||
|
var userPath string = path.Join(o.dbPath, "users")
|
||||||
var wakeOnLanHostsPath string = path.Join(o.dbPath, "wake_on_lan_hosts")
|
var wakeOnLanHostsPath string = path.Join(o.dbPath, "wake_on_lan_hosts")
|
||||||
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
|
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
|
||||||
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
|
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
|
||||||
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
|
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
|
||||||
var hashesPath string = path.Join(serverPath, "hashes.json")
|
var hashesPath string = path.Join(serverPath, "hashes.json")
|
||||||
var userPath string = path.Join(serverPath, "users.json")
|
|
||||||
|
|
||||||
// create directories if they do not exist
|
// create directories if they do not exist
|
||||||
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
|
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
|
||||||
|
@ -52,12 +52,12 @@ func (o *JsonDB) Init() error {
|
||||||
if _, err := os.Stat(serverPath); os.IsNotExist(err) {
|
if _, err := os.Stat(serverPath); os.IsNotExist(err) {
|
||||||
os.MkdirAll(serverPath, os.ModePerm)
|
os.MkdirAll(serverPath, os.ModePerm)
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(wakeOnLanHostsPath); os.IsNotExist(err) {
|
|
||||||
os.MkdirAll(wakeOnLanHostsPath, os.ModePerm)
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(userPath); os.IsNotExist(err) {
|
if _, err := os.Stat(userPath); os.IsNotExist(err) {
|
||||||
os.MkdirAll(userPath, os.ModePerm)
|
os.MkdirAll(userPath, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
if _, err := os.Stat(wakeOnLanHostsPath); os.IsNotExist(err) {
|
||||||
|
os.MkdirAll(wakeOnLanHostsPath, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
// server's interface
|
// server's interface
|
||||||
if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
|
if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
|
||||||
|
@ -68,6 +68,10 @@ func (o *JsonDB) Init() error {
|
||||||
serverInterface.PostDown = util.LookupEnvOrString(util.ServerPostDownScriptEnvVar, "")
|
serverInterface.PostDown = util.LookupEnvOrString(util.ServerPostDownScriptEnvVar, "")
|
||||||
serverInterface.UpdatedAt = time.Now().UTC()
|
serverInterface.UpdatedAt = time.Now().UTC()
|
||||||
o.conn.Write("server", "interfaces", serverInterface)
|
o.conn.Write("server", "interfaces", serverInterface)
|
||||||
|
err := util.ManagePerms(serverInterfacePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// server's key pair
|
// server's key pair
|
||||||
|
@ -82,6 +86,10 @@ func (o *JsonDB) Init() error {
|
||||||
serverKeyPair.PublicKey = key.PublicKey().String()
|
serverKeyPair.PublicKey = key.PublicKey().String()
|
||||||
serverKeyPair.UpdatedAt = time.Now().UTC()
|
serverKeyPair.UpdatedAt = time.Now().UTC()
|
||||||
o.conn.Write("server", "keypair", serverKeyPair)
|
o.conn.Write("server", "keypair", serverKeyPair)
|
||||||
|
err = util.ManagePerms(serverKeyPairPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// global settings
|
// global settings
|
||||||
|
@ -106,6 +114,10 @@ func (o *JsonDB) Init() error {
|
||||||
globalSetting.ConfigFilePath = util.LookupEnvOrString(util.ConfigFilePathEnvVar, util.DefaultConfigFilePath)
|
globalSetting.ConfigFilePath = util.LookupEnvOrString(util.ConfigFilePathEnvVar, util.DefaultConfigFilePath)
|
||||||
globalSetting.UpdatedAt = time.Now().UTC()
|
globalSetting.UpdatedAt = time.Now().UTC()
|
||||||
o.conn.Write("server", "global_settings", globalSetting)
|
o.conn.Write("server", "global_settings", globalSetting)
|
||||||
|
err := util.ManagePerms(globalSettingPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashes
|
// hashes
|
||||||
|
@ -114,6 +126,10 @@ func (o *JsonDB) Init() error {
|
||||||
clientServerHashes.Client = "none"
|
clientServerHashes.Client = "none"
|
||||||
clientServerHashes.Server = "none"
|
clientServerHashes.Server = "none"
|
||||||
o.conn.Write("server", "hashes", clientServerHashes)
|
o.conn.Write("server", "hashes", clientServerHashes)
|
||||||
|
err := util.ManagePerms(hashesPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// user info
|
// user info
|
||||||
|
@ -124,25 +140,30 @@ func (o *JsonDB) Init() error {
|
||||||
user.Admin = util.DefaultIsAdmin
|
user.Admin = util.DefaultIsAdmin
|
||||||
user.PasswordHash = util.LookupEnvOrString(util.PasswordHashEnvVar, "")
|
user.PasswordHash = util.LookupEnvOrString(util.PasswordHashEnvVar, "")
|
||||||
if user.PasswordHash == "" {
|
if user.PasswordHash == "" {
|
||||||
plaintext := util.LookupEnvOrString(util.PasswordEnvVar, util.DefaultPassword)
|
user.PasswordHash = util.LookupEnvOrFile(util.PasswordHashFileEnvVar, "")
|
||||||
hash, err := util.HashPassword(plaintext)
|
if user.PasswordHash == "" {
|
||||||
if err != nil {
|
plaintext := util.LookupEnvOrString(util.PasswordEnvVar, util.DefaultPassword)
|
||||||
return err
|
if plaintext == util.DefaultPassword {
|
||||||
|
plaintext = util.LookupEnvOrFile(util.PasswordFileEnvVar, util.DefaultPassword)
|
||||||
|
}
|
||||||
|
hash, err := util.HashPassword(plaintext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.PasswordHash = hash
|
||||||
}
|
}
|
||||||
user.PasswordHash = hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
o.conn.Write("users", user.Username, user)
|
o.conn.Write("users", user.Username, user)
|
||||||
|
err = util.ManagePerms(path.Join(path.Join(o.dbPath, "users"), user.Username+".json"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser func to query user info from the database
|
|
||||||
func (o *JsonDB) GetUser() (model.User, error) {
|
|
||||||
user := model.User{}
|
|
||||||
return user, o.conn.Read("server", "users", &user)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsers func to get all users from the database
|
// GetUsers func to get all users from the database
|
||||||
func (o *JsonDB) GetUsers() ([]model.User, error) {
|
func (o *JsonDB) GetUsers() ([]model.User, error) {
|
||||||
var users []model.User
|
var users []model.User
|
||||||
|
@ -175,7 +196,13 @@ func (o *JsonDB) GetUserByName(username string) (model.User, error) {
|
||||||
|
|
||||||
// SaveUser func to save user in the database
|
// SaveUser func to save user in the database
|
||||||
func (o *JsonDB) SaveUser(user model.User) error {
|
func (o *JsonDB) SaveUser(user model.User) error {
|
||||||
return o.conn.Write("users", user.Username, user)
|
userPath := path.Join(path.Join(o.dbPath, "users"), user.Username+".json")
|
||||||
|
output := o.conn.Write("users", user.Username, user)
|
||||||
|
err := util.ManagePerms(userPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteUser func to remove user from the database
|
// DeleteUser func to remove user from the database
|
||||||
|
@ -285,7 +312,13 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) SaveClient(client model.Client) error {
|
func (o *JsonDB) SaveClient(client model.Client) error {
|
||||||
return o.conn.Write("clients", client.ID, client)
|
clientPath := path.Join(path.Join(o.dbPath, "clients"), client.ID+".json")
|
||||||
|
output := o.conn.Write("clients", client.ID, client)
|
||||||
|
err := util.ManagePerms(clientPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) DeleteClient(clientID string) error {
|
func (o *JsonDB) DeleteClient(clientID string) error {
|
||||||
|
@ -293,15 +326,33 @@ func (o *JsonDB) DeleteClient(clientID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) SaveServerInterface(serverInterface model.ServerInterface) error {
|
func (o *JsonDB) SaveServerInterface(serverInterface model.ServerInterface) error {
|
||||||
return o.conn.Write("server", "interfaces", serverInterface)
|
serverInterfacePath := path.Join(path.Join(o.dbPath, "server"), "interfaces.json")
|
||||||
|
output := o.conn.Write("server", "interfaces", serverInterface)
|
||||||
|
err := util.ManagePerms(serverInterfacePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error {
|
func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error {
|
||||||
return o.conn.Write("server", "keypair", serverKeyPair)
|
serverKeyPairPath := path.Join(path.Join(o.dbPath, "server"), "keypair.json")
|
||||||
|
output := o.conn.Write("server", "keypair", serverKeyPair)
|
||||||
|
err := util.ManagePerms(serverKeyPairPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
|
func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
|
||||||
return o.conn.Write("server", "global_settings", globalSettings)
|
globalSettingsPath := path.Join(path.Join(o.dbPath, "server"), "global_settings.json")
|
||||||
|
output := o.conn.Write("server", "global_settings", globalSettings)
|
||||||
|
err := util.ManagePerms(globalSettingsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) GetPath() string {
|
func (o *JsonDB) GetPath() string {
|
||||||
|
@ -314,5 +365,11 @@ func (o *JsonDB) GetHashes() (model.ClientServerHashes, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) SaveHashes(hashes model.ClientServerHashes) error {
|
func (o *JsonDB) SaveHashes(hashes model.ClientServerHashes) error {
|
||||||
return o.conn.Write("server", "hashes", hashes)
|
hashesPath := path.Join(path.Join(o.dbPath, "server"), "hashes.json")
|
||||||
|
output := o.conn.Write("server", "hashes", hashes)
|
||||||
|
err := util.ManagePerms(hashesPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ package jsondb
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||||
|
"github.com/ngoduykhanh/wireguard-ui/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (o *JsonDB) GetWakeOnLanHosts() ([]model.WakeOnLanHost, error) {
|
func (o *JsonDB) GetWakeOnLanHosts() ([]model.WakeOnLanHost, error) {
|
||||||
|
@ -65,7 +68,15 @@ func (o *JsonDB) SaveWakeOnLanHost(host model.WakeOnLanHost) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return o.conn.Write(model.WakeOnLanHostCollectionName, resourceName, host)
|
wakeOnLanHostPath := path.Join(path.Join(o.dbPath, model.WakeOnLanHostCollectionName), resourceName+".json")
|
||||||
|
output := o.conn.Write(model.WakeOnLanHostCollectionName, resourceName, host)
|
||||||
|
err = util.ManagePerms(wakeOnLanHostPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *JsonDB) DeleteWakeOnHost(host model.WakeOnLanHost) error {
|
func (o *JsonDB) DeleteWakeOnHost(host model.WakeOnLanHost) error {
|
||||||
|
|
|
@ -232,6 +232,10 @@
|
||||||
</label>
|
</label>
|
||||||
<input type="text" data-role="tagsinput" class="form-control" id="client_extra_allowed_ips" value="{{ StringsJoin .client_defaults.ExtraAllowedIps "," }}">
|
<input type="text" data-role="tagsinput" class="form-control" id="client_extra_allowed_ips" value="{{ StringsJoin .client_defaults.ExtraAllowedIps "," }}">
|
||||||
</div>
|
</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="form-group">
|
||||||
<div class="icheck-primary d-inline">
|
<div class="icheck-primary d-inline">
|
||||||
<input type="checkbox" id="use_server_dns" {{ if .client_defaults.UseServerDNS }}checked{{ end }}>
|
<input type="checkbox" id="use_server_dns" {{ if .client_defaults.UseServerDNS }}checked{{ end }}>
|
||||||
|
@ -413,6 +417,7 @@
|
||||||
const email = $("#client_email").val();
|
const email = $("#client_email").val();
|
||||||
const allocated_ips = $("#client_allocated_ips").val().split(",");
|
const allocated_ips = $("#client_allocated_ips").val().split(",");
|
||||||
const allowed_ips = $("#client_allowed_ips").val().split(",");
|
const allowed_ips = $("#client_allowed_ips").val().split(",");
|
||||||
|
const endpoint = $("#client_endpoint").val();
|
||||||
let use_server_dns = false;
|
let use_server_dns = false;
|
||||||
let extra_allowed_ips = [];
|
let extra_allowed_ips = [];
|
||||||
|
|
||||||
|
@ -434,7 +439,7 @@
|
||||||
const preshared_key = $("#client_preshared_key").val();
|
const preshared_key = $("#client_preshared_key").val();
|
||||||
|
|
||||||
const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips,
|
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};
|
"public_key": public_key, "preshared_key": preshared_key};
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -492,6 +497,7 @@
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -503,6 +509,7 @@
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -513,6 +520,7 @@
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,10 @@ Wireguard Clients
|
||||||
<input type="text" data-role="tagsinput" class="form-control"
|
<input type="text" data-role="tagsinput" class="form-control"
|
||||||
id="_client_extra_allowed_ips">
|
id="_client_extra_allowed_ips">
|
||||||
</div>
|
</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="form-group">
|
||||||
<div class="icheck-primary d-inline">
|
<div class="icheck-primary d-inline">
|
||||||
<input type="checkbox" id="_use_server_dns">
|
<input type="checkbox" id="_use_server_dns">
|
||||||
|
@ -129,6 +133,26 @@ Wireguard Clients
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<details>
|
||||||
|
<summary><strong>Public and Preshared Keys</strong>
|
||||||
|
<i class="fas fa-info-circle" data-toggle="tooltip"
|
||||||
|
data-original-title="Update the server stored
|
||||||
|
client Public and Preshared keys.">
|
||||||
|
</i>
|
||||||
|
</summary>
|
||||||
|
<div class="form-group" style="margin-top: 1rem">
|
||||||
|
<label for="_client_public_key" class="control-label">
|
||||||
|
Public Key
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control" id="_client_public_key" name="_client_public_key" aria-invalid="false">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="_client_preshared_key" class="control-label">
|
||||||
|
Preshared Key
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control" id="_client_preshared_key" name="_client_preshared_key">
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer justify-content-between">
|
<div class="modal-footer justify-content-between">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
@ -388,6 +412,7 @@ Wireguard Clients
|
||||||
|
|
||||||
|
|
||||||
// Edit client modal event
|
// Edit client modal event
|
||||||
|
// This fills the modal dialogue with data from the DB when we open the edit menu
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$("#modal_edit_client").on('show.bs.modal', function (event) {
|
$("#modal_edit_client").on('show.bs.modal', function (event) {
|
||||||
let modal = $(this);
|
let modal = $(this);
|
||||||
|
@ -402,6 +427,7 @@ Wireguard Clients
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -413,6 +439,7 @@ Wireguard Clients
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -423,6 +450,7 @@ Wireguard Clients
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace' : true,
|
'removeWithBackspace' : true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -456,8 +484,13 @@ Wireguard Clients
|
||||||
modal.find("#_client_extra_allowed_ips").addTag(obj);
|
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("#_use_server_dns").prop("checked", client.use_server_dns);
|
||||||
modal.find("#_enabled").prop("checked", client.enabled);
|
modal.find("#_enabled").prop("checked", client.enabled);
|
||||||
|
|
||||||
|
modal.find("#_client_public_key").val(client.public_key);
|
||||||
|
modal.find("#_client_preshared_key").val(client.preshared_key);
|
||||||
},
|
},
|
||||||
error: function (jqXHR, exception) {
|
error: function (jqXHR, exception) {
|
||||||
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
|
@ -523,6 +556,8 @@ Wireguard Clients
|
||||||
}
|
}
|
||||||
|
|
||||||
// submitEditClient function for updating an existing client
|
// submitEditClient function for updating an existing client
|
||||||
|
// This sends dialogue data to the back-end when user presses "Save"
|
||||||
|
// See e.g. routes.go:UpdateClient for where data is processed/verified.
|
||||||
function submitEditClient() {
|
function submitEditClient() {
|
||||||
const client_id = $("#_client_id").val();
|
const client_id = $("#_client_id").val();
|
||||||
const name = $("#_client_name").val();
|
const name = $("#_client_name").val();
|
||||||
|
@ -531,11 +566,15 @@ Wireguard Clients
|
||||||
const allowed_ips = $("#_client_allowed_ips").val().split(",");
|
const allowed_ips = $("#_client_allowed_ips").val().split(",");
|
||||||
let use_server_dns = false;
|
let use_server_dns = false;
|
||||||
let extra_allowed_ips = [];
|
let extra_allowed_ips = [];
|
||||||
|
const public_key = $("#_client_public_key").val();
|
||||||
|
const preshared_key = $("#_client_preshared_key").val();
|
||||||
|
|
||||||
if( $("#_client_extra_allowed_ips").val() !== "" ) {
|
if( $("#_client_extra_allowed_ips").val() !== "" ) {
|
||||||
extra_allowed_ips = $("#_client_extra_allowed_ips").val().split(",");
|
extra_allowed_ips = $("#_client_extra_allowed_ips").val().split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const endpoint = $("#_client_endpoint").val();
|
||||||
|
|
||||||
if ($("#_use_server_dns").is(':checked')){
|
if ($("#_use_server_dns").is(':checked')){
|
||||||
use_server_dns = true;
|
use_server_dns = true;
|
||||||
}
|
}
|
||||||
|
@ -547,7 +586,8 @@ Wireguard Clients
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips,
|
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};
|
"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({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
|
|
|
@ -203,6 +203,7 @@ Global Settings
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,12 @@ Wireguard Server Settings
|
||||||
<textarea class="form-control" id="post_up" name="post_up"
|
<textarea class="form-control" id="post_up" name="post_up"
|
||||||
placeholder="Post Up Script">{{ .serverInterface.PostUp }}</textarea>
|
placeholder="Post Up Script">{{ .serverInterface.PostUp }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pre_down">Pre Down Script</label>
|
||||||
|
<input type="text" class="form-control" id="pre_down" name="pre_down"
|
||||||
|
placeholder="Pre Down Script" value="{{ .serverInterface.PreDown }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="post_down">Post Down Script</label>
|
<label for="post_down">Post Down Script</label>
|
||||||
<textarea class="form-control" id="post_down" name="post_down"
|
<textarea class="form-control" id="post_down" name="post_down"
|
||||||
|
@ -130,8 +136,9 @@ Wireguard Server Settings
|
||||||
const addresses = $("#addresses").val().split(",");
|
const addresses = $("#addresses").val().split(",");
|
||||||
const listen_port = $("#listen_port").val();
|
const listen_port = $("#listen_port").val();
|
||||||
const post_up = $("#post_up").val();
|
const post_up = $("#post_up").val();
|
||||||
|
const pre_down = $("#pre_down").val();
|
||||||
const post_down = $("#post_down").val();
|
const post_down = $("#post_down").val();
|
||||||
const data = {"addresses": addresses, "listen_port": listen_port, "post_up": post_up, "post_down": post_down};
|
const data = {"addresses": addresses, "listen_port": listen_port, "post_up": post_up, "pre_down": pre_down, "post_down": post_down};
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
|
@ -160,6 +167,7 @@ Wireguard Server Settings
|
||||||
'defaultText': 'Add More',
|
'defaultText': 'Add More',
|
||||||
'removeWithBackspace': true,
|
'removeWithBackspace': true,
|
||||||
'minChars': 0,
|
'minChars': 0,
|
||||||
|
'minInputWidth': '100%',
|
||||||
'placeholderColor': '#666666'
|
'placeholderColor': '#666666'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ ListenPort = {{ .serverConfig.Interface.ListenPort }}
|
||||||
PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }}
|
PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }}
|
||||||
{{if .globalSettings.MTU}}MTU = {{ .globalSettings.MTU }}{{end}}
|
{{if .globalSettings.MTU}}MTU = {{ .globalSettings.MTU }}{{end}}
|
||||||
PostUp = {{ .serverConfig.Interface.PostUp }}
|
PostUp = {{ .serverConfig.Interface.PostUp }}
|
||||||
|
PreDown = {{ .serverConfig.Interface.PreDown }}
|
||||||
PostDown = {{ .serverConfig.Interface.PostDown }}
|
PostDown = {{ .serverConfig.Interface.PostDown }}
|
||||||
Table = {{ .globalSettings.Table }}
|
Table = {{ .globalSettings.Table }}
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ Table = {{ .globalSettings.Table }}
|
||||||
# Update at: {{ .Client.UpdatedAt }}
|
# Update at: {{ .Client.UpdatedAt }}
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = {{ .Client.PublicKey }}
|
PublicKey = {{ .Client.PublicKey }}
|
||||||
{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}
|
{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}{{end}}
|
||||||
{{end}}AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}}
|
AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}}{{end}}
|
||||||
{{end}}{{end}}
|
{{if .Client.Endpoint }}Endpoint = {{ .Client.Endpoint }}{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
|
@ -30,12 +30,14 @@ const (
|
||||||
DefaultDNS = "1.1.1.1"
|
DefaultDNS = "1.1.1.1"
|
||||||
DefaultMTU = 1450
|
DefaultMTU = 1450
|
||||||
DefaultPersistentKeepalive = 15
|
DefaultPersistentKeepalive = 15
|
||||||
DefaultFirewallMark = "0xca6c" // i.e. 51820
|
DefaultFirewallMark = "0xca6c" // i.e. 51820
|
||||||
DefaultTable = "auto"
|
DefaultTable = "auto"
|
||||||
DefaultConfigFilePath = "/etc/wireguard/wg0.conf"
|
DefaultConfigFilePath = "/etc/wireguard/wg0.conf"
|
||||||
UsernameEnvVar = "WGUI_USERNAME"
|
UsernameEnvVar = "WGUI_USERNAME"
|
||||||
PasswordEnvVar = "WGUI_PASSWORD"
|
PasswordEnvVar = "WGUI_PASSWORD"
|
||||||
|
PasswordFileEnvVar = "WGUI_PASSWORD_FILE"
|
||||||
PasswordHashEnvVar = "WGUI_PASSWORD_HASH"
|
PasswordHashEnvVar = "WGUI_PASSWORD_HASH"
|
||||||
|
PasswordHashFileEnvVar = "WGUI_PASSWORD_HASH_FILE"
|
||||||
FaviconFilePathEnvVar = "WGUI_FAVICON_FILE_PATH"
|
FaviconFilePathEnvVar = "WGUI_FAVICON_FILE_PATH"
|
||||||
EndpointAddressEnvVar = "WGUI_ENDPOINT_ADDRESS"
|
EndpointAddressEnvVar = "WGUI_ENDPOINT_ADDRESS"
|
||||||
DNSEnvVar = "WGUI_DNS"
|
DNSEnvVar = "WGUI_DNS"
|
||||||
|
|
36
util/util.go
36
util/util.go
|
@ -1,14 +1,14 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/store"
|
|
||||||
"golang.org/x/mod/sumdb/dirhash"
|
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -18,6 +18,9 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ngoduykhanh/wireguard-ui/store"
|
||||||
|
"golang.org/x/mod/sumdb/dirhash"
|
||||||
|
|
||||||
externalip "github.com/glendc/go-external-ip"
|
externalip "github.com/glendc/go-external-ip"
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||||
|
@ -465,6 +468,20 @@ func LookupEnvOrStrings(key string, defaultVal []string) []string {
|
||||||
return defaultVal
|
return defaultVal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LookupEnvOrFile(key string, defaultVal string) string {
|
||||||
|
if val, ok := os.LookupEnv(key); ok {
|
||||||
|
if file, err := os.Open(val); err == nil {
|
||||||
|
var content string
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
content += scanner.Text()
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
|
||||||
func StringFromEmbedFile(embed fs.FS, filename string) (string, error) {
|
func StringFromEmbedFile(embed fs.FS, filename string) (string, error) {
|
||||||
file, err := embed.Open(filename)
|
file, err := embed.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -529,3 +546,18 @@ func UpdateHashes(db store.IStore) error {
|
||||||
clientServerHashes.Client, clientServerHashes.Server = GetCurrentHash(db)
|
clientServerHashes.Client, clientServerHashes.Server = GetCurrentHash(db)
|
||||||
return db.SaveHashes(clientServerHashes)
|
return db.SaveHashes(clientServerHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RandomString(length int) string {
|
||||||
|
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[seededRand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ManagePerms(path string) error {
|
||||||
|
err := os.Chmod(path, 0600)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue