Merge remote-tracking branch 'origin/master' into clients-patch

This commit is contained in:
kevin 2022-12-21 10:46:09 +08:00
commit b37c0a7f5b
11 changed files with 157 additions and 106 deletions

View file

@ -62,17 +62,17 @@ FROM alpine:3.16
RUN addgroup -S wgui && \
adduser -S -D -G wgui wgui
RUN apk --no-cache add ca-certificates
RUN apk --no-cache add ca-certificates wireguard-tools jq
WORKDIR /app
RUN mkdir -p db
# Copy binary files
COPY --from=builder --chown=wgui:wgui /build/wg-ui /app
COPY --from=builder --chown=wgui:wgui /build/wg-ui .
RUN chmod +x wg-ui
COPY init.sh .
EXPOSE 5000/tcp
HEALTHCHECK CMD ["wget","--output-document=-","--quiet","--tries=1","http://127.0.0.1:5000/_health"]
ENTRYPOINT ["./wg-ui"]
ENTRYPOINT ["./init.sh"]

181
README.md
View file

@ -5,18 +5,21 @@
A web user interface to manage your WireGuard setup.
## Features
- Friendly UI
- Authentication
- Manage extra client's information (name, email, etc)
- Retrieve configs using QR code / file
- Manage extra client information (name, email, etc)
- Retrieve client config using QR code / file / email
![wireguard-ui 0.3.7](https://user-images.githubusercontent.com/37958026/177041280-e3e7ca16-d4cf-4e95-9920-68af15e780dd.png)
## Run WireGuard-UI
Default username and password are `admin`.
> ⚠The default username and password are `admin`. Please change it to secure your setup.
### Using binary file
Download the binary file from the release and run it with command:
Download the binary file from the release page and run it directly on the host machine
```
./wireguard-ui
@ -24,7 +27,9 @@ Download the binary file from the release and run it with command:
### Using docker compose
You can take a look at this example of [docker-compose.yml](https://github.com/ngoduykhanh/wireguard-ui/blob/master/docker-compose.yaml). Please adjust volume mount points to work with your setup. Then run it like below:
You can take a look at this example
of [docker-compose.yml](https://github.com/ngoduykhanh/wireguard-ui/blob/master/docker-compose.yaml). Please adjust
volume mount points to work with your setup. Then run it like below:
```
docker-compose up
@ -32,84 +37,82 @@ docker-compose up
Note:
- There is a Status option that needs docker to be able to access the network of the host in order to read the
wireguard interface stats. See the `cap_add` and `network_mode` options on the docker-compose.yaml
- Because the `network_mode` is set to `host`, we don't need to specify the exposed ports. The app will listen on port `5000` by default.
- There is a Status page that needs docker to be able to access the network of the host in order to read the
wireguard interface stats. See the `cap_add` and `network_mode` options on the docker-compose.yaml
- Similarly, the `WGUI_MANAGE_START` and `WGUI_MANAGE_RESTART` settings need the same access, in order to restart the
wireguard interface.
- Because the `network_mode` is set to `host`, we don't need to specify the exposed ports. The app will listen on
port `5000` by default.
## Environment Variables
| Variable | Description |
|-----------------------------|-----------------------------------------------------------------------------------------------------------------|
| `SESSION_SECRET` | Used to encrypt the session cookies. Set this to a random value. |
| `WGUI_USERNAME` | The username for the login page. (default `admin`) |
| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically. (default `admin`) |
| `WGUI_PASSWORD_HASH` | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`) |
| `WGUI_ENDPOINT_ADDRESS` | The default endpoint address used in global settings. (default is your public IP address) |
| `WGUI_DNS` | The default DNS servers (comma-separated-list) used in the global settings. (default `1.1.1.1`) |
| `WGUI_MTU` | The default MTU used in global settings. (default `1450`) |
| `WGUI_PERSISTENT_KEEPALIVE` | The default persistent keepalive for WireGuard in global settings. (default `15`) |
| `WGUI_FORWARD_MARK` | The default WireGuard forward mark. (default `0xca6c`) |
| `WGUI_CONFIG_FILE_PATH` | The default WireGuard config file path used in global settings. (default `/etc/wireguard/wg0.conf`) |
| `BASE_PATH` | Set this variable if you run wireguard-ui under a subpath of your reverse proxy virtual host (e.g. /wireguard)) |
| 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 |
| `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 | `admin` |
| `WGUI_PASSWORD` | The password for the user on the login page. Will be hashed automatically | `admin` |
| `WGUI_PASSWORD_HASH` | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`) | N/A |
| `WGUI_ENDPOINT_ADDRESS` | The default endpoint address used in global settings | Resolved to your public ip address |
| `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_FORWARD_MARK` | The default WireGuard forward mark | `0xca6c` |
| `WGUI_CONFIG_FILE_PATH` | The default WireGuard config file path used in global settings | `/etc/wireguard/wg0.conf` |
| `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: `SSL`, `SSLTLS`, `TLS`, `STARTTLS` | `STARTTLS` |
### Defaults for server configuration
These environment variables are used to control the default server settings used when initializing the database.
| Variable | Description |
|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| `WGUI_SERVER_INTERFACE_ADDRESSES` | The default interface addresses (comma-separated-list) for the WireGuard server configuration. (default `10.252.1.0/24`) |
| `WGUI_SERVER_LISTEN_PORT` | The default server listen port. (default `51820`) |
| `WGUI_SERVER_POST_UP_SCRIPT` | The default server post-up script. |
| `WGUI_SERVER_POST_DOWN_SCRIPT` | The default server post-down script. |
| Variable | Description | Default |
|-----------------------------------|-----------------------------------------------------------------------------------------------|-----------------|
| `WGUI_SERVER_INTERFACE_ADDRESSES` | The default interface addresses (comma-separated-list) for the WireGuard server configuration | `10.252.1.0/24` |
| `WGUI_SERVER_LISTEN_PORT` | The default server listen port | `51820` |
| `WGUI_SERVER_POST_UP_SCRIPT` | The default server post-up script | N/A |
| `WGUI_SERVER_POST_DOWN_SCRIPT` | The default server post-down script | N/A |
### Defaults for new clients
These environment variables are used to set the defaults used in `New Client` dialog.
| Variable | Description |
|---------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| `WGUI_DEFAULT_CLIENT_ALLOWED_IPS` | Comma-separated-list of CIDRs for the `Allowed IPs` field. (default `0.0.0.0/0`) |
| `WGUI_DEFAULT_CLIENT_EXTRA_ALLOWED_IPS` | Comma-separated-list of CIDRs for the `Extra Allowed IPs` field. (default empty) |
| `WGUI_DEFAULT_CLIENT_USE_SERVER_DNS` | Boolean value [`0`, `f`, `F`, `false`, `False`, `FALSE`, `1`, `t`, `T`, `true`, `True`, `TRUE`] (default `true`) |
| `WGUI_DEFAULT_CLIENT_ENABLE_AFTER_CREATION` | Boolean value [`0`, `f`, `F`, `false`, `False`, `FALSE`, `1`, `t`, `T`, `true`, `True`, `TRUE`] (default `true`) |
| Variable | Description | Default |
|---------------------------------------------|-------------------------------------------------------------------------------------------------|-------------|
| `WGUI_DEFAULT_CLIENT_ALLOWED_IPS` | Comma-separated-list of CIDRs for the `Allowed IPs` field. (default ) | `0.0.0.0/0` |
| `WGUI_DEFAULT_CLIENT_EXTRA_ALLOWED_IPS` | Comma-separated-list of CIDRs for the `Extra Allowed IPs` field. (default empty) | N/A |
| `WGUI_DEFAULT_CLIENT_USE_SERVER_DNS` | Boolean value [`0`, `f`, `F`, `false`, `False`, `FALSE`, `1`, `t`, `T`, `true`, `True`, `TRUE`] | `true` |
| `WGUI_DEFAULT_CLIENT_ENABLE_AFTER_CREATION` | Boolean value [`0`, `f`, `F`, `false`, `False`, `FALSE`, `1`, `t`, `T`, `true`, `True`, `TRUE`] | `true` |
### Email configuration
### Docker only
To use custom `wg.conf` template set the `WG_CONF_TEMPLATE` environment variable to a path to such file. Make sure `wireguard-ui` will be able to work with it - use [default template](templates/wg.conf) for reference.
These environment variables only apply to the docker container.
In order to sent the wireguard configuration to clients via email, set the following environment variables:
- using SendGrid API
```
SENDGRID_API_KEY: Your sendgrid api key
EMAIL_FROM_ADDRESS: the email address you registered on sendgrid
EMAIL_FROM_NAME: the sender's email address
```
- using SMTP
```
SMTP_HOSTNAME: The SMTP ip address or hostname
SMTP_PORT: the SMTP port
SMTP_USERNAME: the SMTP username to authenticate
SMTP_PASSWORD: the SMTP user password
SMTP_AUTH_TYPE: the authentication type. Possible values: PLAIN, LOGIN, NONE
SMTP_ENCRYPTION: the encryption method. Possible values: SSL, SSLTLS, TLS or STARTTLS (default)
EMAIL_FROM_ADDRESS: the sender's email address
EMAIL_FROM_NAME: the sender's name
```
| Variable | Description | Default |
|-----------------------|---------------------------------------------------------------|---------|
| `WGUI_MANAGE_START` | Start/stop WireGaurd when the container is started/stopped | `false` |
| `WGUI_MANAGE_RESTART` | Auto restart WireGuard when we Apply Config changes in the UI | `false` |
## Auto restart WireGuard daemon
WireGuard-UI only takes care of configuration generation. You can use systemd to watch for the changes and restart the service. Following is an example:
### systemd
WireGuard-UI only takes care of configuration generation. You can use systemd to watch for the changes and restart the
service. Following is an example:
Create /etc/systemd/system/wgui.service
### Using systemd
```
Create `/etc/systemd/system/wgui.service`
```bash
cd /etc/systemd/system/
cat << EOF > wgui.service
[Unit]
Description=Restart WireGuard
After=network.target
@ -120,11 +123,14 @@ ExecStart=/usr/bin/systemctl restart wg-quick@wg0.service
[Install]
RequiredBy=wgui.path
EOF
```
Create /etc/systemd/system/wgui.path
Create `/etc/systemd/system/wgui.path`
```
```bash
cd /etc/systemd/system/
cat << EOF > wgui.path
[Unit]
Description=Watch /etc/wireguard/wg0.conf for changes
@ -133,48 +139,68 @@ PathModified=/etc/wireguard/wg0.conf
[Install]
WantedBy=multi-user.target
EOF
```
Apply it
```
```sh
systemctl enable wgui.{path,service}
systemctl start wgui.{path,service}
```
### openrc
### Using openrc
Create and `chmod +x` /usr/local/bin/wgui
```
Create `/usr/local/bin/wgui` file and make it executable
```sh
cd /usr/local/bin/
cat << EOF > wgui
#!/bin/sh
wg-quick down wg0
wg-quick up wg0
EOF
chmod +x wgui
```
Create and `chmod +x` /etc/init.d/wgui
```
Create `/etc/init.d/wgui` file and make it executable
```sh
cd /etc/init.d/
cat << EOF > wgui
#!/sbin/openrc-run
command=/sbin/inotifyd
command_args="/usr/local/bin/wgui /etc/wireguard/wg0.conf:w"
pidfile=/run/${RC_SVCNAME}.pid
command_background=yes
EOF
chmod +x wgui
```
Apply it
```
```sh
rc-service wgui start
rc-update add wgui default
```
### Using Docker
Set `WGUI_MANAGE_RESTART=true` to manage Wireguard interface restarts.
Using `WGUI_MANAGE_START=true` can also replace the function of `wg-quick@wg0` service, to start Wireguard at boot, by
running the container with `restart: unless-stopped`. These settings can also pick up changes to Wireguard Config File
Path, after restarting the container. Please make sure you have `--cap-add=NET_ADMIN` in your container config to make
this
feature work.
## Build
### Build docker image
Go to the project root directory and run the following command:
```
```sh
docker build -t wireguard-ui .
```
@ -182,31 +208,30 @@ docker build -t wireguard-ui .
Prepare the assets directory
```
```sh
./prepare_assets.sh
```
Then you can embed resources by generating Go source code
```
```sh
rice embed-go
go build -o wireguard-ui
```
Or, append resources to executable as zip file
```
```sh
go build -o wireguard-ui
rice append --exec wireguard-ui
```
## Screenshot
![wireguard-ui 0.3.7](https://user-images.githubusercontent.com/37958026/177041280-e3e7ca16-d4cf-4e95-9920-68af15e780dd.png)
## License
MIT. See [LICENSE](https://github.com/ngoduykhanh/wireguard-ui/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

@ -16,6 +16,8 @@ services:
- WGUI_USERNAME=alpha
- WGUI_PASSWORD=this-unusual-password
- WG_CONF_TEMPLATE
- WGUI_MANAGE_START=false
- WGUI_MANAGE_RESTART=false
logging:
driver: json-file
options:

View file

@ -231,6 +231,9 @@ func NewClient(db store.IStore) echo.HandlerFunc {
})
}
client.PresharedKey = presharedKey.String()
} else if client.PresharedKey == "-" {
client.PresharedKey = ""
log.Infof("skipped PresharedKey generation for user: %v", client.Name)
} else {
_, err := wgtypes.ParseKey(client.PresharedKey)
if err != nil {
@ -253,7 +256,7 @@ func NewClient(db store.IStore) echo.HandlerFunc {
}
}
// EmailClient handler to sent the configuration via email
// EmailClient handler to send the configuration via email
func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc {
type clientIdEmailPayload struct {
ID string `json:"id"`
@ -282,17 +285,17 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
globalSettings, _ := db.GetGlobalSettings()
config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
cfg_att := emailer.Attachment{"wg0.conf", []byte(config)}
cfgAtt := emailer.Attachment{"wg0.conf", []byte(config)}
var attachments []emailer.Attachment
if clientData.Client.PrivateKey != "" {
qrdata, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(clientData.QRCode, "data:image/png;base64,"))
if err != nil {
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "decoding: " + err.Error()})
}
qr_att := emailer.Attachment{"wg.png", qrdata}
attachments = []emailer.Attachment{cfg_att, qr_att}
qrAtt := emailer.Attachment{"wg.png", qrdata}
attachments = []emailer.Attachment{cfgAtt, qrAtt}
} else {
attachments = []emailer.Attachment{cfg_att}
attachments = []emailer.Attachment{cfgAtt}
}
err = mailer.Send(
clientData.Client.Name,
@ -384,12 +387,12 @@ func SetClientStatus(db store.IStore) echo.HandlerFunc {
clientID := data["id"].(string)
status := data["status"].(bool)
clientdata, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
clientData, err := db.GetClientByID(clientID, model.QRCodeSettings{Enabled: false})
if err != nil {
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
}
client := *clientdata.Client
client := *clientData.Client
client.Enabled = status
if err := db.SaveClient(client); err != nil {
@ -557,7 +560,7 @@ func Status(db store.IStore) echo.HandlerFunc {
}
return func(c echo.Context) error {
wgclient, err := wgctrl.New()
wgClient, err := wgctrl.New()
if err != nil {
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
@ -566,7 +569,7 @@ func Status(db store.IStore) echo.HandlerFunc {
})
}
devices, err := wgclient.Devices()
devices, err := wgClient.Devices()
if err != nil {
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},

23
init.sh Executable file
View file

@ -0,0 +1,23 @@
#!/bin/bash
# extract wg config file path, or use default
conf="$(jq -r .config_file_path db/server/global_settings.json || echo /etc/wireguard/wg0.conf)"
# manage wireguard stop/start with the container
case $WGUI_MANAGE_START in (1|t|T|true|True|TRUE)
wg-quick up "$conf"
trap 'wg-quick down "$conf"' SIGTERM # catches container stop
esac
# manage wireguard restarts
case $WGUI_MANAGE_RESTART 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-quick down "$file"
wg-quick up "$file"
done &
esac
./wg-ui &
wait $!

View file

@ -31,7 +31,7 @@ var (
flagSmtpPort int = 25
flagSmtpUsername string
flagSmtpPassword string
flagSmtpAuthType string = "None"
flagSmtpAuthType string = "NONE"
flagSmtpNoTLSCheck bool = false
flagSmtpEncryption string = "STARTTLS"
flagSendgridApiKey string
@ -62,7 +62,7 @@ func init() {
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 : 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(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")

View file

@ -1,6 +1,6 @@
package model
// Defaults for creation of new clients used in the templates
// ClientDefaults Defaults for creation of new clients used in the templates
type ClientDefaults struct {
AllowedIps []string
ExtraAllowedIps []string

View file

@ -214,7 +214,7 @@
<summary><strong>Public and Preshared Keys</strong>
<i class="fas fa-info-circle" data-toggle="tooltip"
data-original-title="If you don't want to let the server generate and store the
client's private, you can manually specify its public and preshared key here
client's private key, you can manually specify its public and preshared key here
. Note: QR code will not be generated">
</i>
</summary>
@ -228,7 +228,7 @@
<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" placeholder="Autogenerated">
<input type="text" class="form-control" id="client_preshared_key" name="client_preshared_key" placeholder="Autogenerated - enter &quot;-&quot; to skip generation">
</div>
</details>
</div>

View file

@ -85,7 +85,7 @@
if (nextURL) {
window.location.href = nextURL;
} else {
window.location.href = '/wireguard/';
window.location.href = '/{{.basePath}}';
}
}
</script>

View file

@ -16,8 +16,6 @@ var (
SendgridApiKey string
EmailFrom string
EmailFromName string
EmailSubject string
EmailContent string
SessionSecret []byte
WgConfTemplate string
BasePath string

View file

@ -93,15 +93,15 @@ func BuildClientConfig(client model.Client, server model.Server, setting model.G
return strConfig
}
// Read the default values for creating a new client from the environment or use sane defaults
// ClientDefaultsFromEnv to read the default values for creating a new client from the environment or use sane defaults
func ClientDefaultsFromEnv() model.ClientDefaults {
client_defaults := model.ClientDefaults{}
client_defaults.AllowedIps = LookupEnvOrStrings(DefaultClientAllowedIpsEnvVar, []string{"0.0.0.0/0"})
client_defaults.ExtraAllowedIps = LookupEnvOrStrings(DefaultClientExtraAllowedIpsEnvVar, []string{})
client_defaults.UseServerDNS = LookupEnvOrBool(DefaultClientUseServerDNSEnvVar, true)
client_defaults.EnableAfterCreation = LookupEnvOrBool(DefaultClientEnableAfterCreationEnvVar, true)
clientDefaults := model.ClientDefaults{}
clientDefaults.AllowedIps = LookupEnvOrStrings(DefaultClientAllowedIpsEnvVar, []string{"0.0.0.0/0"})
clientDefaults.ExtraAllowedIps = LookupEnvOrStrings(DefaultClientExtraAllowedIpsEnvVar, []string{})
clientDefaults.UseServerDNS = LookupEnvOrBool(DefaultClientUseServerDNSEnvVar, true)
clientDefaults.EnableAfterCreation = LookupEnvOrBool(DefaultClientEnableAfterCreationEnvVar, true)
return client_defaults
return clientDefaults
}
// ValidateCIDR to validate a network CIDR