Include soft reload to not loosing connections, also update packages

This commit is contained in:
Michael Reber
2025-02-04 10:21:13 +01:00
parent 4375347556
commit 8e43d4fb51
7 changed files with 120 additions and 41 deletions

View File

@@ -2,11 +2,11 @@
# wireguard-manager
A web user interface to manage your WireGuard setup.
A web interface to manage WireGuard.
## Features
- Friendly UI
- User-Friendly UI
- Authentication
- Manage extra client information (name, email, etc.)
- Retrieve client config using QR code / file / email / Telegram
@@ -39,6 +39,7 @@ docker-compose up
| Variable | Description | Default |
|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------|
| `BASE_PATH` | Set this variable if you run wireguard-manager under a subpath of your reverse proxy virtual host (e.g. /wireguard) | N/A |
| `PROXY` | Use X-FORWARDED-FOR header for logging | `false`
| `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_FILE` | Optional filepath for the secret key used to encrypt the session cookies. Leave `SESSION_SECRET` blank to take effect | N/A |
@@ -105,6 +106,7 @@ These environment variables only apply to the docker container.
|-----------------------|---------------------------------------------------------------|---------|
| `WGUI_MANAGE_START` | Start/stop WireGuard when the container is started/stopped | `false` |
| `WGUI_MANAGE_RESTART` | Auto restart WireGuard when we Apply Config changes in the UI | `false` |
| `WGUI_MANAGE_RELOAD` | Auto reload WireGuard when we Apply Config changes in the UI | `false` |
## Auto restart WireGuard daemon
@@ -113,6 +115,72 @@ service. Following is an example:
### Using systemd
#### Create dedicated wireguard-ui user
```bash
useradd -m -r -s /bin/false -d /var/lib/wireguard-ui wireguard-ui
```
#### Create wireguard config file and set permission with Linux ACL
```bash
touch /etc/wireguard/wg0.conf
setfacl -m wireguard-ui:rw /etc/wireguard/wg0.conf
```
#### Create environment file for wireguard-ui
```/etc/wireguard-ui/environment.conf```
```env
BASE_PATH="/"
BIND_ADDRESS="127.0.0.1:5000"
SESSION_SECRET="veryS3cr3t"
WGUI_USERNAME="admin"
WGUI_PASSWORD="my+password"
WGUI_ENDPOINT_ADDRESS="vpn.example.com"
WGUI_DNS="1.1.1.1"
WGUI_MTU="1450"
WGUI_PERSISTENT_KEEPALIVE="15"
WGUI_CONFIG_FILE_PATH="/etc/wireguard/wg0.conf"
WGUI_LOG_LEVEL="DEBUG"
# WG_CONF_TEMPLATE=
# EMAIL_FROM_ADDRESS=
# EMAIL_FROM_NAME=
# SENDGRID_API_KEY=
# SMTP_HOSTNAME=
# SMTP_PORT=
# SMTP_USERNAME=
# SMTP_PASSWORD=
# SMTP_AUTH_TYPE=
# SMTP_ENCRYPTION=
```
#### Create systemd service for wireguard-ui
```/etc/systemd/system/wireguard-ui.service```
```bash
[Unit]
Description=WireGuard UI
ConditionPathExists=/var/lib/wireguard-ui
After=network.target
[Service]
Type=simple
User=wireguard-ui
Group=wireguard-ui
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_NET_RAW
AmbientCapabilities=CAP_DAC_READ_SEARCH CAP_NET_ADMIN CAP_NET_RAW
WorkingDirectory=/var/lib/wireguard-ui
EnvironmentFile=/etc/wireguard-ui/environment.conf
ExecStart=/usr/local/share/applications/wireguard-ui
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
```
#### TODO (maybe delete)
Create `/etc/systemd/system/wgui.service`
```bash
@@ -198,6 +266,10 @@ running the container with `restart: unless-stopped`. These settings can also pi
Path, after restarting the container. Please make sure you have `--cap-add=NET_ADMIN` in your container config to make
this feature work.
Set `WGUI_MANAGE_RELOAD=true` to manage WireGuard interface reload.
Using `WGUI_MANAGE_RELOAD=true` will use `wg syncconf wg0 /path/to/file` to update the WireGuard running-configuration
without restart. Please make sure you have `--cap-add=NET_ADMIN` in your container config to make this feature work.
## Build
### Build docker image
@@ -238,9 +310,3 @@ go build -o wireguard-manager
## License
MIT. See [LICENSE](https://github.com/swissmakers/wireguard-manager/blob/master/LICENSE).
## Support
If you like the project and want to support it, you can *buy me a coffee*
<a href="https://www.buymeacoffee.com/khanhngo" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>

View File

@@ -18,6 +18,13 @@ case $WGUI_MANAGE_RESTART in (1|t|T|true|True|TRUE)
done &
esac
# manage wireguard reload
case $WGUI_MANAGE_RELOAD in (1|t|T|true|True|TRUE)
[[ -f $conf ]] || touch "$conf" # inotifyd needs file to exist
inotifyd - "$conf":w | while read -r event file; do
wg syncconf wg0 <(wg-quick strip wg0)
done &
esac
./wg-ui &
wait $!

View File

@@ -30,9 +30,11 @@ var (
appVersion = "development"
gitCommit = "N/A"
gitRef = "N/A"
buildTime = fmt.Sprintf(time.Now().UTC().Format("01-02-2006 15:04:05"))
buildTime = time.Now().UTC().Format("01-02-2006 15:04:05")
// configuration variables
flagDisableLogin = false
flagProxy = false
flagBindAddress = "0.0.0.0:5000"
flagSmtpHostname = "127.0.0.1"
flagSmtpPort = 25
@@ -77,6 +79,7 @@ var embeddedAssets embed.FS
func init() {
// command-line flags and env variables
flag.BoolVar(&flagDisableLogin, "disable-login", util.LookupEnvOrBool("DISABLE_LOGIN", flagDisableLogin), "Disable authentication on the app. This is potentially dangerous.")
flag.BoolVar(&flagProxy, "proxy", util.LookupEnvOrBool("PROXY", flagProxy), "Behind a proxy. Use X-FORWARDED-FOR for failed login logging")
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")
@@ -126,6 +129,7 @@ func init() {
// update runtime config
util.DisableLogin = flagDisableLogin
util.Proxy = flagProxy
util.BindAddress = flagBindAddress
util.SmtpHostname = flagSmtpHostname
util.SmtpPort = flagSmtpPort

View File

@@ -1,10 +1,10 @@
{
"name": "wireguard-manager",
"version": "1.0.0",
"description": "Wireguard web interface",
"version": "0.0.1",
"description": "A Wireguard web-interface",
"main": "index.js",
"repository": "git@github.com:swissmakers/wireguard-manager.git",
"author": "Khanh Ngo <k@ndk.name>",
"author": "Swissmakers GmbH <info@swissmakers.ch>",
"license": "MIT",
"dependencies": {
"admin-lte": "^3.0",

View File

@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>WireGuard UI</title>
<title>Login</title>
<!-- Tell the browser to be responsive to screen width -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Favicon -->
@@ -25,7 +25,7 @@
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<a href="https://github.com/swissmakers/wireguard-manager">WireGuard UI</a>
<a href="#">Welcome</a>
</div>
<!-- /.login-logo -->
<div class="card">

View File

@@ -10,6 +10,7 @@ import (
// Runtime config
var (
DisableLogin bool
Proxy bool
BindAddress string
SmtpHostname string
SmtpPort int

View File

@@ -20,6 +20,7 @@ import (
"text/template"
"time"
"github.com/chmike/domain"
"github.com/skip2/go-qrcode"
"github.com/swissmakers/wireguard-manager/store"
"github.com/swissmakers/wireguard-manager/telegram"
@@ -117,10 +118,7 @@ func ContainsCIDR(ipnet1, ipnet2 *net.IPNet) bool {
// ValidateCIDR to validate a network CIDR
func ValidateCIDR(cidr string) bool {
_, _, err := net.ParseCIDR(cidr)
if err != nil {
return false
}
return true
return err == nil
}
// ValidateCIDRList to validate a list of network CIDR
@@ -128,12 +126,12 @@ func ValidateCIDRList(cidrs []string, allowEmpty bool) bool {
for _, cidr := range cidrs {
if allowEmpty {
if len(cidr) > 0 {
if ValidateCIDR(cidr) == false {
if !ValidateCIDR(cidr) {
return false
}
}
} else {
if ValidateCIDR(cidr) == false {
if !ValidateCIDR(cidr) {
return false
}
}
@@ -143,42 +141,45 @@ func ValidateCIDRList(cidrs []string, allowEmpty bool) bool {
// ValidateAllowedIPs to validate allowed ip addresses in CIDR format
func ValidateAllowedIPs(cidrs []string) bool {
if ValidateCIDRList(cidrs, false) == false {
return false
}
return true
return ValidateCIDRList(cidrs, false)
}
// ValidateExtraAllowedIPs to validate extra Allowed ip addresses, allowing empty strings
func ValidateExtraAllowedIPs(cidrs []string) bool {
if ValidateCIDRList(cidrs, true) == false {
return false
}
return true
return ValidateCIDRList(cidrs, true)
}
// ValidateServerAddresses to validate allowed ip addresses in CIDR format
func ValidateServerAddresses(cidrs []string) bool {
if ValidateCIDRList(cidrs, false) == false {
return false
}
return true
return ValidateCIDRList(cidrs, false)
}
// ValidateIPAddress to validate the IPv4 and IPv6 address
func ValidateIPAddress(ip string) bool {
if net.ParseIP(ip) == nil {
return false
}
return true
return net.ParseIP(ip) != nil
}
// ValidateIPAddressList to validate a list of IPv4 and IPv6 addresses
func ValidateIPAddressList(ips []string) bool {
for _, ip := range ips {
if ValidateIPAddress(ip) == false {
return false
// ValidateDomainName to validate domain name
func ValidateDomainName(name string) bool {
return domain.Check(name) == nil
}
// ValidateIPAndSearchDomainAddressList to validate a list of IPv4 and IPv6 addresses plus added search domains
func ValidateIPAndSearchDomainAddressList(entries []string) bool {
ip := false
domain := false
for _, entry := range entries {
// ip but not after domain
if ValidateIPAddress(entry) && !domain {
ip = true
continue
}
// domain and after ip
if ValidateDomainName(entry) && ip {
domain = true
continue
}
return false
}
return true
}