From 3024d36d761e680dc6d65ca9f7bf785fca7b5347 Mon Sep 17 00:00:00 2001 From: Cameron <113076756+cameronaw13@users.noreply.github.com> Date: Mon, 25 Dec 2023 10:58:31 -0800 Subject: [PATCH] env variable file support (#391) --- README.md | 59 +++++++++++++++++++++++------------------- main.go | 38 ++++++++++++++++++++++----- store/jsondb/jsondb.go | 17 ++++++++---- util/config.go | 4 ++- util/util.go | 20 ++++++++++++-- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 0da4652..f7c41f8 100644 --- a/README.md +++ b/README.md @@ -36,33 +36,38 @@ docker-compose up ## Environment Variables -| 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 | -| `BIND_ADDRESS` | The addresses that can access to the web interface and the port | 0.0.0.0:80 | -| `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` | -| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically. 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_ENDPOINT_ADDRESS` | The default endpoint address used in global settings where clients should connect to | Resolved to your public ip address | -| `WGUI_FAVICON_FILE_PATH` | The file path used as website favicon | Embedded WireGuard logo | -| `WGUI_DNS` | The default DNS servers (comma-separated-list) used in the global settings | `1.1.1.1` | -| `WGUI_MTU` | The default MTU used in global settings | `1450` | -| `WGUI_PERSISTENT_KEEPALIVE` | The default persistent keepalive for WireGuard in global settings | `15` | -| `WGUI_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) | -| `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 | -| `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 | -| `SMTP_HOSTNAME` | The SMTP IP address or hostname | `127.0.0.1` | -| `SMTP_PORT` | The SMTP port | `25` | -| `SMTP_USERNAME` | The SMTP username | N/A | -| `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` | +| 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 | +| `BIND_ADDRESS` | The addresses that can access to the web interface and the port | 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_FILE` | Optional filepath for the secret key used to encrypt the session cookies. Leave `SESSION_SECRET` blank to take effect | N/A | +| `WGUI_USERNAME` | The username for the login page. Used for db initialization only | `admin` | +| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically. Used for db initialization only | `admin` | +| `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_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_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_ENDPOINT_ADDRESS` | The default endpoint address used in global settings where clients should connect to | Resolved to your public ip address | +| `WGUI_FAVICON_FILE_PATH` | The file path used as website favicon | Embedded WireGuard logo | +| `WGUI_DNS` | The default DNS servers (comma-separated-list) used in the global settings | `1.1.1.1` | +| `WGUI_MTU` | The default MTU used in global settings | `1450` | +| `WGUI_PERSISTENT_KEEPALIVE` | The default persistent keepalive for WireGuard in global settings | `15` | +| `WGUI_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) | +| `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 | +| `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 | +| `SENDGRID_API_KEY_FILE` | Optional filepath for the SendGrid api key. Leave `SENDGRID_API_KEY` blank to take effect | N/A | +| `SMTP_HOSTNAME` | The SMTP IP address or hostname | `127.0.0.1` | +| `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 diff --git a/main.go b/main.go index 0131208..60c5c35 100644 --- a/main.go +++ b/main.go @@ -4,14 +4,15 @@ import ( "embed" "flag" "fmt" - "github.com/labstack/echo/v4" - "github.com/labstack/gommon/log" - "github.com/ngoduykhanh/wireguard-ui/store" "io/fs" "net/http" "os" "time" + "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/handler" "github.com/ngoduykhanh/wireguard-ui/router" @@ -70,16 +71,41 @@ func init() { 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(&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.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(&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(&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(&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() // update runtime config diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 0b674b0..4ccd9b7 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -128,13 +128,20 @@ func (o *JsonDB) Init() error { user.Admin = util.DefaultIsAdmin user.PasswordHash = util.LookupEnvOrString(util.PasswordHashEnvVar, "") if user.PasswordHash == "" { - plaintext := util.LookupEnvOrString(util.PasswordEnvVar, util.DefaultPassword) - hash, err := util.HashPassword(plaintext) - if err != nil { - return err + user.PasswordHash = util.LookupEnvOrFile(util.PasswordHashFileEnvVar, "") + if user.PasswordHash == "" { + plaintext := util.LookupEnvOrString(util.PasswordEnvVar, util.DefaultPassword) + 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) os.Chmod(path.Join(path.Join(o.dbPath, "users"), user.Username+".json"), 0600) } diff --git a/util/config.go b/util/config.go index 3c3bd18..6d5a8df 100644 --- a/util/config.go +++ b/util/config.go @@ -30,12 +30,14 @@ const ( DefaultDNS = "1.1.1.1" DefaultMTU = 1450 DefaultPersistentKeepalive = 15 - DefaultFirewallMark = "0xca6c" // i.e. 51820 + DefaultFirewallMark = "0xca6c" // i.e. 51820 DefaultTable = "auto" DefaultConfigFilePath = "/etc/wireguard/wg0.conf" UsernameEnvVar = "WGUI_USERNAME" PasswordEnvVar = "WGUI_PASSWORD" + PasswordFileEnvVar = "WGUI_PASSWORD_FILE" PasswordHashEnvVar = "WGUI_PASSWORD_HASH" + PasswordHashFileEnvVar = "WGUI_PASSWORD_HASH_FILE" FaviconFilePathEnvVar = "WGUI_FAVICON_FILE_PATH" EndpointAddressEnvVar = "WGUI_ENDPOINT_ADDRESS" DNSEnvVar = "WGUI_DNS" diff --git a/util/util.go b/util/util.go index 44f8f01..1203082 100644 --- a/util/util.go +++ b/util/util.go @@ -1,11 +1,10 @@ package util import ( + "bufio" "encoding/json" "errors" "fmt" - "github.com/ngoduykhanh/wireguard-ui/store" - "golang.org/x/mod/sumdb/dirhash" "io" "io/fs" "io/ioutil" @@ -19,6 +18,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" @@ -466,6 +468,20 @@ func LookupEnvOrStrings(key string, defaultVal []string) []string { 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) { file, err := embed.Open(filename) if err != nil {