mirror of
https://github.com/swissmakers/wireguard-manager.git
synced 2026-03-30 07:47:07 +02:00
Include soft reload to not loosing connections, also update packages
This commit is contained in:
82
README.md
82
README.md
@@ -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>
|
||||
|
||||
7
init.sh
7
init.sh
@@ -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 $!
|
||||
|
||||
6
main.go
6
main.go
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
// Runtime config
|
||||
var (
|
||||
DisableLogin bool
|
||||
Proxy bool
|
||||
BindAddress string
|
||||
SmtpHostname string
|
||||
SmtpPort int
|
||||
|
||||
55
util/util.go
55
util/util.go
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user