From 4ecafb3191441de130927270cfb81d5950540d90 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Mon, 12 Oct 2020 21:47:56 +0200 Subject: [PATCH 001/162] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f8d96a4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [ngoduykhanh] From 1fad3a6d073974e2689b744cb239653e3b4d1681 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Mon, 12 Oct 2020 22:49:42 +0200 Subject: [PATCH 002/162] Create stale.yml --- .github/stale.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..778659f --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,19 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - enhancement + - feature request +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From 68058a356e92815fdc9ab2fc76d95352859cc6da Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Mon, 12 Oct 2020 22:50:55 +0200 Subject: [PATCH 003/162] Update stale.yml --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index 778659f..a1b7aa1 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -16,4 +16,4 @@ markComment: > recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false +closeComment: true From e482fa09886e7eb7c5a0e8226bcdac1f0201f2ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Dec 2020 22:10:41 +0100 Subject: [PATCH 004/162] Bump ini from 1.3.5 to 1.3.8 (#44) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ffd4b12..b24634f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1513,9 +1513,9 @@ inherits@2, inherits@^2.0.3, inherits@~2.0.3: integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.5, ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inputmask@^5.0.3: version "5.0.3" From 872f0fbed5f95fdd50d504bc2ab0ab9863bcf3ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Mar 2021 20:01:48 +0100 Subject: [PATCH 005/162] Bump jquery-validation from 1.19.1 to 1.19.3 (#48) Bumps [jquery-validation](https://github.com/jquery-validation/jquery-validation) from 1.19.1 to 1.19.3. - [Release notes](https://github.com/jquery-validation/jquery-validation/releases) - [Changelog](https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md) - [Commits](https://github.com/jquery-validation/jquery-validation/compare/1.19.1...1.19.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b24634f..8fb8ca4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1667,9 +1667,9 @@ jquery-ui-dist@^1.12.1: integrity sha1-XAgV08xvkP9fqvWyaKbiO0ypBPo= jquery-validation@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.19.1.tgz#a85043467dc2b70d9fff05778646d150e747742f" - integrity sha512-QNnrZBqSltWUEJx+shOY5WtfrIb0gWmDjFfQP8rZKqMMSfpRSwEkSqhfHPvDfkObD8Hnv5KHSYI8yg73sVFdqA== + version "1.19.3" + resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.19.3.tgz#50b350eba8b02bcfd119ba15f199487b7eb64086" + integrity sha512-iXxCS5W7STthSTMFX/NDZfWHBLbJ1behVK3eAgHXAV8/0vRa9M4tiqHvJMr39VGWHMGdlkhrtrkBuaL2UlE8yw== jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.7, jquery@>=2.1.0, jquery@^3.0, "jquery@^3.0 || ^2.0 || ^1.0", jquery@^3.4.0, jquery@^3.4.1: version "3.5.0" From 024aadbfd2a48f03b833ec44d0593a95ccff0adc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Mar 2021 20:02:02 +0100 Subject: [PATCH 006/162] Bump ssri from 8.0.0 to 8.0.1 (#51) Bumps [ssri](https://github.com/npm/ssri) from 8.0.0 to 8.0.1. - [Release notes](https://github.com/npm/ssri/releases) - [Changelog](https://github.com/npm/ssri/blob/latest/CHANGELOG.md) - [Commits](https://github.com/npm/ssri/compare/v8.0.0...v8.0.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8fb8ca4..696a0ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2623,9 +2623,9 @@ sprintf-js@~1.0.2: integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= ssri@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" - integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA== + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== dependencies: minipass "^3.1.1" From 2fa4ca32ab25731d28905a8ce727c0d04ee8a51b Mon Sep 17 00:00:00 2001 From: Gerwim Date: Thu, 5 Aug 2021 19:46:11 +0200 Subject: [PATCH 007/162] Updated README (#80) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1c53cb5..94791b7 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ After=network.target [Service] Type=oneshot ExecStart=/usr/bin/systemctl restart wg-quick@wg0.service + +[Install] +RequiredBy=wgui.path ``` Create /etc/systemd/system/wgui.path From de0c9fd26b5a3d59dcc8b784b74c32a891151d6b Mon Sep 17 00:00:00 2001 From: Elijah Pavkin Date: Thu, 5 Aug 2021 20:50:31 +0300 Subject: [PATCH 008/162] Port forwarding for server (#69) --- util/util.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/util/util.go b/util/util.go index d98aaec..1d85d0d 100644 --- a/util/util.go +++ b/util/util.go @@ -6,12 +6,14 @@ import ( "fmt" "net" "os" + "strconv" "strings" "text/template" "time" rice "github.com/GeertJohan/go.rice" externalip "github.com/glendc/go-external-ip" + "github.com/labstack/gommon/log" "github.com/ngoduykhanh/wireguard-ui/model" "github.com/sdomino/scribble" ) @@ -27,7 +29,20 @@ func BuildClientConfig(client model.Client, server model.Server, setting model.G peerPublicKey := fmt.Sprintf("PublicKey = %s", server.KeyPair.PublicKey) peerPresharedKey := fmt.Sprintf("PresharedKey = %s", client.PresharedKey) peerAllowedIPs := fmt.Sprintf("AllowedIPs = %s", strings.Join(client.AllowedIPs, ",")) - peerEndpoint := fmt.Sprintf("Endpoint = %s:%d", setting.EndpointAddress, server.Interface.ListenPort) + + desiredHost := setting.EndpointAddress + desiredPort := server.Interface.ListenPort + if strings.Contains(desiredHost, ":") { + split := strings.Split(desiredHost, ":") + desiredHost = split[0] + if n, err := strconv.Atoi(split[1]); err == nil { + desiredPort = n + } else { + log.Error("Endpoint appears to be incorrectly formated: ", err) + } + } + peerEndpoint := fmt.Sprintf("Endpoint = %s:%d", desiredHost, desiredPort) + peerPersistentKeepalive := fmt.Sprintf("PersistentKeepalive = %d", setting.PersistentKeepalive) // build the config as string From 2aa042b919e115b12b5777ab6718099a2756f10d Mon Sep 17 00:00:00 2001 From: Gerwim Date: Thu, 5 Aug 2021 19:58:01 +0200 Subject: [PATCH 009/162] Added UseServerDNS option for clients who do not have to use the DNS specified in the server configuration. (#79) --- custom/js/helper.js | 2 ++ go.mod | 1 + go.sum | 15 +++++++++------ handler/routes.go | 1 + model/client.go | 1 + templates/base.html | 16 +++++++++++++++- templates/clients.html | 17 ++++++++++++++++- util/util.go | 5 ++++- 8 files changed, 49 insertions(+), 9 deletions(-) diff --git a/custom/js/helper.js b/custom/js/helper.js index 42bc17a..a624591 100644 --- a/custom/js/helper.js +++ b/custom/js/helper.js @@ -46,6 +46,8 @@ function renderClientList(data) { ${obj.Client.created_at} ${obj.Client.updated_at} + + ${obj.Client.use_server_dns ? 'DNS enabled' : 'DNS disabled'} IP Allocation` + allocatedIpsHtml + `Allowed IPs` diff --git a/go.mod b/go.mod index a932024..1678c52 100644 --- a/go.mod +++ b/go.mod @@ -16,5 +16,6 @@ require ( github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.31.0 ) diff --git a/go.sum b/go.sum index cd95b58..9a0e435 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -16,6 +14,7 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -36,6 +35,7 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -47,7 +47,6 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= @@ -56,7 +55,6 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= github.com/labstack/echo-contrib v0.9.0 h1:hKBA2SnxdxR7sghH0J04zq/pImnKRmgvmQ6MvY9hug4= github.com/labstack/echo-contrib v0.9.0/go.mod h1:TsFE5Vv0LRpZLoh4mMmaaAxzcTH+1CBFiUtVhwlegzU= github.com/labstack/echo/v4 v4.1.6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE= @@ -85,11 +83,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -113,6 +111,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber/jaeger-client-go v2.19.1-0.20191002155754-0be28c34dabf+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= @@ -167,16 +166,20 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= -golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf h1:rWUZHukj3poXegPQMZOXgxjTGIBe3mLNHNVvL5DsHus= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/handler/routes.go b/handler/routes.go index 28bc576..d3029e1 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -241,6 +241,7 @@ func UpdateClient() echo.HandlerFunc { client.Name = _client.Name client.Email = _client.Email client.Enabled = _client.Enabled + client.UseServerDNS = _client.UseServerDNS client.AllocatedIPs = _client.AllocatedIPs client.AllowedIPs = _client.AllowedIPs client.UpdatedAt = time.Now().UTC() diff --git a/model/client.go b/model/client.go index 913d5f1..8e6ba52 100644 --- a/model/client.go +++ b/model/client.go @@ -14,6 +14,7 @@ type Client struct { Email string `json:"email"` AllocatedIPs []string `json:"allocated_ips"` AllowedIPs []string `json:"allowed_ips"` + UseServerDNS bool `json:"use_server_dns"` Enabled bool `json:"enabled"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/templates/base.html b/templates/base.html index 314c200..970dff5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -155,6 +155,14 @@ +
+
+ + +
+
@@ -276,6 +284,12 @@ const email = $("#client_email").val(); const allocated_ips = $("#client_allocated_ips").val().split(","); const allowed_ips = $("#client_allowed_ips").val().split(","); + let use_server_dns = false; + + if ($("#use_server_dns").is(':checked')){ + use_server_dns = true; + } + let enabled = false; if ($("#enabled").is(':checked')){ @@ -283,7 +297,7 @@ } const data = {"name": name, "email": email, "allocated_ips": allocated_ips, "allowed_ips": allowed_ips, - "enabled": enabled}; + "use_server_dns": use_server_dns, "enabled": enabled}; $.ajax({ cache: false, diff --git a/templates/clients.html b/templates/clients.html index 689d5d2..fd5a443 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -58,6 +58,14 @@ Wireguard Clients
+
+
+ + +
+
@@ -291,6 +299,7 @@ Wireguard Clients modal.find("#_client_allowed_ips").addTag(obj); }); + modal.find("#_use_server_dns").prop("checked", client.use_server_dns); modal.find("#_enabled").prop("checked", client.enabled); }, error: function (jqXHR, exception) { @@ -308,6 +317,12 @@ Wireguard Clients const email = $("#_client_email").val(); const allocated_ips = $("#_client_allocated_ips").val().split(","); const allowed_ips = $("#_client_allowed_ips").val().split(","); + let use_server_dns = false; + + if ($("#_use_server_dns").is(':checked')){ + use_server_dns = true; + } + let enabled = false; if ($("#_enabled").is(':checked')){ @@ -315,7 +330,7 @@ Wireguard Clients } const data = {"id": client_id, "name": name, "email": email, "allocated_ips": allocated_ips, - "allowed_ips": allowed_ips, "enabled": enabled}; + "allowed_ips": allowed_ips, "use_server_dns": use_server_dns, "enabled": enabled}; $.ajax({ cache: false, diff --git a/util/util.go b/util/util.go index 1d85d0d..09e5fd9 100644 --- a/util/util.go +++ b/util/util.go @@ -23,7 +23,10 @@ func BuildClientConfig(client model.Client, server model.Server, setting model.G // Interface section clientAddress := fmt.Sprintf("Address = %s", strings.Join(client.AllocatedIPs, ",")) clientPrivateKey := fmt.Sprintf("PrivateKey = %s", client.PrivateKey) - clientDNS := fmt.Sprintf("DNS = %s", strings.Join(setting.DNSServers, ",")) + clientDNS := "" + if client.UseServerDNS { + clientDNS = fmt.Sprintf("DNS = %s", strings.Join(setting.DNSServers, ",")) + } // Peer section peerPublicKey := fmt.Sprintf("PublicKey = %s", server.KeyPair.PublicKey) From 854a9fdde92fe6603a967077a3767c99dfc42ed8 Mon Sep 17 00:00:00 2001 From: Alan Scherger Date: Thu, 5 Aug 2021 13:07:42 -0500 Subject: [PATCH 010/162] remove maxchars for copy+paste (#58) --- templates/base.html | 2 -- templates/clients.html | 2 -- templates/global_settings.html | 1 - templates/server.html | 1 - 4 files changed, 6 deletions(-) diff --git a/templates/base.html b/templates/base.html index 970dff5..3ba800c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -354,7 +354,6 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); @@ -366,7 +365,6 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); diff --git a/templates/clients.html b/templates/clients.html index fd5a443..b4e26bd 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -258,7 +258,6 @@ Wireguard Clients 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); @@ -270,7 +269,6 @@ Wireguard Clients 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); diff --git a/templates/global_settings.html b/templates/global_settings.html index b24a48d..6eb1393 100644 --- a/templates/global_settings.html +++ b/templates/global_settings.html @@ -154,7 +154,6 @@ Global Settings 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); diff --git a/templates/server.html b/templates/server.html index a93fed8..5a7c5a4 100644 --- a/templates/server.html +++ b/templates/server.html @@ -160,7 +160,6 @@ Wireguard Server Settings 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'maxChars': 18, 'placeholderColor': '#666666' }); From 7edcd1b80cc7229c030cb16cf6202e3a4fd32be1 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Thu, 5 Aug 2021 20:46:23 +0200 Subject: [PATCH 011/162] Fix release workflows --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12b71ab..976c271 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,9 +28,9 @@ jobs: # set environment - name: Set APP_VERSION env - run: echo ::set-env name=APP_VERSION::$(echo ${GITHUB_REF} | rev | cut -d'/' -f 1 | rev ) + run: echo "APP_VERSION=$(echo ${GITHUB_REF} | rev | cut -d'/' -f 1 | rev )" >> $GITHUB_ENV - name: Set BUILD_TIME env - run: echo ::set-env name=BUILD_TIME::$(date) + run: echo "BUILD_TIME=$(date)" >> $GITHUB_ENV - name: Environment Printer uses: managedkaos/print-env@v1.0 From 1711530ddad3a442b4a5028dd89e12b6654dcea4 Mon Sep 17 00:00:00 2001 From: Georgios Komninos Date: Sun, 8 Aug 2021 20:55:59 +0300 Subject: [PATCH 012/162] Fixes security issue & Adds support to sent configuration via email (#83) --- README.md | 12 ++++ custom/js/helper.js | 3 + docker-compose.yaml | 8 ++- emailer/interface.go | 10 +++ emailer/sendgrid.go | 54 ++++++++++++++++ go.mod | 2 + go.sum | 4 ++ handler/routes.go | 76 +++++++++++++--------- handler/session.go | 28 +++++--- main.go | 55 ++++++++++------ router/router.go | 6 +- templates/clients.html | 143 +++++++++++++++++++++++++++++++++++++---- util/config.go | 10 ++- 13 files changed, 335 insertions(+), 76 deletions(-) create mode 100644 emailer/interface.go create mode 100644 emailer/sendgrid.go diff --git a/README.md b/README.md index 94791b7..2d4713b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,18 @@ You can take a look at this example of [docker-compose.yml](https://github.com/n ``` docker-compose up ``` +### Environment Variables + + +Set the `SESSION_SECRET` environment variable to a random value. + +In order to sent the wireguard configuration to clients via email (using sendgrid api) set the following environment variables + +``` +SENDGRID_API_KEY: Your sendgrid api key +EMAIL_FROM: the email address you registered on sendgrid +EMAIL_FROM_NAME: the sender's email address +``` ### Using binary file diff --git a/custom/js/helper.js b/custom/js/helper.js index a624591..feab9ac 100644 --- a/custom/js/helper.js +++ b/custom/js/helper.js @@ -29,6 +29,9 @@ function renderClientList(data) {
+ diff --git a/docker-compose.yaml b/docker-compose.yaml index 72da096..1494a72 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,8 +2,14 @@ version: '3' services: wg: - image: ngoduykhanh/wireguard-ui:latest + build: . + #image: ngoduykhanh/wireguard-ui:latest container_name: wgui + environment: + - SENDGRID_API_KEY + - EMAIL_FROM + - EMAIL_FROM_NAME + - SESSION_SECRET ports: - 5000:5000 logging: diff --git a/emailer/interface.go b/emailer/interface.go new file mode 100644 index 0000000..5a486fc --- /dev/null +++ b/emailer/interface.go @@ -0,0 +1,10 @@ +package emailer + +type Attachment struct { + Name string + Data []byte +} + +type Emailer interface { + Send(toName string, to string, subject string, content string, attachments []Attachment) error +} diff --git a/emailer/sendgrid.go b/emailer/sendgrid.go new file mode 100644 index 0000000..864c953 --- /dev/null +++ b/emailer/sendgrid.go @@ -0,0 +1,54 @@ +package emailer + +import ( + "encoding/base64" + + "github.com/sendgrid/sendgrid-go" + "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +type SendgridApiMail struct { + apiKey string + fromName string + from string +} + +func NewSendgridApiMail(apiKey, fromName, from string) *SendgridApiMail { + ans := SendgridApiMail{apiKey: apiKey, fromName: fromName, from: from} + return &ans +} + +func (o *SendgridApiMail) Send(toName string, to string, subject string, content string, attachments []Attachment) error { + m := mail.NewV3Mail() + + mailFrom := mail.NewEmail(o.fromName, o.from) + mailContent := mail.NewContent("text/html", content) + mailTo := mail.NewEmail(toName, to) + + m.SetFrom(mailFrom) + m.AddContent(mailContent) + + personalization := mail.NewPersonalization() + personalization.AddTos(mailTo) + personalization.Subject = subject + + m.AddPersonalizations(personalization) + + toAdd := make([]*mail.Attachment, 0, len(attachments)) + for i := range attachments { + var att mail.Attachment + encoded := base64.StdEncoding.EncodeToString(attachments[i].Data) + att.SetContent(encoded) + att.SetType("text/plain") + att.SetFilename(attachments[i].Name) + att.SetDisposition("attachment") + toAdd = append(toAdd, &att) + } + + m.AddAttachment(toAdd...) + request := sendgrid.GetRequest(o.apiKey, "/v3/mail/send", "https://api.sendgrid.com") + request.Method = "POST" + request.Body = mail.GetRequestBody(m) + _, err := sendgrid.API(request) + return err +} diff --git a/go.mod b/go.mod index 1678c52..282cb02 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/leodido/go-urn v1.2.0 // indirect github.com/rs/xid v1.2.1 github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba + github.com/sendgrid/rest v2.6.4+incompatible // indirect + github.com/sendgrid/sendgrid-go v3.10.0+incompatible github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf gopkg.in/go-playground/assert.v1 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 9a0e435..224f330 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,10 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba h1:8QAc9wFAf2b/9cAXskm0wBylObZ0bTpRcaP7ThjLPVQ= github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba/go.mod h1:W6zxGUBCXRR5QugSd/nFcFVmwoGnvpjiNY/JwT03Wew= +github.com/sendgrid/rest v2.6.4+incompatible h1:lq6gAQxLwVBf3mVyCCSHI6mgF+NfaJFJHjT0kl6SSo8= +github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c= +github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs= github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= diff --git a/handler/routes.go b/handler/routes.go index d3029e1..22df3d7 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -1,6 +1,7 @@ package handler import ( + "encoding/base64" "encoding/json" "fmt" "net/http" @@ -13,6 +14,7 @@ import ( "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/gommon/log" + "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/model" "github.com/ngoduykhanh/wireguard-ui/util" "github.com/rs/xid" @@ -77,8 +79,6 @@ func Logout() echo.HandlerFunc { // WireGuardClients handler func WireGuardClients() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) clientDataList, err := util.GetClients(true) if err != nil { @@ -97,8 +97,6 @@ func WireGuardClients() echo.HandlerFunc { // GetClients handler return a list of Wireguard client data func GetClients() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) clientDataList, err := util.GetClients(true) if err != nil { @@ -114,8 +112,6 @@ func GetClients() echo.HandlerFunc { // GetClient handler return a of Wireguard client data func GetClient() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) clientID := c.Param("id") clientData, err := util.GetClientByID(clientID, true) @@ -130,8 +126,6 @@ func GetClient() echo.HandlerFunc { // NewClient handler func NewClient() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) client := new(model.Client) c.Bind(client) @@ -194,11 +188,53 @@ func NewClient() echo.HandlerFunc { } } +// EmailClient handler to sent the configuration via email +func EmailClient(mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc { + type clientIdEmailPayload struct { + ID string `json:"id"` + Email string `json:"email"` + } + + return func(c echo.Context) error { + var payload clientIdEmailPayload + c.Bind(&payload) + // TODO validate email + + clientData, err := util.GetClientByID(payload.ID, true) + if err != nil { + log.Errorf("Cannot generate client id %s config file for downloading: %v", payload.ID, err) + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) + } + + // build config + server, _ := util.GetServer() + globalSettings, _ := util.GetGlobalSettings() + config := util.BuildClientConfig(*clientData.Client, server, globalSettings) + + cfg_att := emailer.Attachment{"wg0.conf", []byte(config)} + 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} + err = mailer.Send( + clientData.Client.Name, + payload.Email, + emailSubject, + emailContent, + []emailer.Attachment{cfg_att, qr_att}, + ) + if err != nil { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + + return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Email sent successfully"}) + } +} + // UpdateClient handler to update client information func UpdateClient() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) _client := new(model.Client) c.Bind(_client) @@ -257,8 +293,6 @@ func UpdateClient() echo.HandlerFunc { // SetClientStatus handler to enable / disable a client func SetClientStatus() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) data := make(map[string]interface{}) err := json.NewDecoder(c.Request().Body).Decode(&data) @@ -320,8 +354,6 @@ func DownloadClient() echo.HandlerFunc { // RemoveClient handler func RemoveClient() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) client := new(model.Client) c.Bind(client) @@ -346,8 +378,6 @@ func RemoveClient() echo.HandlerFunc { // WireGuardServer handler func WireGuardServer() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) server, err := util.GetServer() if err != nil { @@ -365,8 +395,6 @@ func WireGuardServer() echo.HandlerFunc { // WireGuardServerInterfaces handler func WireGuardServerInterfaces() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) serverInterface := new(model.ServerInterface) c.Bind(serverInterface) @@ -396,8 +424,6 @@ func WireGuardServerInterfaces() echo.HandlerFunc { // WireGuardServerKeyPair handler to generate private and public keys func WireGuardServerKeyPair() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) // gen Wireguard key pair key, err := wgtypes.GeneratePrivateKey() @@ -428,8 +454,6 @@ func WireGuardServerKeyPair() echo.HandlerFunc { // GlobalSettings handler func GlobalSettings() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) globalSettings, err := util.GetGlobalSettings() if err != nil { @@ -446,8 +470,6 @@ func GlobalSettings() echo.HandlerFunc { // GlobalSettingSubmit handler to update the global settings func GlobalSettingSubmit() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) globalSettings := new(model.GlobalSetting) c.Bind(globalSettings) @@ -477,8 +499,6 @@ func GlobalSettingSubmit() echo.HandlerFunc { // MachineIPAddresses handler to get local interface ip addresses func MachineIPAddresses() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) // get private ip addresses interfaceList, err := util.GetInterfaceIPs() @@ -503,8 +523,6 @@ func MachineIPAddresses() echo.HandlerFunc { // SuggestIPAllocation handler to get the list of ip address for client func SuggestIPAllocation() echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) server, err := util.GetServer() if err != nil { @@ -541,8 +559,6 @@ func SuggestIPAllocation() echo.HandlerFunc { // ApplyServerConfig handler to write config file and restart Wireguard server func ApplyServerConfig(tmplBox *rice.Box) echo.HandlerFunc { return func(c echo.Context) error { - // access validation - validSession(c) server, err := util.GetServer() if err != nil { diff --git a/handler/session.go b/handler/session.go index 6985327..10042ac 100644 --- a/handler/session.go +++ b/handler/session.go @@ -9,22 +9,32 @@ import ( "github.com/ngoduykhanh/wireguard-ui/util" ) -// validSession to redirect user to the login page if they are not authenticated or session expired. -func validSession(c echo.Context) { - if !util.DisableLogin { - sess, _ := session.Get("session", c) - cookie, err := c.Cookie("session_token") - if err != nil || sess.Values["session_token"] != cookie.Value { +func ValidSession(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if !isValidSession(c) { nextURL := c.Request().URL - if nextURL != nil { - c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/login?next=%s", c.Request().URL)) + if nextURL != nil && c.Request().Method == http.MethodGet { + return c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/login?next=%s", c.Request().URL)) } else { - c.Redirect(http.StatusTemporaryRedirect, "/login") + return c.Redirect(http.StatusTemporaryRedirect, "/login") } } + return next(c) } } +func isValidSession(c echo.Context) bool { + if util.DisableLogin { + return true + } + sess, _ := session.Get("session", c) + cookie, err := c.Cookie("session_token") + if err != nil || sess.Values["session_token"] != cookie.Value { + return false + } + return true +} + // currentUser to get username of logged in user func currentUser(c echo.Context) string { if util.DisableLogin { diff --git a/main.go b/main.go index 0553272..2e9e5cd 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,13 @@ import ( "flag" "fmt" "net/http" + "os" "time" rice "github.com/GeertJohan/go.rice" "github.com/labstack/echo/v4" + + "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/handler" "github.com/ngoduykhanh/wireguard-ui/router" "github.com/ngoduykhanh/wireguard-ui/util" @@ -21,6 +24,15 @@ var ( buildTime = fmt.Sprintf(time.Now().UTC().Format("01-02-2006 15:04:05")) ) +const ( + defaultEmailSubject = "Your wireguard configuration" + defaultEmailContent = `Hi,
+

in this email you can file your personal configuration for our wireguard server.

+ +

Best

+` +) + func init() { // command-line flags flagDisableLogin := flag.Bool("disable-login", false, "Disable login page. Turn off authentication.") @@ -30,6 +42,10 @@ func init() { // update runtime config util.DisableLogin = *flagDisableLogin util.BindAddress = *flagBindAddress + util.SendgridApiKey = os.Getenv("SENDGRID_API_KEY") + util.EmailFrom = os.Getenv("EMAIL_FROM") + util.EmailFromName = os.Getenv("EMAIL_FROM_NAME") + util.SessionSecret = []byte(os.Getenv("SESSION_SECRET")) // print app information fmt.Println("Wireguard UI") @@ -60,31 +76,34 @@ func main() { assetHandler := http.FileServer(rice.MustFindBox("assets").HTTPBox()) // register routes - app := router.New(tmplBox, extraData) + app := router.New(tmplBox, extraData, util.SessionSecret) - app.GET("/", handler.WireGuardClients()) + app.GET("/", handler.WireGuardClients(), handler.ValidSession) if !util.DisableLogin { app.GET("/login", handler.LoginPage()) app.POST("/login", handler.Login()) } - app.GET("/logout", handler.Logout()) - app.POST("/new-client", handler.NewClient()) - app.POST("/update-client", handler.UpdateClient()) - app.POST("/client/set-status", handler.SetClientStatus()) - app.POST("/remove-client", handler.RemoveClient()) - app.GET("/download", handler.DownloadClient()) - app.GET("/wg-server", handler.WireGuardServer()) - app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces()) - app.POST("wg-server/keypair", handler.WireGuardServerKeyPair()) - app.GET("/global-settings", handler.GlobalSettings()) - app.POST("/global-settings", handler.GlobalSettingSubmit()) - app.GET("/api/clients", handler.GetClients()) - app.GET("/api/client/:id", handler.GetClient()) - app.GET("/api/machine-ips", handler.MachineIPAddresses()) - app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation()) - app.GET("/api/apply-wg-config", handler.ApplyServerConfig(tmplBox)) + sendmail := emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom) + + app.GET("/logout", handler.Logout(), handler.ValidSession) + app.POST("/new-client", handler.NewClient(), handler.ValidSession) + app.POST("/update-client", handler.UpdateClient(), handler.ValidSession) + app.POST("/email-client", handler.EmailClient(sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession) + app.POST("/client/set-status", handler.SetClientStatus(), handler.ValidSession) + app.POST("/remove-client", handler.RemoveClient(), handler.ValidSession) + app.GET("/download", handler.DownloadClient(), handler.ValidSession) + app.GET("/wg-server", handler.WireGuardServer(), handler.ValidSession) + app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(), handler.ValidSession) + app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(), handler.ValidSession) + app.GET("/global-settings", handler.GlobalSettings(), handler.ValidSession) + app.POST("/global-settings", handler.GlobalSettingSubmit(), handler.ValidSession) + app.GET("/api/clients", handler.GetClients(), handler.ValidSession) + app.GET("/api/client/:id", handler.GetClient(), handler.ValidSession) + app.GET("/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession) + app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(), handler.ValidSession) + app.GET("/api/apply-wg-config", handler.ApplyServerConfig(tmplBox), handler.ValidSession) // servers other static files app.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler))) diff --git a/router/router.go b/router/router.go index 2bd634e..bb14431 100644 --- a/router/router.go +++ b/router/router.go @@ -6,7 +6,7 @@ import ( "reflect" "text/template" - "github.com/GeertJohan/go.rice" + rice "github.com/GeertJohan/go.rice" "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" @@ -44,9 +44,9 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c } // New function -func New(tmplBox *rice.Box, extraData map[string]string) *echo.Echo { +func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Echo { e := echo.New() - e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret")))) + e.Use(session.Middleware(sessions.NewCookieStore(secret))) // read html template file to string tmplBaseString, err := tmplBox.String("base.html") diff --git a/templates/clients.html b/templates/clients.html index b4e26bd..cc6b287 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -30,6 +30,34 @@ Wireguard Clients
+ + + diff --git a/templates/clients.html b/templates/clients.html index 239e54e..5a585f0 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -263,6 +263,7 @@ Wireguard Clients // hide all clients and display only the ones that meet the search criteria (name, email, IP) $('#search-input').keyup(function () { + $("#status-selector").val("All"); var query = $(this).val(); $('.col-lg-4').hide(); $(".info-box-text").each(function() { @@ -274,6 +275,70 @@ Wireguard Clients $(".badge-secondary").filter(':contains("' + query + '")').parent().parent().parent().show(); }) + $("#status-selector").on('change', function () { + $('#search-input').val(""); + switch ($("#status-selector").val()) { + case "All": + $('.col-lg-4').show(); + break; + case "Enabled": + $('.col-lg-4').hide(); + $('[id^="paused_"]').each(function () { + if ($(this).css("visibility") === "hidden") { + $(this).parent().parent().show(); + } + }); + break; + case "Disabled": + $('.col-lg-4').hide(); + $('[id^="paused_"]').each(function () { + if ($(this).css("visibility") !== "hidden") { + $(this).parent().parent().show(); + } + }); + break; + case "Connected": + $('.col-lg-4').hide(); + $.ajax({ + cache: false, + method: 'GET', + url: '{{.basePath}}/status', + success: function (data) { + const returnedHTML = $(data).find(".table-success").get(); + var returnedString = ""; + returnedHTML.forEach(entry => returnedString += entry.outerHTML); + $(".fa-key").each(function () { + if (returnedString.indexOf($(this).parent().text().trim()) != -1) { + $(this).closest('.col-lg-4').show(); + } + }) + } + }); + break; + case "Disconnected": + $('.col-lg-4').show(); + $.ajax({ + cache: false, + method: 'GET', + url: '{{.basePath}}/status', + success: function (data) { + const returnedHTML = $(data).find(".table-success").get(); + var returnedString = ""; + returnedHTML.forEach(entry => returnedString += entry.outerHTML); + $(".fa-key").each(function () { + if (returnedString.indexOf($(this).parent().text().trim()) != -1) { + $(this).closest('.col-lg-4').hide(); + } + }) + } + }); + break; + default: + $('.col-lg-4').show(); + break; + } + }); + // modal_pause_client modal event $("#modal_pause_client").on('show.bs.modal', function (event) { const button = $(event.relatedTarget); From 3d59c7d0de8e5460abdec997e22034c9269a2177 Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 15 Mar 2023 21:29:08 +0100 Subject: [PATCH 095/162] Add log levels (#332) --- README.md | 1 + main.go | 33 ++++++++++++++++++--------------- router/router.go | 22 ++++++++++++++++++++-- util/config.go | 1 + util/util.go | 16 ++++++++++++++++ 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 489314c..6153ea0 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Note: | `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` | +| `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` | diff --git a/main.go b/main.go index 7e79ca4..3e1b6b5 100644 --- a/main.go +++ b/main.go @@ -88,21 +88,24 @@ func init() { util.WgConfTemplate = flagWgConfTemplate util.BasePath = util.ParseBasePath(flagBasePath) - // print app information - fmt.Println("Wireguard UI") - fmt.Println("App Version\t:", appVersion) - fmt.Println("Git Commit\t:", gitCommit) - fmt.Println("Git Ref\t\t:", gitRef) - fmt.Println("Build Time\t:", buildTime) - fmt.Println("Git Repo\t:", "https://github.com/ngoduykhanh/wireguard-ui") - fmt.Println("Authentication\t:", !util.DisableLogin) - fmt.Println("Bind address\t:", util.BindAddress) - //fmt.Println("Sendgrid key\t:", util.SendgridApiKey) - fmt.Println("Email from\t:", util.EmailFrom) - fmt.Println("Email from name\t:", util.EmailFromName) - //fmt.Println("Session secret\t:", util.SessionSecret) - fmt.Println("Custom wg.conf\t:", util.WgConfTemplate) - fmt.Println("Base path\t:", util.BasePath+"/") + // print only if log level is INFO or lower + if lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")); lvl <= log.INFO { + // print app information + fmt.Println("Wireguard UI") + fmt.Println("App Version\t:", appVersion) + fmt.Println("Git Commit\t:", gitCommit) + fmt.Println("Git Ref\t\t:", gitRef) + fmt.Println("Build Time\t:", buildTime) + fmt.Println("Git Repo\t:", "https://github.com/ngoduykhanh/wireguard-ui") + fmt.Println("Authentication\t:", !util.DisableLogin) + fmt.Println("Bind address\t:", util.BindAddress) + //fmt.Println("Sendgrid key\t:", util.SendgridApiKey) + fmt.Println("Email from\t:", util.EmailFrom) + fmt.Println("Email from name\t:", util.EmailFromName) + //fmt.Println("Session secret\t:", util.SessionSecret) + fmt.Println("Custom wg.conf\t:", util.WgConfTemplate) + fmt.Println("Base path\t:", util.BasePath+"/") + } } func main() { diff --git a/router/router.go b/router/router.go index 802ba2a..cdc6424 100644 --- a/router/router.go +++ b/router/router.go @@ -118,10 +118,28 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString)) templates["about.html"] = template.Must(template.New("about").Funcs(funcs).Parse(tmplBaseString + aboutPageString)) - e.Logger.SetLevel(log.DEBUG) + lvl, err := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")) + if err != nil { + log.Fatal(err) + } + logConfig := middleware.DefaultLoggerConfig + logConfig.Skipper = func(c echo.Context) bool { + resp := c.Response() + if resp.Status >= 500 && lvl > log.ERROR { // do not log if response is 5XX but log level is higher than ERROR + return true + } else if resp.Status >= 400 && lvl > log.WARN { // do not log if response is 4XX but log level is higher than WARN + return true + } else if lvl > log.DEBUG { // do not log if log level is higher than DEBUG + return true + } + return false + } + + e.Logger.SetLevel(lvl) e.Pre(middleware.RemoveTrailingSlash()) - e.Use(middleware.Logger()) + e.Use(middleware.LoggerWithConfig(logConfig)) e.HideBanner = true + e.HidePort = lvl > log.INFO // hide the port output if the log level is higher than INFO e.Validator = NewValidator() e.Renderer = &TemplateRegistry{ templates: templates, diff --git a/util/config.go b/util/config.go index 28887e6..018690f 100644 --- a/util/config.go +++ b/util/config.go @@ -42,6 +42,7 @@ const ( PersistentKeepaliveEnvVar = "WGUI_PERSISTENT_KEEPALIVE" ForwardMarkEnvVar = "WGUI_FORWARD_MARK" ConfigFilePathEnvVar = "WGUI_CONFIG_FILE_PATH" + LogLevel = "WGUI_LOG_LEVEL" ServerAddressesEnvVar = "WGUI_SERVER_INTERFACE_ADDRESSES" ServerListenPortEnvVar = "WGUI_SERVER_LISTEN_PORT" ServerPostUpScriptEnvVar = "WGUI_SERVER_POST_UP_SCRIPT" diff --git a/util/util.go b/util/util.go index 6b61d84..44f357b 100644 --- a/util/util.go +++ b/util/util.go @@ -469,6 +469,22 @@ func LookupEnvOrStrings(key string, defaultVal []string) []string { return defaultVal } +func ParseLogLevel(lvl string) (log.Lvl, error) { + switch strings.ToLower(lvl) { + case "debug": + return log.DEBUG, nil + case "info": + return log.INFO, nil + case "warn": + return log.WARN, nil + case "error": + return log.ERROR, nil + case "off": + return log.OFF, nil + default: + return log.DEBUG, fmt.Errorf("not a valid log level: %s", lvl) + } + // GetCurrentHash returns current hashes func GetCurrentHash(db store.IStore) (string, string) { hashClients, _ := dirhash.HashDir(path.Join(db.GetPath(), "clients"), "prefix", dirhash.Hash1) From 7b848c841f19468ff668d07d24fbfccf150221b6 Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 15 Mar 2023 21:30:18 +0100 Subject: [PATCH 096/162] Disable cgo on release ci (#334) --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e5d93a..8907d06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,6 +62,7 @@ jobs: goos: ${{ matrix.goos }} goarch: ${{ matrix.goarch }} goversion: "https://dl.google.com/go/go1.16.1.linux-amd64.tar.gz" + pre_command: export CGO_ENABLED=0 binary_name: "wireguard-ui" build_flags: -v ldflags: -X "main.appVersion=${{ env.APP_VERSION }}" -X "main.buildTime=${{ env.BUILD_TIME }}" -X main.gitCommit=${{ github.sha }} -X main.gitRef=${{ github.ref }} From b8341dd36f68ca21bf4f7a8dfd6b4da0f90561c3 Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 15 Mar 2023 21:35:57 +0100 Subject: [PATCH 097/162] Add docker-compose examples (#339) --- .dockerignore | 3 ++ .gitignore | 4 +++ README.md | 14 ++------ examples/docker-compose/README.md | 30 +++++++++++++++++ examples/docker-compose/boringtun.yml | 43 +++++++++++++++++++++++++ examples/docker-compose/linuxserver.yml | 42 ++++++++++++++++++++++++ examples/docker-compose/system.yml | 27 ++++++++++++++++ 7 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 examples/docker-compose/README.md create mode 100644 examples/docker-compose/boringtun.yml create mode 100644 examples/docker-compose/linuxserver.yml create mode 100644 examples/docker-compose/system.yml diff --git a/.dockerignore b/.dockerignore index b9ff5d8..7624d3e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -25,3 +25,6 @@ docker-compose* db assets wireguard-ui + +# Examples +examples diff --git a/.gitignore b/.gitignore index d6eba78..b8e3cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ rice-box.go # IDEs .vscode .idea + +# Examples +examples/docker-compose/config +examples/docker-compose/db diff --git a/README.md b/README.md index 6153ea0..0c7bebd 100644 --- a/README.md +++ b/README.md @@ -27,23 +27,13 @@ Download the binary file from the release page and run it directly on the host m ### 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: +The [examples/docker-compose](examples/docker-compose) folder contains example docker-compose files. +Choose the example which fits you the most, adjust the configuration for your needs, then run it like below: ``` docker-compose up ``` -Note: - -- 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 | Default | diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md new file mode 100644 index 0000000..951df08 --- /dev/null +++ b/examples/docker-compose/README.md @@ -0,0 +1,30 @@ +## Prerequisites + +### Kernel Module + +Depending on if the Wireguard kernel module is available on your system you have more or less choices which example to use. + +You can check if the kernel modules are available via the following command: +```shell +modprobe wireguard +``` + +If the command exits successfully and doesn't print an error the kernel modules are available. +If it does error, you either have to install them manually (or activate if deactivated) or use an userspace implementation. +For an example of an userspace implementation, see _borigtun_. + +### Credentials + +Username and password for all examples is `admin` by default. +For security reasons it's highly recommended to change them before the first startup. + +## Examples +- **[system](system.yml)** + + If you have Wireguard already installed on your system and only want to run the UI in docker this might fit the most. +- **[linuxserver](linuxserver.yml)** + + If you have the Wireguard kernel modules installed (included in the mainline kernel since version 5.6) but want it running inside of docker, this might fit the most. +- **[boringtun](boringtun.yml)** + + If Wireguard kernel modules are not available, you can switch to an userspace implementation like [boringtun](https://github.com/cloudflare/boringtun). diff --git a/examples/docker-compose/boringtun.yml b/examples/docker-compose/boringtun.yml new file mode 100644 index 0000000..a1bdd2f --- /dev/null +++ b/examples/docker-compose/boringtun.yml @@ -0,0 +1,43 @@ +version: "3" + +services: + boringtun: + image: ghcr.io/ntkme/boringtun:edge + command: + - wg0 + container_name: boringtun + # use the network of the 'wireguard-ui' service. this enables to show active clients in the status page + network_mode: service:wireguard-ui + cap_add: + - NET_ADMIN + volumes: + - /dev/net/tun:/dev/net/tun + - ./config:/etc/wireguard + + wireguard-ui: + image: ngoduykhanh/wireguard-ui:latest + container_name: wireguard-ui + cap_add: + - NET_ADMIN + environment: + - SENDGRID_API_KEY + - EMAIL_FROM_ADDRESS + - EMAIL_FROM_NAME + - SESSION_SECRET + - WGUI_USERNAME=admin + - WGUI_PASSWORD=admin + - WG_CONF_TEMPLATE + - WGUI_MANAGE_START=true + - WGUI_MANAGE_RESTART=true + logging: + driver: json-file + options: + max-size: 50m + volumes: + - ./db:/app/db + - ./config:/etc/wireguard + ports: + # port for wireguard-ui + - "5000:5000" + # port of the wireguard server. this must be set here as the `boringtun` container joins the network of this container and hasn't its own network over which it could publish the ports + - "51820:51820/udp" diff --git a/examples/docker-compose/linuxserver.yml b/examples/docker-compose/linuxserver.yml new file mode 100644 index 0000000..1b7a66f --- /dev/null +++ b/examples/docker-compose/linuxserver.yml @@ -0,0 +1,42 @@ +version: "3" + +services: + wireguard: + image: linuxserver/wireguard:latest + container_name: wireguard + cap_add: + - NET_ADMIN + volumes: + - ./config:/config + ports: + # port for wireguard-ui. this must be set here as the `wireguard-ui` container joins the network of this container and hasn't its own network over which it could publish the ports + - "5000:5000" + # port of the wireguard server + - "51820:51820/udp" + + wireguard-ui: + image: ngoduykhanh/wireguard-ui:latest + container_name: wireguard-ui + depends_on: + - wireguard + cap_add: + - NET_ADMIN + # use the network of the 'wireguard' service. this enables to show active clients in the status page + network_mode: service:wireguard + environment: + - SENDGRID_API_KEY + - EMAIL_FROM_ADDRESS + - EMAIL_FROM_NAME + - SESSION_SECRET + - WGUI_USERNAME=admin + - WGUI_PASSWORD=admin + - WG_CONF_TEMPLATE + - WGUI_MANAGE_START=true + - WGUI_MANAGE_RESTART=true + logging: + driver: json-file + options: + max-size: 50m + volumes: + - ./db:/app/db + - ./config:/etc/wireguard diff --git a/examples/docker-compose/system.yml b/examples/docker-compose/system.yml new file mode 100644 index 0000000..c27f31e --- /dev/null +++ b/examples/docker-compose/system.yml @@ -0,0 +1,27 @@ +version: "3" + +services: + wireguard-ui: + image: ngoduykhanh/wireguard-ui:latest + container_name: wireguard-ui + cap_add: + - NET_ADMIN + # required to show active clients. with this set, you don't need to expose the ui port (5000) anymore + network_mode: host + environment: + - SENDGRID_API_KEY + - EMAIL_FROM_ADDRESS + - EMAIL_FROM_NAME + - SESSION_SECRET + - WGUI_USERNAME=admin + - WGUI_PASSWORD=admin + - WG_CONF_TEMPLATE + - WGUI_MANAGE_START=false + - WGUI_MANAGE_RESTART=false + logging: + driver: json-file + options: + max-size: 50m + volumes: + - ./db:/app/db + - /etc/wireguard:/etc/wireguard From b80c44af43518c3dc1e48d8d5406085c667e3d9f Mon Sep 17 00:00:00 2001 From: Paul Dee Date: Wed, 15 Mar 2023 21:37:39 +0100 Subject: [PATCH 098/162] Fix for fwmark (#279) --- README.md | 2 +- model/setting.go | 2 +- store/jsondb/jsondb.go | 5 +---- templates/clients.html | 17 +++-------------- templates/global_settings.html | 18 +++++++++--------- util/config.go | 4 ++-- util/util.go | 6 ------ 7 files changed, 17 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 0c7bebd..27b4ed8 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ docker-compose up | `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_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) | | `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 | diff --git a/model/setting.go b/model/setting.go index e871591..a702293 100644 --- a/model/setting.go +++ b/model/setting.go @@ -10,7 +10,7 @@ type GlobalSetting struct { DNSServers []string `json:"dns_servers"` MTU int `json:"mtu,string"` PersistentKeepalive int `json:"persistent_keepalive,string"` - ForwardMark string `json:"forward_mark"` + FirewallMark string `json:"firewall_mark"` ConfigFilePath string `json:"config_file_path"` UpdatedAt time.Time `json:"updated_at"` } diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 0357cbe..f95ff16 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -101,7 +101,7 @@ func (o *JsonDB) Init() error { globalSetting.DNSServers = util.LookupEnvOrStrings(util.DNSEnvVar, []string{util.DefaultDNS}) globalSetting.MTU = util.LookupEnvOrInt(util.MTUEnvVar, util.DefaultMTU) globalSetting.PersistentKeepalive = util.LookupEnvOrInt(util.PersistentKeepaliveEnvVar, util.DefaultPersistentKeepalive) - globalSetting.ForwardMark = util.LookupEnvOrString(util.ForwardMarkEnvVar, util.DefaultForwardMark) + globalSetting.FirewallMark = util.LookupEnvOrString(util.FirewallMarkEnvVar, util.DefaultFirewallMark) globalSetting.ConfigFilePath = util.LookupEnvOrString(util.ConfigFilePathEnvVar, util.DefaultConfigFilePath) globalSetting.UpdatedAt = time.Now().UTC() o.conn.Write("server", "global_settings", globalSetting) @@ -269,9 +269,6 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti if !qrCodeSettings.IncludeMTU { globalSettings.MTU = 0 } - if !qrCodeSettings.IncludeFwMark { - globalSettings.ForwardMark = "" - } png, err := qrcode.Encode(util.BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256) if err == nil { diff --git a/templates/clients.html b/templates/clients.html index 5a585f0..513797c 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -70,17 +70,8 @@ Wireguard Clients
@@ -490,9 +481,7 @@ Wireguard Clients cache: false, method: 'GET', url: '{{.basePath}}/api/client/' + client_id, - data: { - qrCodeIncludeFwMark: include_fwmark - }, + data: JSON.stringify(data), dataType: 'json', contentType: "application/json", success: function (resp) { diff --git a/templates/global_settings.html b/templates/global_settings.html index 8a41d1f..15d7b4b 100644 --- a/templates/global_settings.html +++ b/templates/global_settings.html @@ -56,10 +56,10 @@ Global Settings value="{{if .globalSettings.PersistentKeepalive }}{{ .globalSettings.PersistentKeepalive }}{{end}}">
- - + +
@@ -100,8 +100,8 @@ Global Settings until they reach out to other peers themselves. Adding PersistentKeepalive can ensure that the connection remains open.
Leave blank to omit this setting in the Client config.
-
5. Forward Mark
-
Set an fwmark on all packets going out of WireGuard's UDP socket. Default value: 0xca6c
+
5. Firewall Mark
+
Add a matching fwmark on all packets going out of a WireGuard non-default-route tunnel. Default value: 0xca6c
6. Wireguard Config File Path
The path of your Wireguard server config file. Please make sure the parent directory exists and is writable.
@@ -149,9 +149,9 @@ Global Settings const dns_servers = $("#dns_servers").val().split(","); const mtu = $("#mtu").val(); const persistent_keepalive = $("#persistent_keepalive").val(); - const forward_mark = $("#forward_mark").val(); + const firewall_mark = $("#firewall_mark").val(); const config_file_path = $("#config_file_path").val(); - const data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive, "forward_mark": forward_mark, "config_file_path": config_file_path}; + const data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive, "firewall_mark": firewall_mark, "config_file_path": config_file_path}; $.ajax({ cache: false, @@ -222,7 +222,7 @@ Global Settings config_file_path: { required: true }, - forward_mark: { + firewall_mark: { required: false } }, diff --git a/util/config.go b/util/config.go index 018690f..7a95f97 100644 --- a/util/config.go +++ b/util/config.go @@ -30,7 +30,7 @@ const ( DefaultDNS = "1.1.1.1" DefaultMTU = 1450 DefaultPersistentKeepalive = 15 - DefaultForwardMark = "0xca6c" + DefaultFirewallMark = "0xca6c" // i.e. 51820 DefaultConfigFilePath = "/etc/wireguard/wg0.conf" UsernameEnvVar = "WGUI_USERNAME" PasswordEnvVar = "WGUI_PASSWORD" @@ -40,7 +40,7 @@ const ( DNSEnvVar = "WGUI_DNS" MTUEnvVar = "WGUI_MTU" PersistentKeepaliveEnvVar = "WGUI_PERSISTENT_KEEPALIVE" - ForwardMarkEnvVar = "WGUI_FORWARD_MARK" + FirewallMarkEnvVar = "WGUI_FIREWALL_MARK" ConfigFilePathEnvVar = "WGUI_CONFIG_FILE_PATH" LogLevel = "WGUI_LOG_LEVEL" ServerAddressesEnvVar = "WGUI_SERVER_INTERFACE_ADDRESSES" diff --git a/util/util.go b/util/util.go index 44f357b..04950f9 100644 --- a/util/util.go +++ b/util/util.go @@ -65,18 +65,12 @@ func BuildClientConfig(client model.Client, server model.Server, setting model.G peerPersistentKeepalive = fmt.Sprintf("PersistentKeepalive = %d\n", setting.PersistentKeepalive) } - forwardMark := "" - if setting.ForwardMark != "" { - forwardMark = fmt.Sprintf("FwMark = %s\n", setting.ForwardMark) - } - // build the config as string strConfig := "[Interface]\n" + clientAddress + clientPrivateKey + clientDNS + clientMTU + - forwardMark + "\n[Peer]\n" + peerPublicKey + peerPresharedKey + From 814093cdd32863dc3b5becfc0ae8de6cd0eec982 Mon Sep 17 00:00:00 2001 From: Paul Dee Date: Wed, 15 Mar 2023 21:39:20 +0100 Subject: [PATCH 099/162] Stamp git commit into docker builds. (#325) --- Dockerfile | 3 ++- README.md | 8 +++++++- main.go | 1 + templates/about.html | 6 ++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a30cefe..e4d5525 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ LABEL maintainer="Khanh Ngo Current version
+{{ if .gitCommit }} +
+ + +
+{{ end }}
From abef29bf172482305f940dba7e1519037e05172f Mon Sep 17 00:00:00 2001 From: Matze <37954743+Matze1224@users.noreply.github.com> Date: Wed, 15 Mar 2023 21:41:46 +0100 Subject: [PATCH 100/162] better error-handling if no public IP could be detected (#323) --- README.md | 5 +++-- util/util.go | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eaa18c3..7eca337 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,15 @@ docker-compose up | `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_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_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) | +| `WGUI_FIREWALL_MARK` | The default WireGuard firewall mark | `0xca6c` (51820) | | `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` | | +| `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` | diff --git a/util/util.go b/util/util.go index 04950f9..35568da 100644 --- a/util/util.go +++ b/util/util.go @@ -220,10 +220,12 @@ func GetPublicIP() (model.Interface, error) { ip, err := consensus.ExternalIP() if err != nil { publicInterface.IPAddress = "N/A" + } else { + publicInterface.IPAddress = ip.String() } - publicInterface.IPAddress = ip.String() - return publicInterface, err + // error handling happend above, no need to pass it through + return publicInterface, nil } // GetIPFromCIDR get ip from CIDR From c8240fe15791d375f5f3441c47cc67cf1d9c856b Mon Sep 17 00:00:00 2001 From: Arminas Date: Wed, 15 Mar 2023 22:45:46 +0200 Subject: [PATCH 101/162] fixed about page not showing menu items (#343) --- handler/routes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/routes.go b/handler/routes.go index 4f76d22..96be22d 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -1012,7 +1012,7 @@ func GetHashesChanges(db store.IStore) echo.HandlerFunc { func AboutPage() echo.HandlerFunc { return func(c echo.Context) error { return c.Render(http.StatusOK, "about.html", map[string]interface{}{ - "baseData": model.BaseData{Active: "about", CurrentUser: currentUser(c)}, + "baseData": model.BaseData{Active: "about", CurrentUser: currentUser(c), Admin: isAdmin(c)}, }) } } From e3e363944344ac667f384d8e8bd50df2942eea9b Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 15 Mar 2023 21:50:46 +0100 Subject: [PATCH 102/162] Bracket fixes --- util/util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/util/util.go b/util/util.go index 35568da..b62752d 100644 --- a/util/util.go +++ b/util/util.go @@ -480,6 +480,7 @@ func ParseLogLevel(lvl string) (log.Lvl, error) { default: return log.DEBUG, fmt.Errorf("not a valid log level: %s", lvl) } +} // GetCurrentHash returns current hashes func GetCurrentHash(db store.IStore) (string, string) { From 4fc52b62d21614325d61cc1787dd95bacec9841c Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Thu, 16 Mar 2023 08:40:04 +0100 Subject: [PATCH 103/162] Replace go.rice with native go embedding (#331) --- .github/workflows/release.yml | 10 +-------- .gitignore | 1 - Dockerfile | 7 +------ README.md | 11 +--------- go.mod | 3 +-- go.sum | 9 --------- handler/routes.go | 9 ++++----- main.go | 38 +++++++++++++++++++++++------------ router/router.go | 24 +++++++++++----------- util/util.go | 18 ++++++++++++++--- 10 files changed, 60 insertions(+), 70 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8907d06..feb4c93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,20 +40,12 @@ jobs: node-version: '14' registry-url: 'https://registry.npmjs.org' - # prepare assets for go rice + # prepare assets - name: Prepare assets run: | chmod +x ./prepare_assets.sh ./prepare_assets.sh - # get go rice tool - - name: Get go rice tool - run: go get github.com/GeertJohan/go.rice/rice - - # run go rice embed - - name: Run go rice embed - run: ${HOME}/go/bin/rice embed-go - # build and make the releases - name: Build and make the releases uses: wangyoucao577/go-release-action@master diff --git a/.gitignore b/.gitignore index b8e3cf3..f1e3f44 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ wireguard-ui vendor/ assets/ node_modules/ -rice-box.go # IDEs .vscode diff --git a/Dockerfile b/Dockerfile index e4d5525..e42603b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,10 +43,6 @@ RUN mkdir -p assets/plugins && \ /build/node_modules/jquery-tags-input/ \ assets/plugins/ -# Get go modules and build tool -RUN go mod download && \ - go get github.com/GeertJohan/go.rice/rice - # Add sources COPY . /build @@ -54,8 +50,7 @@ COPY . /build RUN cp -r /build/custom/ assets/ # Build -RUN rice embed-go && \ - CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.gitCommit=${COMMIT}" -a -o wg-ui . +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.gitCommit=${COMMIT}" -a -o wg-ui . # Release stage FROM alpine:3.16 diff --git a/README.md b/README.md index 7eca337..7fc3207 100644 --- a/README.md +++ b/README.md @@ -211,18 +211,9 @@ Prepare the assets directory ./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 - +Then build your executable ```sh go build -o wireguard-ui -rice append --exec wireguard-ui ``` ## License diff --git a/go.mod b/go.mod index d7c4a16..5842acb 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module github.com/ngoduykhanh/wireguard-ui -go 1.14 +go 1.16 require ( - github.com/GeertJohan/go.rice v1.0.2 github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd github.com/go-playground/universal-translator v0.17.0 // indirect github.com/gorilla/sessions v1.2.0 diff --git a/go.sum b/go.sum index 814f123..ee957cb 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,4 @@ -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/GeertJohan/go.rice v1.0.2 h1:PtRw+Tg3oa3HYwiDBZyvOJ8LdIyf6lAovJJtr7YOAYk= -github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= @@ -12,8 +8,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/casbin/casbin/v2 v2.0.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.1-coreos.6.0.20180223184059-4f5275f4ebbf/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= -github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -52,7 +46,6 @@ github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= github.com/jessevdk/go-flags v0.0.0-20150816100521-1acbbaff2f34/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= @@ -109,7 +102,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -252,7 +244,6 @@ golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmo golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= diff --git a/handler/routes.go b/handler/routes.go index 96be22d..d43d75f 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -5,13 +5,13 @@ import ( "encoding/base64" "encoding/json" "fmt" + "io/fs" "net/http" "os" "sort" "strings" "time" - rice "github.com/GeertJohan/go.rice" "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" @@ -948,7 +948,7 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { } // ApplyServerConfig handler to write config file and restart Wireguard server -func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc { +func ApplyServerConfig(db store.IStore, tmplDir fs.FS) echo.HandlerFunc { return func(c echo.Context) error { server, err := db.GetServer() @@ -976,14 +976,14 @@ func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc { } // Write config file - err = util.WriteWireGuardServerConfig(tmplBox, server, clients, users, settings) + err = util.WriteWireGuardServerConfig(tmplDir, server, clients, users, settings) if err != nil { log.Error("Cannot apply server config: ", err) return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ false, fmt.Sprintf("Cannot apply server config: %v", err), }) } - + err = util.UpdateHashes(db) if err != nil { log.Error("Cannot update hashes: ", err) @@ -1016,4 +1016,3 @@ func AboutPage() echo.HandlerFunc { }) } } - diff --git a/main.go b/main.go index ccd163a..2fb2d03 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,17 @@ package main 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" - rice "github.com/GeertJohan/go.rice" "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/handler" "github.com/ngoduykhanh/wireguard-ui/router" @@ -51,6 +52,16 @@ const ( ` ) +// embed the "templates" directory +// +//go:embed templates/* +var embeddedTemplates embed.FS + +// embed the "assets" directory +// +//go:embed assets/* +var embeddedAssets embed.FS + func init() { // command-line flags and env variables @@ -122,17 +133,15 @@ func main() { extraData["gitCommit"] = gitCommit extraData["basePath"] = util.BasePath - // create rice box for embedded template - tmplBox := rice.MustFindBox("templates") - - // rice file server for assets. "assets" is the folder where the files come from. - assetHandler := http.FileServer(rice.MustFindBox("assets").HTTPBox()) + // strip the "templates/" prefix from the embedded directory so files can be read by their direct name (e.g. + // "base.html" instead of "templates/base.html") + tmplDir, _ := fs.Sub(fs.FS(embeddedTemplates), "templates") // create the wireguard config on start, if it doesn't exist - initServerConfig(db, tmplBox) + initServerConfig(db, tmplDir) // register routes - app := router.New(tmplBox, extraData, util.SessionSecret) + app := router.New(tmplDir, extraData, util.SessionSecret) app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession) @@ -170,26 +179,29 @@ func main() { app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin) - app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession) app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) app.GET(util.BasePath+"/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession) app.GET(util.BasePath+"/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession) - app.POST(util.BasePath+"/api/apply-wg-config", handler.ApplyServerConfig(db, tmplBox), handler.ValidSession, handler.ContentTypeJson) + app.POST(util.BasePath+"/api/apply-wg-config", handler.ApplyServerConfig(db, tmplDir), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession) app.POST(util.BasePath+"/wake_on_lan_host", handler.SaveWakeOnLanHost(db), handler.ValidSession, handler.ContentTypeJson) app.DELETE(util.BasePath+"/wake_on_lan_host/:mac_address", handler.DeleteWakeOnHost(db), handler.ValidSession, handler.ContentTypeJson) app.PUT(util.BasePath+"/wake_on_lan_host/:mac_address", handler.WakeOnHost(db), handler.ValidSession, handler.ContentTypeJson) - // servers other static files + // strip the "assets/" prefix from the embedded directory so files can be called directly without the "assets/" + // prefix + assetsDir, _ := fs.Sub(fs.FS(embeddedAssets), "assets") + assetHandler := http.FileServer(http.FS(assetsDir)) + // serves other static files app.GET(util.BasePath+"/static/*", echo.WrapHandler(http.StripPrefix(util.BasePath+"/static/", assetHandler))) app.Logger.Fatal(app.Start(util.BindAddress)) } -func initServerConfig(db store.IStore, tmplBox *rice.Box) { +func initServerConfig(db store.IStore, tmplDir fs.FS) { settings, err := db.GetGlobalSettings() if err != nil { log.Fatalf("Cannot get global settings: ", err) @@ -216,7 +228,7 @@ func initServerConfig(db store.IStore, tmplBox *rice.Box) { } // write config file - err = util.WriteWireGuardServerConfig(tmplBox, server, clients, users, settings) + err = util.WriteWireGuardServerConfig(tmplDir, server, clients, users, settings) if err != nil { log.Fatalf("Cannot create server config: ", err) } diff --git a/router/router.go b/router/router.go index cdc6424..c2d1943 100644 --- a/router/router.go +++ b/router/router.go @@ -3,11 +3,11 @@ package router import ( "errors" "io" + "io/fs" "reflect" "strings" "text/template" - rice "github.com/GeertJohan/go.rice" "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" @@ -48,57 +48,57 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c } // New function -func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Echo { +func New(tmplDir fs.FS, extraData map[string]string, secret []byte) *echo.Echo { e := echo.New() e.Use(session.Middleware(sessions.NewCookieStore(secret))) // read html template file to string - tmplBaseString, err := tmplBox.String("base.html") + tmplBaseString, err := util.StringFromEmbedFile(tmplDir, "base.html") if err != nil { log.Fatal(err) } - tmplLoginString, err := tmplBox.String("login.html") + tmplLoginString, err := util.StringFromEmbedFile(tmplDir, "login.html") if err != nil { log.Fatal(err) } - tmplProfileString, err := tmplBox.String("profile.html") + tmplProfileString, err := util.StringFromEmbedFile(tmplDir, "profile.html") if err != nil { log.Fatal(err) } - tmplClientsString, err := tmplBox.String("clients.html") + tmplClientsString, err := util.StringFromEmbedFile(tmplDir, "clients.html") if err != nil { log.Fatal(err) } - tmplServerString, err := tmplBox.String("server.html") + tmplServerString, err := util.StringFromEmbedFile(tmplDir, "server.html") if err != nil { log.Fatal(err) } - tmplGlobalSettingsString, err := tmplBox.String("global_settings.html") + tmplGlobalSettingsString, err := util.StringFromEmbedFile(tmplDir, "global_settings.html") if err != nil { log.Fatal(err) } - tmplUsersSettingsString, err := tmplBox.String("users_settings.html") + tmplUsersSettingsString, err := util.StringFromEmbedFile(tmplDir, "users_settings.html") if err != nil { log.Fatal(err) } - tmplStatusString, err := tmplBox.String("status.html") + tmplStatusString, err := util.StringFromEmbedFile(tmplDir, "status.html") if err != nil { log.Fatal(err) } - tmplWakeOnLanHostsString, err := tmplBox.String("wake_on_lan_hosts.html") + tmplWakeOnLanHostsString, err := util.StringFromEmbedFile(tmplDir, "wake_on_lan_hosts.html") if err != nil { log.Fatal(err) } - aboutPageString, err := tmplBox.String("about.html") + aboutPageString, err := util.StringFromEmbedFile(tmplDir, "about.html") if err != nil { log.Fatal(err) } diff --git a/util/util.go b/util/util.go index b62752d..7b93a57 100644 --- a/util/util.go +++ b/util/util.go @@ -7,6 +7,7 @@ import ( "github.com/ngoduykhanh/wireguard-ui/store" "golang.org/x/mod/sumdb/dirhash" "io" + "io/fs" "io/ioutil" "net" "os" @@ -17,7 +18,6 @@ import ( "text/template" "time" - rice "github.com/GeertJohan/go.rice" externalip "github.com/glendc/go-external-ip" "github.com/labstack/gommon/log" "github.com/ngoduykhanh/wireguard-ui/model" @@ -382,7 +382,7 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip } // WriteWireGuardServerConfig to write Wireguard server config. e.g. wg0.conf -func WriteWireGuardServerConfig(tmplBox *rice.Box, serverConfig model.Server, clientDataList []model.ClientData, usersList []model.User, globalSettings model.GlobalSetting) error { +func WriteWireGuardServerConfig(tmplDir fs.FS, serverConfig model.Server, clientDataList []model.ClientData, usersList []model.User, globalSettings model.GlobalSetting) error { var tmplWireguardConf string // if set, read wg.conf template from WgConfTemplate @@ -394,7 +394,7 @@ func WriteWireGuardServerConfig(tmplBox *rice.Box, serverConfig model.Server, cl tmplWireguardConf = string(fileContentBytes) } else { // read default wg.conf template file to string - fileContent, err := tmplBox.String("wg.conf") + fileContent, err := StringFromEmbedFile(tmplDir, "wg.conf") if err != nil { return err } @@ -465,6 +465,18 @@ func LookupEnvOrStrings(key string, defaultVal []string) []string { return defaultVal } +func StringFromEmbedFile(embed fs.FS, filename string) (string, error) { + file, err := embed.Open(filename) + if err != nil { + return "", err + } + content, err := io.ReadAll(file) + if err != nil { + return "", err + } + return string(content), nil +} + func ParseLogLevel(lvl string) (log.Lvl, error) { switch strings.ToLower(lvl) { case "debug": From 00f7f3d280b90fdaa15faf48379d509b1b37e135 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Thu, 16 Mar 2023 08:58:28 +0100 Subject: [PATCH 104/162] Style fixes --- templates/base.html | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/templates/base.html b/templates/base.html index 6c2e3f3..181049d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -56,13 +56,15 @@
- +
+ +
From d3c47c53c8460b108ac1a0833f77cc0a3dcce33a Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Thu, 16 Mar 2023 09:09:48 +0100 Subject: [PATCH 105/162] QR code fixes --- templates/clients.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/clients.html b/templates/clients.html index 513797c..0fa98f7 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -481,7 +481,9 @@ Wireguard Clients cache: false, method: 'GET', url: '{{.basePath}}/api/client/' + client_id, - data: JSON.stringify(data), + data: { + qrCodeIncludeFwMark: include_fwmark + }, dataType: 'json', contentType: "application/json", success: function (resp) { From 5e0217db049ae0adc8947fa796f9614301603fd6 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Thu, 16 Mar 2023 16:25:38 +0100 Subject: [PATCH 106/162] Set password type field --- templates/users_settings.html | 2 +- util/util.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/users_settings.html b/templates/users_settings.html index 05d8878..6fb8d69 100644 --- a/templates/users_settings.html +++ b/templates/users_settings.html @@ -42,7 +42,7 @@ Users Settings
-
diff --git a/util/util.go b/util/util.go index 7b93a57..4d4b9b3 100644 --- a/util/util.go +++ b/util/util.go @@ -514,11 +514,11 @@ func HashesChanged(db store.IStore) bool { newClient, newServer := GetCurrentHash(db) if oldClient != newClient { - fmt.Println("Hash for client differs") + //fmt.Println("Hash for client differs") return true } if oldServer != newServer { - fmt.Println("Hash for server differs") + //fmt.Println("Hash for server differs") return true } return false From ac99317ba3ff35eb169b6acc561ecf6f674f4062 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Fri, 17 Mar 2023 09:53:57 +0100 Subject: [PATCH 107/162] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 7fc3207..62eef0d 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,11 @@ or docker compose build --build-arg=COMMIT=$(git rev-parse --short HEAD) ``` +:information_source: A container image is avaialble on [Docker Hub](https://hub.docker.com/r/ngoduykhanh/wireguard-ui) which you can pull and use +``` +docker pull ngoduykhanh/wireguard-ui +```` + ### Build binary file Prepare the assets directory From cfbdae7abbf15ac097b86e5875f2621ad1db74b1 Mon Sep 17 00:00:00 2001 From: Paul Dee Date: Wed, 24 May 2023 12:02:07 +0200 Subject: [PATCH 108/162] Follow-up fix for fwmark 101b5564c267e673afdea97e24a6bd778939abd8 (#372) Remove all FwMark settings from client configs (illegal) and QRcode (also illegal). --- handler/routes.go | 3 --- model/client.go | 1 - templates/clients.html | 6 +----- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/handler/routes.go b/handler/routes.go index d43d75f..e443af9 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -357,11 +357,9 @@ func GetClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { clientID := c.Param("id") - qrCodeIncludeFwMark := c.QueryParam("qrCodeIncludeFwMark") qrCodeSettings := model.QRCodeSettings{ Enabled: true, IncludeDNS: true, - IncludeFwMark: qrCodeIncludeFwMark == "true", IncludeMTU: true, } @@ -490,7 +488,6 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon qrCodeSettings := model.QRCodeSettings{ Enabled: true, IncludeDNS: true, - IncludeFwMark: true, IncludeMTU: true, } clientData, err := db.GetClientByID(payload.ID, qrCodeSettings) diff --git a/model/client.go b/model/client.go index 7a4cb41..be5fa76 100644 --- a/model/client.go +++ b/model/client.go @@ -30,6 +30,5 @@ type ClientData struct { type QRCodeSettings struct { Enabled bool IncludeDNS bool - IncludeFwMark bool IncludeMTU bool } diff --git a/templates/clients.html b/templates/clients.html index 0fa98f7..94ab634 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -472,17 +472,13 @@ Wireguard Clients const client_id = $("#qr_client_id").val(); const QRCodeImg = $("#qr_code"); const QRCodeA = $("#qr_code_a"); - let include_fwmark = false; - if ($("#qr_include_fwmark").is(':checked')){ - include_fwmark = true; - } QRCodeImg.hide(); $.ajax({ cache: false, method: 'GET', url: '{{.basePath}}/api/client/' + client_id, data: { - qrCodeIncludeFwMark: include_fwmark + }, dataType: 'json', contentType: "application/json", From ec757286c5a85f383d58b24dac487606e69636ca Mon Sep 17 00:00:00 2001 From: ByteDream <63594396+ByteDream@users.noreply.github.com> Date: Wed, 24 May 2023 12:04:02 +0200 Subject: [PATCH 109/162] Hide user settings if login is disabled (#356) (#361) --- main.go | 3 ++- router/router.go | 4 ++-- templates/base.html | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 2fb2d03..a6c147f 100644 --- a/main.go +++ b/main.go @@ -128,10 +128,11 @@ func main() { panic(err) } // set app extra data - extraData := make(map[string]string) + extraData := make(map[string]interface{}) extraData["appVersion"] = appVersion extraData["gitCommit"] = gitCommit extraData["basePath"] = util.BasePath + extraData["loginDisabled"] = flagDisableLogin // strip the "templates/" prefix from the embedded directory so files can be read by their direct name (e.g. // "base.html" instead of "templates/base.html") diff --git a/router/router.go b/router/router.go index c2d1943..569ebaf 100644 --- a/router/router.go +++ b/router/router.go @@ -19,7 +19,7 @@ import ( // TemplateRegistry is a custom html/template renderer for Echo framework type TemplateRegistry struct { templates map[string]*template.Template - extraData map[string]string + extraData map[string]interface{} } // Render e.Renderer interface @@ -48,7 +48,7 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c } // New function -func New(tmplDir fs.FS, extraData map[string]string, secret []byte) *echo.Echo { +func New(tmplDir fs.FS, extraData map[string]interface{}, secret []byte) *echo.Echo { e := echo.New() e.Use(session.Middleware(sessions.NewCookieStore(secret))) diff --git a/templates/base.html b/templates/base.html index 181049d..0865ff3 100644 --- a/templates/base.html +++ b/templates/base.html @@ -145,6 +145,7 @@

+ {{if not .loginDisabled}} {{end}} + {{end}}
+
+ + +
Leave blank to omit this setting in the Client config.
5. Firewall Mark
Add a matching fwmark on all packets going out of a WireGuard non-default-route tunnel. Default value: 0xca6c
-
6. Wireguard Config File Path
+
6. Table
+
Value for the Table setting in the wg conf file. Default value: auto
+
7. Wireguard Config File Path
The path of your Wireguard server config file. Please make sure the parent directory exists and is writable.
@@ -150,8 +158,9 @@ Global Settings const mtu = $("#mtu").val(); const persistent_keepalive = $("#persistent_keepalive").val(); const firewall_mark = $("#firewall_mark").val(); + const table = $("#table").val(); const config_file_path = $("#config_file_path").val(); - const data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive, "firewall_mark": firewall_mark, "config_file_path": config_file_path}; + const data = {"endpoint_address": endpoint_address, "dns_servers": dns_servers, "mtu": mtu, "persistent_keepalive": persistent_keepalive, "firewall_mark": firewall_mark, "table": table, "config_file_path": config_file_path}; $.ajax({ cache: false, @@ -224,6 +233,9 @@ Global Settings }, firewall_mark: { required: false + }, + table: { + required: false } }, messages: { diff --git a/templates/wg.conf b/templates/wg.conf index 745a92f..81893a8 100644 --- a/templates/wg.conf +++ b/templates/wg.conf @@ -10,6 +10,7 @@ PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }} {{if .globalSettings.MTU}}MTU = {{ .globalSettings.MTU }}{{end}} PostUp = {{ .serverConfig.Interface.PostUp }} PostDown = {{ .serverConfig.Interface.PostDown }} +Table = {{ .globalSettings.Table }} {{range .clientDataList}}{{if eq .Client.Enabled true}} # ID: {{ .Client.ID }} diff --git a/util/config.go b/util/config.go index 7a95f97..3c3bd18 100644 --- a/util/config.go +++ b/util/config.go @@ -31,6 +31,7 @@ const ( DefaultMTU = 1450 DefaultPersistentKeepalive = 15 DefaultFirewallMark = "0xca6c" // i.e. 51820 + DefaultTable = "auto" DefaultConfigFilePath = "/etc/wireguard/wg0.conf" UsernameEnvVar = "WGUI_USERNAME" PasswordEnvVar = "WGUI_PASSWORD" @@ -41,6 +42,7 @@ const ( MTUEnvVar = "WGUI_MTU" PersistentKeepaliveEnvVar = "WGUI_PERSISTENT_KEEPALIVE" FirewallMarkEnvVar = "WGUI_FIREWALL_MARK" + TableEnvVar = "WGUI_TABLE" ConfigFilePathEnvVar = "WGUI_CONFIG_FILE_PATH" LogLevel = "WGUI_LOG_LEVEL" ServerAddressesEnvVar = "WGUI_SERVER_INTERFACE_ADDRESSES" From f3ed766bc420472b39d64f7aa121e367f30521b3 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 24 May 2023 12:08:12 +0200 Subject: [PATCH 112/162] Update stale.yml --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index 2a79225..5e6b505 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -39,7 +39,7 @@ markComment: > limitPerRun: 30 # Limit to only `issues` or `pulls` -# only: issues +only: issues # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': # pulls: From 86e52c58681c9b40f82062dc95007aeec8083eda Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 24 May 2023 17:51:44 +0200 Subject: [PATCH 113/162] Add docker build workflow --- .github/workflows/docker-build.yml | 99 ++++++++++++++++++++++++++++++ Dockerfile | 6 +- Jenkinsfile | 2 - 3 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/docker-build.yml delete mode 100644 Jenkinsfile diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..b4bc91f --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,99 @@ +name: Build container images + +on: + push: + branches: + - "master" + tags: + - "*" + +jobs: + build-image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + # set environment + - name: Set APP_VERSION env + run: echo "APP_VERSION=$(echo ${GITHUB_REF} | rev | cut -d'/' -f 1 | rev )" >> $GITHUB_ENV + + - name: Set BUILD_TIME env + run: echo "BUILD_TIME=$(date)" >> $GITHUB_ENV + + - name: Set COMMIT env + run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + + - name: Environment printer + uses: managedkaos/print-env@v1.0 + + - name: Prepare image tags + id: image-tags + run: | + base=ngoduykhanh/wireguard-ui + app_version=dev + + ## Set git tag as image tag + ## + if [[ '${{ github.ref }}' == *"refs/tags/"* ]]; then + github_tag="${GITHUB_REF#refs/*/}" + + SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" + if [[ "$github_tag" =~ $SEMVER_REGEX ]]; then + github_tag=$(echo "${github_tag}" | sed 's/^v//') + fi + + app_version=${github_tag} + + container_images=$(cat <> $GITHUB_OUTPUT + echo "$container_images" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + ## Set APP_VERSION env + # + echo "APP_VERSION=${app_version}" >> $GITHUB_ENV + + # set up docker and build images + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v4 + with: + push: true + context: . + platforms: linux/amd64,linux/arm/v7 + tags: ${{ steps.image-tags.outputs.container_images }} + build-args: | + APP_VERSION=${{ env.APP_VERSION }} + BUILD_TIME=${{ env.BUILD_TIME }} + COMMIT=${{ env.COMMIT }} diff --git a/Dockerfile b/Dockerfile index 8bc96e8..945623d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,9 @@ LABEL maintainer="Khanh Ngo Date: Wed, 24 May 2023 17:58:20 +0200 Subject: [PATCH 114/162] GHA fixes --- .github/workflows/docker-build.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index b4bc91f..eba1973 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -14,9 +14,6 @@ jobs: - uses: actions/checkout@v2 # set environment - - name: Set APP_VERSION env - run: echo "APP_VERSION=$(echo ${GITHUB_REF} | rev | cut -d'/' -f 1 | rev )" >> $GITHUB_ENV - - name: Set BUILD_TIME env run: echo "BUILD_TIME=$(date)" >> $GITHUB_ENV @@ -45,17 +42,17 @@ jobs: app_version=${github_tag} container_images=$(cat < Date: Wed, 24 May 2023 18:00:48 +0200 Subject: [PATCH 115/162] GHA fixes --- .github/workflows/docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index eba1973..986980c 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -93,4 +93,4 @@ jobs: build-args: | APP_VERSION=${{ env.APP_VERSION }} BUILD_TIME=${{ env.BUILD_TIME }} - COMMIT=${{ env.COMMIT }} + COMMIT=${{ env.SHORT_SHA }} From 5183bb5093123a90d523a8aba3f3a81ea2b40f93 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 24 May 2023 18:07:50 +0200 Subject: [PATCH 116/162] GHA fixes --- .github/workflows/docker-build.yml | 2 ++ Dockerfile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 986980c..ee76b79 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -94,3 +94,5 @@ jobs: APP_VERSION=${{ env.APP_VERSION }} BUILD_TIME=${{ env.BUILD_TIME }} COMMIT=${{ env.SHORT_SHA }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 945623d..69192a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ COPY . /build RUN cp -r /build/custom/ assets/ # Build -RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X main.appVersion=${APP_VERSION} -X main.buildTime=${BUILD_TIME} -X main.gitCommit=${COMMIT}" -a -o wg-ui . +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X 'main.appVersion=${APP_VERSION}' -X 'main.buildTime=${BUILD_TIME}' -X 'main.gitCommit=${COMMIT}'" -a -o wg-ui . # Release stage FROM alpine:3.16 From 8ac33a027801036966cb3a3a21d7d32f42d927e5 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Wed, 24 May 2023 21:21:44 +0200 Subject: [PATCH 117/162] GHA fixes --- .github/workflows/docker-build.yml | 12 ++++++------ .github/workflows/release.yml | 4 ++-- Dockerfile | 15 ++++++++------- README.md | 4 ++-- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index ee76b79..0875223 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -9,16 +9,16 @@ on: jobs: build-image: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # set environment - name: Set BUILD_TIME env run: echo "BUILD_TIME=$(date)" >> $GITHUB_ENV - - name: Set COMMIT env - run: echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + - name: Set GIT_COMMIT env + run: echo "GIT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - name: Environment printer uses: managedkaos/print-env@v1.0 @@ -88,11 +88,11 @@ jobs: with: push: true context: . - platforms: linux/amd64,linux/arm/v7 + platforms: linux/amd64,linux/arm/v7,linux/arm64 tags: ${{ steps.image-tags.outputs.container_images }} build-args: | APP_VERSION=${{ env.APP_VERSION }} BUILD_TIME=${{ env.BUILD_TIME }} - COMMIT=${{ env.SHORT_SHA }} + GIT_COMMIT=${{ env.GIT_COMMIT }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feb4c93..a00dbca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: releases-matrix: name: Release Go Binary - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: # build and publish in parallel: linux/386, linux/amd64, darwin/386, darwin/amd64 @@ -24,7 +24,7 @@ jobs: - 7 steps: # get the source code - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # set environment - name: Set APP_VERSION env diff --git a/Dockerfile b/Dockerfile index 69192a9..94823e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,13 @@ # Build stage -FROM golang:1.17-alpine3.16 as builder -LABEL maintainer="Khanh Ngo Date: Sat, 3 Jun 2023 10:40:24 +0200 Subject: [PATCH 118/162] chore: remove healthcheck from Dockerfile (#382) --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 94823e5..c6e149f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,5 +73,4 @@ RUN chmod +x wg-ui COPY init.sh . EXPOSE 5000/tcp -HEALTHCHECK CMD ["wget","--output-document=-","--quiet","--tries=1","http://127.0.0.1:$(echo ${BIND_ADDRESS:-5000} | cut -d ':' -f2)/_health"] ENTRYPOINT ["./init.sh"] From 39324c5cf9b90d3a9c9c10a09d270a64cff6bee9 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Tue, 6 Jun 2023 21:09:12 +0200 Subject: [PATCH 119/162] Add .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..28cee3f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.html linguist-detectable=false From b9e5ddf1946456d042e3d619e196739c2ba147ce Mon Sep 17 00:00:00 2001 From: A A R I X Date: Tue, 6 Jun 2023 21:11:43 +0200 Subject: [PATCH 120/162] Added BIND_ADDRESS environment variable to the project README (#384) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dd9c73c..6a48efb 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ docker-compose up | 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` | From 28f3e820f0c428b0ed07606aff15c682d01a1eaf Mon Sep 17 00:00:00 2001 From: Gabriel Klavans Date: Thu, 22 Jun 2023 15:51:38 -0400 Subject: [PATCH 121/162] Remove duplicate env var entry (#392) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6a48efb..0da4652 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ docker-compose up | `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_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` | From 6bbe230fe8a25844cebe9997a5acf4645033f9b5 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen <50922013+catmandx@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:42:39 +0700 Subject: [PATCH 122/162] [Vulnerability] Cross site scripting (XSS) and Open Redirect on the login page (#396) --- templates/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/login.html b/templates/login.html index bc5ace4..c0a96b9 100644 --- a/templates/login.html +++ b/templates/login.html @@ -84,7 +84,7 @@ function redirectNext() { const urlParams = new URLSearchParams(window.location.search); const nextURL = urlParams.get('next'); - if (nextURL) { + if (nextURL && /(?:^\/[a-zA-Z_])|(?:^\/$)/.test(nextURL.trim())) { window.location.href = nextURL; } else { window.location.href = '/{{.basePath}}'; From 7488f283c462d7dcc26dcd9dd6cb0bc9b1610e4a Mon Sep 17 00:00:00 2001 From: Cameron <113076756+cameronaw13@users.noreply.github.com> Date: Fri, 11 Aug 2023 01:25:56 -0700 Subject: [PATCH 123/162] secure jsondb user perms (#404) --- store/jsondb/jsondb.go | 37 ++++++++++++++++++++++++------ store/jsondb/jsondb_wake_on_lan.go | 9 +++++++- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index cb831ca..0b674b0 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -68,6 +68,7 @@ func (o *JsonDB) Init() error { serverInterface.PostDown = util.LookupEnvOrString(util.ServerPostDownScriptEnvVar, "") serverInterface.UpdatedAt = time.Now().UTC() o.conn.Write("server", "interfaces", serverInterface) + os.Chmod(serverInterfacePath, 0600) } // server's key pair @@ -82,6 +83,7 @@ func (o *JsonDB) Init() error { serverKeyPair.PublicKey = key.PublicKey().String() serverKeyPair.UpdatedAt = time.Now().UTC() o.conn.Write("server", "keypair", serverKeyPair) + os.Chmod(serverKeyPairPath, 0600) } // global settings @@ -106,14 +108,16 @@ func (o *JsonDB) Init() error { globalSetting.ConfigFilePath = util.LookupEnvOrString(util.ConfigFilePathEnvVar, util.DefaultConfigFilePath) globalSetting.UpdatedAt = time.Now().UTC() o.conn.Write("server", "global_settings", globalSetting) + os.Chmod(globalSettingPath, 0600) } - + // hashes if _, err := os.Stat(hashesPath); os.IsNotExist(err) { clientServerHashes := new(model.ClientServerHashes) clientServerHashes.Client = "none" clientServerHashes.Server = "none" o.conn.Write("server", "hashes", clientServerHashes) + os.Chmod(hashesPath, 0600) } // user info @@ -132,6 +136,7 @@ func (o *JsonDB) Init() error { user.PasswordHash = hash } o.conn.Write("users", user.Username, user) + os.Chmod(path.Join(path.Join(o.dbPath, "users"), user.Username+".json"), 0600) } return nil @@ -175,7 +180,10 @@ func (o *JsonDB) GetUserByName(username string) (model.User, error) { // SaveUser func to save user in the database 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) + os.Chmod(userPath, 0600) + return output } // DeleteUser func to remove user from the database @@ -285,7 +293,10 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti } 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) + os.Chmod(clientPath, 0600) + return output } func (o *JsonDB) DeleteClient(clientID string) error { @@ -293,15 +304,24 @@ func (o *JsonDB) DeleteClient(clientID string) 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) + os.Chmod(serverInterfacePath, 0600) + return output } 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) + os.Chmod(serverKeyPairPath, 0600) + return output } 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) + os.Chmod(globalSettingsPath, 0600) + return output } func (o *JsonDB) GetPath() string { @@ -314,5 +334,8 @@ func (o *JsonDB) GetHashes() (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) + os.Chmod(hashesPath, 0600) + return output } diff --git a/store/jsondb/jsondb_wake_on_lan.go b/store/jsondb/jsondb_wake_on_lan.go index e492aa8..a0ee935 100644 --- a/store/jsondb/jsondb_wake_on_lan.go +++ b/store/jsondb/jsondb_wake_on_lan.go @@ -3,6 +3,9 @@ package jsondb import ( "encoding/json" "fmt" + "os" + "path" + "github.com/ngoduykhanh/wireguard-ui/model" ) @@ -65,7 +68,11 @@ func (o *JsonDB) SaveWakeOnLanHost(host model.WakeOnLanHost) error { 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) + os.Chmod(wakeOnLanHostPath, 0600) + return output + } func (o *JsonDB) DeleteWakeOnHost(host model.WakeOnLanHost) error { From 364a43e3dcc5a9598827a166036346be8ce08094 Mon Sep 17 00:00:00 2001 From: Paul Dee Date: Fri, 11 Aug 2023 10:34:11 +0200 Subject: [PATCH 124/162] Implement updating a client Pub+PSK when editing a client (#401) This covers the normal use-case where clients generate keys locally on their device and notify the server of their new/updated keys. The server verifies Preshared and Public keys independently of each other. Should a client generate a new tunnel which lacks a PSK and send only a Public key to the server (admin) where the earlier server created profile has a Preshared key, the server admin/user must determine the course of action: keep or remove the PSK. --- handler/routes.go | 41 +++++++++++++++++++++++++++++++++++++++++ templates/clients.html | 30 +++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/handler/routes.go b/handler/routes.go index e443af9..a2ceeaf 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -567,6 +567,45 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { 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 client.Name = _client.Name client.Email = _client.Email @@ -575,6 +614,8 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { client.AllocatedIPs = _client.AllocatedIPs client.AllowedIPs = _client.AllowedIPs client.ExtraAllowedIPs = _client.ExtraAllowedIPs + client.PublicKey = _client.PublicKey + client.PresharedKey = _client.PresharedKey client.UpdatedAt = time.Now().UTC() // write to the database diff --git a/templates/clients.html b/templates/clients.html index 94ab634..bcd5855 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -129,6 +129,26 @@ Wireguard Clients
+
+ Public and Preshared Keys + + + +
+ + +
+
+ + +
+
+
+ + +
@@ -413,6 +417,7 @@ const email = $("#client_email").val(); const allocated_ips = $("#client_allocated_ips").val().split(","); const allowed_ips = $("#client_allowed_ips").val().split(","); + const endpoint = $("#client_endpoint").val(); let use_server_dns = false; let extra_allowed_ips = []; @@ -434,7 +439,7 @@ const preshared_key = $("#client_preshared_key").val(); 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}; $.ajax({ diff --git a/templates/clients.html b/templates/clients.html index bcd5855..ae66ecf 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -113,6 +113,10 @@ Wireguard Clients
+
+ + +
@@ -477,6 +481,8 @@ Wireguard Clients 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("#_enabled").prop("checked", client.enabled); @@ -564,6 +570,8 @@ Wireguard Clients extra_allowed_ips = $("#_client_extra_allowed_ips").val().split(","); } + const endpoint = $("#_client_endpoint").val(); + if ($("#_use_server_dns").is(':checked')){ use_server_dns = true; } @@ -575,7 +583,8 @@ Wireguard Clients } 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, "public_key": public_key, "preshared_key": preshared_key}; + "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({ cache: false, diff --git a/templates/wg.conf b/templates/wg.conf index 81893a8..6b216ce 100644 --- a/templates/wg.conf +++ b/templates/wg.conf @@ -20,6 +20,7 @@ Table = {{ .globalSettings.Table }} # Update at: {{ .Client.UpdatedAt }} [Peer] PublicKey = {{ .Client.PublicKey }} -{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }} -{{end}}AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}} -{{end}}{{end}} +{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}{{end}} +AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}}{{end}} +{{if .Client.Endpoint }}Endpoint = {{ .Client.Endpoint }}{{end}} +{{end}} From af7742bfb32bffef98e861dd0eda67f83dabe569 Mon Sep 17 00:00:00 2001 From: Michael Walter <40073144+MiguSchweiz@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:22:42 +0100 Subject: [PATCH 135/162] Update routes.go (#475) use config file download mime type "txt/conf" to prevent downloaded configs being saved as .txt, instead of wanted .conf. Tested on Android Firefox and Chrome --- handler/routes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/routes.go b/handler/routes.go index 3d1982f..aa5461b 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -729,7 +729,7 @@ func DownloadClient(db store.IStore) echo.HandlerFunc { // set response header for downloading 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) } } From c8623082fec8997ad8a933de79d0958b69a3df02 Mon Sep 17 00:00:00 2001 From: nebulosa2007 <85841412+nebulosa2007@users.noreply.github.com> Date: Mon, 25 Dec 2023 22:23:51 +0300 Subject: [PATCH 136/162] Make Interface PreDown setting. (#480) --- model/server.go | 1 + templates/server.html | 9 ++++++++- templates/wg.conf | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/model/server.go b/model/server.go index 0784eea..0aa804f 100644 --- a/model/server.go +++ b/model/server.go @@ -23,5 +23,6 @@ type ServerInterface struct { ListenPort int `json:"listen_port,string"` // ,string to get listen_port string input as int UpdatedAt time.Time `json:"updated_at"` PostUp string `json:"post_up"` + PreDown string `json:"pre_down"` PostDown string `json:"post_down"` } diff --git a/templates/server.html b/templates/server.html index 366d301..6a24e6e 100644 --- a/templates/server.html +++ b/templates/server.html @@ -42,6 +42,12 @@ Wireguard Server Settings
+
+ + +
+
Date: Tue, 26 Dec 2023 00:25:38 +0500 Subject: [PATCH 137/162] Fixed tag input being too small and unable to fit a CIDR (#483) Co-authored-by: 0xCA --- templates/base.html | 3 +++ templates/clients.html | 3 +++ templates/global_settings.html | 1 + templates/server.html | 1 + 4 files changed, 8 insertions(+) diff --git a/templates/base.html b/templates/base.html index 88d7568..c2fa367 100644 --- a/templates/base.html +++ b/templates/base.html @@ -497,6 +497,7 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -508,6 +509,7 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -518,6 +520,7 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); diff --git a/templates/clients.html b/templates/clients.html index ae66ecf..8b4e4ab 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -427,6 +427,7 @@ Wireguard Clients 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -438,6 +439,7 @@ Wireguard Clients 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -448,6 +450,7 @@ Wireguard Clients 'defaultText': 'Add More', 'removeWithBackspace' : true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }) diff --git a/templates/global_settings.html b/templates/global_settings.html index cafb630..73b3c93 100644 --- a/templates/global_settings.html +++ b/templates/global_settings.html @@ -203,6 +203,7 @@ Global Settings 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); diff --git a/templates/server.html b/templates/server.html index 6a24e6e..e1116a6 100644 --- a/templates/server.html +++ b/templates/server.html @@ -167,6 +167,7 @@ Wireguard Server Settings 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, + 'minInputWidth': '100%', 'placeholderColor': '#666666' }); From e73047b14fbcf9799e00641991ba092fb0c3becd Mon Sep 17 00:00:00 2001 From: Vahid Date: Mon, 25 Dec 2023 23:01:11 +0330 Subject: [PATCH 138/162] Feature: Unix domain socket support (#492) Co-authored-by: Khanh Ngo --- .gitignore | 3 +++ README.md | 2 +- main.go | 18 +++++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f1e3f44..610b977 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ node_modules/ .vscode .idea +# Vim +.*.sw[op] + # Examples examples/docker-compose/config examples/docker-compose/db diff --git a/README.md b/README.md index f7c41f8..d585868 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ docker-compose up | 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 | +| `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 | | `WGUI_USERNAME` | The username for the login page. Used for db initialization only | `admin` | diff --git a/main.go b/main.go index b226ccc..fd4bc90 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,9 @@ import ( "net/http" "os" "time" + "strings" + "net" + "syscall" "github.com/labstack/echo/v4" "github.com/labstack/gommon/log" @@ -229,7 +232,20 @@ func main() { // serves other static files 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) { From a9be53899c292ce3d66361145522016105e2cd01 Mon Sep 17 00:00:00 2001 From: 0xCA <0xCA@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:08:55 +0500 Subject: [PATCH 139/162] Subnet range selector, interface fixes (#481) --- README.md | 1 + custom/js/helper.js | 6 ++ handler/routes.go | 55 ++++++++++-- main.go | 16 ++++ model/client.go | 1 + templates/base.html | 72 +++++++++++++-- templates/clients.html | 124 +++++++++++++++++++++++++- templates/global_settings.html | 1 - templates/server.html | 1 - util/cache.go | 3 + util/config.go | 83 ++++++++++++++---- util/util.go | 154 ++++++++++++++++++++++++++++++++- 12 files changed, 473 insertions(+), 44 deletions(-) create mode 100644 util/cache.go diff --git a/README.md b/README.md index d585868..c43adc3 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ docker-compose up | `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 | +| `SUBNET_RANGES` | The list of address subdivision ranges. Format: `SR Name:10.0.1.0/24; SR2:10.0.2.0/24,10.0.3.0/24` Each CIDR must be inside one of the server interfaces. | 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 | diff --git a/custom/js/helper.js b/custom/js/helper.js index 39bc1fa..4f21c1c 100644 --- a/custom/js/helper.js +++ b/custom/js/helper.js @@ -18,6 +18,11 @@ function renderClientList(data) { allowedIpsHtml += `${obj} `; }) + let subnetRangesString = ""; + if (obj.Client.subnet_ranges && obj.Client.subnet_ranges.length > 0) { + subnetRangesString = obj.Client.subnet_ranges.join(',') + } + // render client html content let html = `
@@ -59,6 +64,7 @@ function renderClientList(data) {
${obj.Client.name} + ${obj.Client.email} ${prettyDateTime(obj.Client.created_at)} diff --git a/handler/routes.go b/handler/routes.go index aa5461b..0358750 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -366,6 +366,10 @@ func GetClients(db store.IStore) echo.HandlerFunc { }) } + for i, clientData := range clientDataList { + clientDataList[i] = util.FillClientSubnetRange(clientData) + } + return c.JSON(http.StatusOK, clientDataList) } } @@ -391,7 +395,7 @@ func GetClient(db store.IStore) echo.HandlerFunc { return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) } - return c.JSON(http.StatusOK, clientData) + return c.JSON(http.StatusOK, util.FillClientSubnetRange(clientData)) } } @@ -988,6 +992,13 @@ func MachineIPAddresses() echo.HandlerFunc { } } +// GetOrderedSubnetRanges handler to get the ordered list of subnet ranges +func GetOrderedSubnetRanges() echo.HandlerFunc { + return func(c echo.Context) error { + return c.JSON(http.StatusOK, util.SubnetRangesOrder) + } +} + // SuggestIPAllocation handler to get the list of ip address for client func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { @@ -1009,22 +1020,48 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { false, "Cannot suggest ip allocation: failed to get list of allocated ip addresses", }) } - for _, cidr := range server.Interface.Addresses { - ip, err := util.GetAvailableIP(cidr, allocatedIPs) + + sr := c.QueryParam("sr") + searchCIDRList := make([]string, 0) + found := false + + // Use subnet range or default to interface addresses + if util.SubnetRanges[sr] != nil { + for _, cidr := range util.SubnetRanges[sr] { + searchCIDRList = append(searchCIDRList, cidr.String()) + } + } else { + searchCIDRList = append(searchCIDRList, server.Interface.Addresses...) + } + + // Save only unique IPs + ipSet := make(map[string]struct{}) + + for _, cidr := range searchCIDRList { + ip, err := util.GetAvailableIP(cidr, allocatedIPs, server.Interface.Addresses) if err != nil { log.Error("Failed to get available ip from a CIDR: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ - false, - fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr), - }) + continue } + found = true if strings.Contains(ip, ":") { - suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip)) + ipSet[fmt.Sprintf("%s/128", ip)] = struct{}{} } else { - suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/32", ip)) + ipSet[fmt.Sprintf("%s/32", ip)] = struct{}{} } } + if !found { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ + false, + "Cannot suggest ip allocation: failed to get available ip. Try a different subnet or deallocate some ips.", + }) + } + + for ip := range ipSet { + suggestedIPs = append(suggestedIPs, ip) + } + return c.JSON(http.StatusOK, suggestedIPs) } } diff --git a/main.go b/main.go index fd4bc90..5f6e341 100644 --- a/main.go +++ b/main.go @@ -45,6 +45,7 @@ var ( flagSessionSecret string = util.RandomString(32) flagWgConfTemplate string flagBasePath string + flagSubnetRanges string ) const ( @@ -81,6 +82,7 @@ func init() { flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.") 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(&flagSubnetRanges, "subnet-ranges", util.LookupEnvOrString("SUBNET_RANGES", flagSubnetRanges), "IP ranges to choose from when assigning an IP for a client.") var ( smtpPasswordLookup = util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword) @@ -127,6 +129,7 @@ func init() { util.SessionSecret = []byte(flagSessionSecret) util.WgConfTemplate = flagWgConfTemplate util.BasePath = util.ParseBasePath(flagBasePath) + util.SubnetRanges = util.ParseSubnetRanges(flagSubnetRanges) // print only if log level is INFO or lower if lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")); lvl <= log.INFO { @@ -145,6 +148,7 @@ func init() { //fmt.Println("Session secret\t:", util.SessionSecret) fmt.Println("Custom wg.conf\t:", util.WgConfTemplate) fmt.Println("Base path\t:", util.BasePath+"/") + fmt.Println("Subnet ranges\t:", util.GetSubnetRangesString()) } } @@ -170,6 +174,17 @@ func main() { // create the wireguard config on start, if it doesn't exist initServerConfig(db, tmplDir) + // Check if subnet ranges are valid for the server configuration + // Remove any non-valid CIDRs + if err := util.ValidateAndFixSubnetRanges(db); err != nil { + panic(err) + } + + // Print valid ranges + if lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")); lvl <= log.INFO { + fmt.Println("Valid subnet ranges:", util.GetSubnetRangesString()) + } + // register routes app := router.New(tmplDir, extraData, util.SessionSecret) @@ -218,6 +233,7 @@ func main() { app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) app.GET(util.BasePath+"/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession) + app.GET(util.BasePath+"/api/subnet-ranges", handler.GetOrderedSubnetRanges(), handler.ValidSession) app.GET(util.BasePath+"/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession) app.POST(util.BasePath+"/api/apply-wg-config", handler.ApplyServerConfig(db, tmplDir), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession) diff --git a/model/client.go b/model/client.go index 95342f0..187ec72 100644 --- a/model/client.go +++ b/model/client.go @@ -12,6 +12,7 @@ type Client struct { PresharedKey string `json:"preshared_key"` Name string `json:"name"` Email string `json:"email"` + SubnetRanges []string `json:"subnet_ranges,omitempty"` AllocatedIPs []string `json:"allocated_ips"` AllowedIPs []string `json:"allowed_ips"` ExtraAllowedIPs []string `json:"extra_allowed_ips"` diff --git a/templates/base.html b/templates/base.html index c2fa367..3f34140 100644 --- a/templates/base.html +++ b/templates/base.html @@ -58,11 +58,13 @@
@@ -209,6 +211,12 @@
+
+ + +
@@ -368,6 +376,36 @@ $(document).ready(function () { + addGlobalStyle(` +.toast-top-right-fix { + top: 67px; + right: 12px; +} + `, 'toastrToastStyleFix') + + toastr.options.closeDuration = 100; + // toastr.options.timeOut = 10000; + toastr.options.positionClass = 'toast-top-right-fix'; + + updateApplyConfigVisibility() + // from clients.html + updateSearchList() + + }); + + function addGlobalStyle(css, id) { + if (!document.querySelector('#' + id)) { + let head = document.head + if (!head) { return } + let style = document.createElement('style') + style.type = 'text/css' + style.id = id + style.innerHTML = css + head.appendChild(style) + } + } + + function updateApplyConfigVisibility() { $.ajax({ cache: false, method: 'GET', @@ -388,8 +426,7 @@ toastr.error(responseJson['message']); } }); - - }); + } // populateClient function for render new client info @@ -456,6 +493,7 @@ if (window.location.pathname === "{{.basePath}}/") { populateClient(resp.id); } + updateApplyConfigVisibility() }, error: function(jqXHR, exception) { const responseJson = jQuery.parseJSON(jqXHR.responseText); @@ -466,19 +504,32 @@ // updateIPAllocationSuggestion function for automatically fill // the IP Allocation input with suggested ip addresses - function updateIPAllocationSuggestion() { + function updateIPAllocationSuggestion(forceDefault = false) { + let subnetRange = $("#subnet_ranges").select2('val'); + + if (forceDefault || !subnetRange || subnetRange.length === 0) { + subnetRange = '__default_any__' + } $.ajax({ cache: false, method: 'GET', - url: '{{.basePath}}/api/suggest-client-ips', + url: `{{.basePath}}/api/suggest-client-ips?sr=${subnetRange}`, dataType: 'json', contentType: "application/json", success: function(data) { + const allocated_ips = $("#client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#client_allocated_ips').removeTag(escape(item)); + }) data.forEach(function (item, index) { $('#client_allocated_ips').addTag(item); }) }, error: function(jqXHR, exception) { + const allocated_ips = $("#client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#client_allocated_ips').removeTag(escape(item)); + }) const responseJson = jQuery.parseJSON(jqXHR.responseText); toastr.error(responseJson['message']); } @@ -497,7 +548,6 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -509,7 +559,6 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -520,7 +569,6 @@ 'defaultText': 'Add More', 'removeWithBackspace': true, 'minChars': 0, - 'minInputWidth': '100%', 'placeholderColor': '#666666' }); @@ -565,10 +613,17 @@ $("#client_preshared_key").val(""); $("#client_allocated_ips").importTags(''); $("#client_extra_allowed_ips").importTags(''); - updateIPAllocationSuggestion(); + updateSubnetRangesList("#subnet_ranges"); + updateIPAllocationSuggestion(true); }); }); + // handle subnet range select + $('#subnet_ranges').on('select2:select', function (e) { + // console.log('Selected Option: ', $("#subnet_ranges").select2('val')); + updateIPAllocationSuggestion(); + }); + // apply_config_confirm button event $(document).ready(function () { $("#apply_config_confirm").click(function () { @@ -579,6 +634,7 @@ dataType: 'json', contentType: "application/json", success: function(data) { + updateApplyConfigVisibility() $("#modal_apply_config").modal('hide'); toastr.success('Applied config successfully'); }, diff --git a/templates/clients.html b/templates/clients.html index 8b4e4ab..1f8f26d 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -100,6 +100,12 @@ Wireguard Clients
+
+ + +
@@ -253,13 +259,102 @@ Wireguard Clients setClientStatus(clientID, true); const divElement = document.getElementById("paused_" + clientID); divElement.style.visibility = "hidden"; + updateApplyConfigVisibility() } function pauseClient(clientID) { setClientStatus(clientID, false); const divElement = document.getElementById("paused_" + clientID); divElement.style.visibility = "visible"; + updateApplyConfigVisibility() } + + // updateIPAllocationSuggestion function for automatically fill + // the IP Allocation input with suggested ip addresses + // FOR CHANGING A SUBNET OF AN EXISTING CLIENT + function updateIPAllocationSuggestionExisting() { + let subnetRange = $("#_subnet_ranges").select2('val'); + + if (!subnetRange || subnetRange.length === 0) { + subnetRange = '__default_any__' + } + $.ajax({ + cache: false, + method: 'GET', + url: `{{.basePath}}/api/suggest-client-ips?sr=${subnetRange}`, + dataType: 'json', + contentType: "application/json", + success: function(data) { + const allocated_ips = $("#_client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#_client_allocated_ips').removeTag(escape(item)); + }) + data.forEach(function (item, index) { + $('#_client_allocated_ips').addTag(item); + }) + }, + error: function(jqXHR, exception) { + const allocated_ips = $("#_client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#_client_allocated_ips').removeTag(escape(item)); + }) + const responseJson = jQuery.parseJSON(jqXHR.responseText); + toastr.error(responseJson['message']); + } + }); + } + + function updateSubnetRangesList(elementID, preselectedVal) { + $.getJSON("{{.basePath}}/api/subnet-ranges", null, function(data) { + $(`${elementID} option`).remove(); + $(elementID).append( + $("") + .text("Any") + .val("__default_any__") + ); + $.each(data, function(index, item) { + $(elementID).append( + $("") + .text(item) + .val(item) + ); + if (item === preselectedVal) { + console.log(preselectedVal); + $(elementID).val(preselectedVal).trigger('change') + } + }); + }); + } + + function updateSearchList() { + $.getJSON("{{.basePath}}/api/subnet-ranges", null, function(data) { + $("#status-selector option").remove(); + $("#status-selector").append( + $("") + .text("All") + .val("All"), + $("") + .text("Enabled") + .val("Enabled"), + $("") + .text("Disabled") + .val("Disabled"), + $("") + .text("Connected") + .val("Connected"), + $("") + .text("Disconnected") + .val("Disconnected") + ); + $.each(data, function(index, item) { + $("#status-selector").append( + $("") + .text(item) + .val(item) + ); + }); + }); +} diff --git a/templates/wg.conf b/templates/wg.conf index 2112a0c..793876e 100644 --- a/templates/wg.conf +++ b/templates/wg.conf @@ -17,6 +17,7 @@ Table = {{ .globalSettings.Table }} # ID: {{ .Client.ID }} # Name: {{ .Client.Name }} # Email: {{ .Client.Email }} +# Telegram: {{ .Client.TgUserid }} # Created at: {{ .Client.CreatedAt }} # Update at: {{ .Client.UpdatedAt }} [Peer] diff --git a/util/cache.go b/util/cache.go index 340f73d..8037c30 100644 --- a/util/cache.go +++ b/util/cache.go @@ -1,3 +1,7 @@ package util +import "sync" + var IPToSubnetRange = map[string]uint16{} +var TgUseridToClientID = map[int64]([]string){} +var TgUseridToClientIDMutex sync.RWMutex diff --git a/util/util.go b/util/util.go index fa168fd..337745a 100644 --- a/util/util.go +++ b/util/util.go @@ -19,6 +19,8 @@ import ( "time" "github.com/ngoduykhanh/wireguard-ui/store" + "github.com/ngoduykhanh/wireguard-ui/telegram" + "github.com/skip2/go-qrcode" "golang.org/x/mod/sumdb/dirhash" externalip "github.com/glendc/go-external-ip" @@ -27,6 +29,12 @@ import ( "github.com/sdomino/scribble" ) +var qrCodeSettings = model.QRCodeSettings{ + Enabled: true, + IncludeDNS: true, + IncludeMTU: true, +} + // BuildClientConfig to create wireguard client config string func BuildClientConfig(client model.Client, server model.Server, setting model.GlobalSetting) string { // Interface section @@ -578,6 +586,57 @@ func WriteWireGuardServerConfig(tmplDir fs.FS, serverConfig model.Server, client return nil } +// SendRequestedConfigsToTelegram to send client all their configs. Returns failed configs list. +func SendRequestedConfigsToTelegram(db store.IStore, userid int64) []string { + failedList := make([]string, 0) + TgUseridToClientIDMutex.RLock() + if clids, found := TgUseridToClientID[userid]; found && len(clids) > 0 { + TgUseridToClientIDMutex.RUnlock() + + for _, clid := range clids { + clientData, err := db.GetClientByID(clid, qrCodeSettings) + if err != nil { + // return fmt.Errorf("unable to get client") + failedList = append(failedList, clid) + continue + } + + // build config + server, _ := db.GetServer() + globalSettings, _ := db.GetGlobalSettings() + config := BuildClientConfig(*clientData.Client, server, globalSettings) + configData := []byte(config) + var qrData []byte + + if clientData.Client.PrivateKey != "" { + qrData, err = qrcode.Encode(config, qrcode.Medium, 512) + if err != nil { + // return fmt.Errorf("unable to encode qr") + failedList = append(failedList, clientData.Client.Name) + continue + } + } + + userid, err := strconv.ParseInt(clientData.Client.TgUserid, 10, 64) + if err != nil { + // return fmt.Errorf("tg usrid is unreadable") + failedList = append(failedList, clientData.Client.Name) + continue + } + + err = telegram.SendConfig(userid, clientData.Client.Name, configData, qrData, true) + if err != nil { + failedList = append(failedList, clientData.Client.Name) + continue + } + time.Sleep(2 * time.Second) + } + } else { + TgUseridToClientIDMutex.RUnlock() + } + return failedList +} + func LookupEnvOrString(key string, defaultVal string) string { if val, ok := os.LookupEnv(key); ok { return val @@ -707,3 +766,65 @@ func ManagePerms(path string) error { err := os.Chmod(path, 0600) return err } + +func AddTgToClientID(userid int64, clientID string) { + TgUseridToClientIDMutex.Lock() + defer TgUseridToClientIDMutex.Unlock() + + if _, ok := TgUseridToClientID[userid]; ok && TgUseridToClientID[userid] != nil { + TgUseridToClientID[userid] = append(TgUseridToClientID[userid], clientID) + } else { + TgUseridToClientID[userid] = []string{clientID} + } +} + +func UpdateTgToClientID(userid int64, clientID string) { + TgUseridToClientIDMutex.Lock() + defer TgUseridToClientIDMutex.Unlock() + + // Detach clientID from any existing userid + for uid, cls := range TgUseridToClientID { + if cls != nil { + filtered := filterStringSlice(cls, clientID) + if len(filtered) > 0 { + TgUseridToClientID[uid] = filtered + } else { + delete(TgUseridToClientID, uid) + } + } + } + + // Attach it to the new one + if _, ok := TgUseridToClientID[userid]; ok && TgUseridToClientID[userid] != nil { + TgUseridToClientID[userid] = append(TgUseridToClientID[userid], clientID) + } else { + TgUseridToClientID[userid] = []string{clientID} + } +} + +func RemoveTgToClientID(clientID string) { + TgUseridToClientIDMutex.Lock() + defer TgUseridToClientIDMutex.Unlock() + + // Detach clientID from any existing userid + for uid, cls := range TgUseridToClientID { + if cls != nil { + filtered := filterStringSlice(cls, clientID) + if len(filtered) > 0 { + TgUseridToClientID[uid] = filtered + } else { + delete(TgUseridToClientID, uid) + } + } + } +} + +func filterStringSlice(s []string, excludedStr string) []string { + filtered := s[:0] + for _, v := range s { + if v != excludedStr { + filtered = append(filtered, v) + } + } + return filtered +} From 4ffd7319f8584dfac08141af7ab2f0418e5de7b2 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Fri, 29 Dec 2023 09:54:51 +0100 Subject: [PATCH 147/162] Upgrade dependencies (#511) - Upgrade GitHub Actions - Upgrade Go version - Upgrade Alpine image --- .github/workflows/docker-build.yml | 8 +- Dockerfile | 4 +- go.mod | 62 ++++-- go.sum | 312 ++++++++--------------------- 4 files changed, 136 insertions(+), 250 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 0875223..47418d7 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -72,19 +72,19 @@ jobs: # set up docker and build images - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: push: true context: . diff --git a/Dockerfile b/Dockerfile index c6e149f..33da3fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine3.16 as builder +FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.21-alpine3.19 AS builder LABEL maintainer="Khanh Ngo " ARG BUILDPLATFORM @@ -56,7 +56,7 @@ RUN cp -r /build/custom/ assets/ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-X 'main.appVersion=${APP_VERSION}' -X 'main.buildTime=${BUILD_TIME}' -X 'main.gitCommit=${GIT_COMMIT}'" -a -o wg-ui . # Release stage -FROM alpine:3.16 +FROM alpine:3.19 RUN addgroup -S wgui && \ adduser -S -D -G wgui wgui diff --git a/go.mod b/go.mod index cb3d6b8..ea102f7 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,52 @@ module github.com/ngoduykhanh/wireguard-ui -go 1.16 +go 1.21 require ( github.com/NicoNex/echotron/v3 v3.27.0 - github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/gorilla/sessions v1.2.0 - github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 // indirect - github.com/labstack/echo-contrib v0.9.0 - github.com/labstack/echo/v4 v4.1.16 - github.com/labstack/gommon v0.3.0 - github.com/leodido/go-urn v1.2.0 // indirect - github.com/rs/xid v1.2.1 + github.com/glendc/go-external-ip v0.1.0 + github.com/gorilla/sessions v1.2.2 + github.com/labstack/echo-contrib v0.15.0 + github.com/labstack/echo/v4 v4.11.4 + github.com/labstack/gommon v0.4.2 + github.com/rs/xid v1.5.0 github.com/sabhiram/go-wol v0.0.0-20211224004021-c83b0c2f887d - github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba - github.com/sendgrid/rest v2.6.4+incompatible // indirect - github.com/sendgrid/sendgrid-go v3.10.0+incompatible - github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 - github.com/xhit/go-simple-mail/v2 v2.10.0 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 - golang.org/x/mod v0.7.0 + github.com/sdomino/scribble v0.0.0-20230717151034-b95d4df19aa8 + github.com/sendgrid/sendgrid-go v3.14.0+incompatible + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + github.com/xhit/go-simple-mail/v2 v2.16.0 + golang.org/x/crypto v0.17.0 + golang.org/x/mod v0.14.0 //golang.zx2c4.com/wireguard v0.0.20200121 // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 gopkg.in/go-playground/validator.v9 v9.31.0 ) + +require ( + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-test/deep v1.1.0 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/gorilla/context v1.1.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 // indirect + github.com/josharian/native v1.1.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mdlayher/genetlink v1.3.2 // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.5.0 // indirect + github.com/sendgrid/rest v2.6.9+incompatible // indirect + github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect +) diff --git a/go.sum b/go.sum index 90023eb..b160f6a 100644 --- a/go.sum +++ b/go.sum @@ -1,257 +1,119 @@ -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/NicoNex/echotron/v3 v3.27.0 h1:iq4BLPO+Dz1JHjh2HPk0D0NldAZSYcAjaOicgYEhUzw= github.com/NicoNex/echotron/v3 v3.27.0/go.mod h1:LpP5IyHw0y+DZUZMBgXEDAF9O8feXrQu7w7nlJzzoZI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/casbin/casbin/v2 v2.0.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.1-coreos.6.0.20180223184059-4f5275f4ebbf/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd h1:1BzxHapafGJd/XlpMvocLeDBin2EKn90gXv2AQt5sfo= -github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd/go.mod h1:o9OoDQyE1WHvYVUH1FdFapy1/rCZHHq3O5wS4VA83ig= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= -github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= -github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/glendc/go-external-ip v0.1.0 h1:iX3xQ2Q26atAmLTbd++nUce2P5ht5P4uD4V7caSY/xg= +github.com/glendc/go-external-ip v0.1.0/go.mod h1:CNx312s2FLAJoWNdJWZ2Fpf5O4oLsMFwuYviHjS4uJE= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= +github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= github.com/jessevdk/go-flags v0.0.0-20150816100521-1acbbaff2f34/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= -github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= -github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= -github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= -github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do= -github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/labstack/echo-contrib v0.9.0 h1:hKBA2SnxdxR7sghH0J04zq/pImnKRmgvmQ6MvY9hug4= -github.com/labstack/echo-contrib v0.9.0/go.mod h1:TsFE5Vv0LRpZLoh4mMmaaAxzcTH+1CBFiUtVhwlegzU= -github.com/labstack/echo/v4 v4.1.6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE= -github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= -github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= -github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4= -github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= +github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= +github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= +github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY= -github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= -github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= -github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= -github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= -github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= -github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0= -github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= +github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= +github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/sabhiram/go-colorize v0.0.0-20210403184538-366f55d711cf/go.mod h1:GvlEbMJBpbAXFn06UajbdBlGZ18iLvHyuIrgG//L8uk= github.com/sabhiram/go-wol v0.0.0-20211224004021-c83b0c2f887d h1:NDtoSmsxTpDYTqvUurn2ooAzDaYbJSB9/tOhLzaewgo= github.com/sabhiram/go-wol v0.0.0-20211224004021-c83b0c2f887d/go.mod h1:SVPBBd492Gk7Cq5lPd6OAYtIGk2r1FsyH8KT3IB8h7c= -github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba h1:8QAc9wFAf2b/9cAXskm0wBylObZ0bTpRcaP7ThjLPVQ= -github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba/go.mod h1:W6zxGUBCXRR5QugSd/nFcFVmwoGnvpjiNY/JwT03Wew= -github.com/sendgrid/rest v2.6.4+incompatible h1:lq6gAQxLwVBf3mVyCCSHI6mgF+NfaJFJHjT0kl6SSo8= -github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= -github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c= -github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs= -github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= +github.com/sdomino/scribble v0.0.0-20230717151034-b95d4df19aa8 h1:hlNRl87eAZhh2QMJVShuXHL6OOd0ObZM0JozDIruNeM= +github.com/sdomino/scribble v0.0.0-20230717151034-b95d4df19aa8/go.mod h1:W6zxGUBCXRR5QugSd/nFcFVmwoGnvpjiNY/JwT03Wew= +github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= +github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA= +github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v0.0.0-20150929183540-2b15294402a8/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/uber/jaeger-client-go v2.19.1-0.20191002155754-0be28c34dabf+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM= +github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= -github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/xhit/go-simple-mail/v2 v2.10.0 h1:nib6RaJ4qVh5HD9UE9QJqnUZyWp3upv+Z6CFxaMj0V8= -github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA= +github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b h1:XDLXhn7ryprJVo+Lpkiib6CIuXE2031GDwtfEm7vLjI= -golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto= -golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= +golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 45849a2aee46cf81ff982fd809a02614b4d0440c Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Fri, 29 Dec 2023 10:56:37 +0100 Subject: [PATCH 148/162] chore: code adjustment (#512) --- handler/routes.go | 7 ++-- main.go | 59 ++++++++++++++++-------------- store/jsondb/jsondb.go | 24 ++++++------ store/jsondb/jsondb_wake_on_lan.go | 2 +- telegram/bot.go | 14 +++++-- templates/users_settings.html | 2 +- util/cache.go | 2 +- util/hash.go | 3 +- util/util.go | 35 +++++++++--------- 9 files changed, 79 insertions(+), 69 deletions(-) diff --git a/handler/routes.go b/handler/routes.go index 75a6eea..4dc95c6 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -75,7 +75,8 @@ func Login(db store.IStore) echo.HandlerFunc { dbuser, err := db.GetUserByName(username) if err != nil { - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"}) + log.Infof("Cannot query user %s from DB", username) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Invalid credentials"}) } userCorrect := subtle.ConstantTimeCompare([]byte(username), []byte(dbuser.Username)) == 1 @@ -173,7 +174,7 @@ func Logout() echo.HandlerFunc { } // LoadProfile to load user information -func LoadProfile(db store.IStore) echo.HandlerFunc { +func LoadProfile() echo.HandlerFunc { return func(c echo.Context) error { return c.Render(http.StatusOK, "profile.html", map[string]interface{}{ "baseData": model.BaseData{Active: "profile", CurrentUser: currentUser(c), Admin: isAdmin(c)}, @@ -182,7 +183,7 @@ func LoadProfile(db store.IStore) echo.HandlerFunc { } // UsersSettings handler -func UsersSettings(db store.IStore) echo.HandlerFunc { +func UsersSettings() echo.HandlerFunc { return func(c echo.Context) error { return c.Render(http.StatusOK, "users_settings.html", map[string]interface{}{ "baseData": model.BaseData{Active: "users-settings", CurrentUser: currentUser(c), Admin: isAdmin(c)}, diff --git a/main.go b/main.go index a93357c..e11cf29 100644 --- a/main.go +++ b/main.go @@ -31,23 +31,23 @@ var ( gitRef = "N/A" buildTime = fmt.Sprintf(time.Now().UTC().Format("01-02-2006 15:04:05")) // configuration variables - flagDisableLogin bool = false - flagBindAddress string = "0.0.0.0:5000" - flagSmtpHostname string = "127.0.0.1" - flagSmtpPort int = 25 + flagDisableLogin = false + flagBindAddress = "0.0.0.0:5000" + flagSmtpHostname = "127.0.0.1" + flagSmtpPort = 25 flagSmtpUsername string flagSmtpPassword string - flagSmtpAuthType string = "NONE" - flagSmtpNoTLSCheck bool = false - flagSmtpEncryption string = "STARTTLS" - flagSmtpHelo string = "localhost" + flagSmtpAuthType = "NONE" + flagSmtpNoTLSCheck = false + flagSmtpEncryption = "STARTTLS" + flagSmtpHelo = "localhost" flagSendgridApiKey string flagEmailFrom string - flagEmailFromName string = "WireGuard UI" + flagEmailFromName = "WireGuard UI" flagTelegramToken string - flagTelegramAllowConfRequest bool = false - flagTelegramFloodWait int = 60 - flagSessionSecret string = util.RandomString(32) + flagTelegramAllowConfRequest = false + flagTelegramFloodWait = 60 + flagSessionSecret = util.RandomString(32) flagWgConfTemplate string flagBasePath string flagSubnetRanges string @@ -94,9 +94,9 @@ func init() { flag.StringVar(&flagSubnetRanges, "subnet-ranges", util.LookupEnvOrString("SUBNET_RANGES", flagSubnetRanges), "IP ranges to choose from when assigning an IP for a client.") var ( - smtpPasswordLookup = util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword) - sengridApiKeyLookup = util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey) - sessionSecretLookup = util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret) + smtpPasswordLookup = util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword) + sendgridApiKeyLookup = util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey) + sessionSecretLookup = util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret) ) // check empty smtpPassword env var @@ -106,9 +106,9 @@ func init() { 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.") + // check empty sendgridApiKey env var + if sendgridApiKeyLookup != "" { + flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", sendgridApiKeyLookup, "Your sendgrid api key.") } else { flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrFile("SENDGRID_API_KEY_FILE", flagSendgridApiKey), "File containing your sendgrid api key.") } @@ -215,12 +215,12 @@ func main() { app.GET(util.BasePath+"/login", handler.LoginPage()) app.POST(util.BasePath+"/login", handler.Login(db), handler.ContentTypeJson) app.GET(util.BasePath+"/logout", handler.Logout(), 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+"/profile", handler.LoadProfile(), handler.ValidSession) + app.GET(util.BasePath+"/users-settings", handler.UsersSettings(), handler.ValidSession, handler.NeedsAdmin) 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.ContentTypeJson, 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+"/get-users", handler.GetUsers(db), handler.ValidSession, handler.NeedsAdmin) app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession) } @@ -276,10 +276,13 @@ func main() { if strings.HasPrefix(util.BindAddress, "unix://") { // Listen on unix domain socket. // https://github.com/labstack/echo/issues/830 - syscall.Unlink(util.BindAddress[6:]) + err := syscall.Unlink(util.BindAddress[6:]) + if err != nil { + app.Logger.Fatalf("Cannot unlink unix socket: Error: %v", err) + } l, err := net.Listen("unix", util.BindAddress[6:]) if err != nil { - app.Logger.Fatal(err) + app.Logger.Fatalf("Cannot create unix socket. Error: %v", err) } app.Listener = l app.Logger.Fatal(app.Start("")) @@ -292,7 +295,7 @@ func main() { func initServerConfig(db store.IStore, tmplDir fs.FS) { settings, err := db.GetGlobalSettings() if err != nil { - log.Fatalf("Cannot get global settings: ", err) + log.Fatalf("Cannot get global settings: %v", err) } if _, err := os.Stat(settings.ConfigFilePath); err == nil { @@ -302,23 +305,23 @@ func initServerConfig(db store.IStore, tmplDir fs.FS) { server, err := db.GetServer() if err != nil { - log.Fatalf("Cannot get server config: ", err) + log.Fatalf("Cannot get server config: %v", err) } clients, err := db.GetClients(false) if err != nil { - log.Fatalf("Cannot get client config: ", err) + log.Fatalf("Cannot get client config: %v", err) } users, err := db.GetUsers() if err != nil { - log.Fatalf("Cannot get user config: ", err) + log.Fatalf("Cannot get user config: %v", err) } // write config file err = util.WriteWireGuardServerConfig(tmplDir, server, clients, users, settings) if err != nil { - log.Fatalf("Cannot create server config: ", err) + log.Fatalf("Cannot create server config: %v", err) } } diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 03b5b62..8b5f84e 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -37,14 +37,14 @@ func New(dbPath string) (*JsonDB, error) { } func (o *JsonDB) Init() error { - var clientPath string = path.Join(o.dbPath, "clients") - 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 serverInterfacePath string = path.Join(serverPath, "interfaces.json") - var serverKeyPairPath string = path.Join(serverPath, "keypair.json") - var globalSettingPath string = path.Join(serverPath, "global_settings.json") - var hashesPath string = path.Join(serverPath, "hashes.json") + var clientPath = path.Join(o.dbPath, "clients") + var serverPath = path.Join(o.dbPath, "server") + var userPath = path.Join(o.dbPath, "users") + var wakeOnLanHostsPath = path.Join(o.dbPath, "wake_on_lan_hosts") + var serverInterfacePath = path.Join(serverPath, "interfaces.json") + var serverKeyPairPath = path.Join(serverPath, "keypair.json") + var globalSettingPath = path.Join(serverPath, "global_settings.json") + var hashesPath = path.Join(serverPath, "hashes.json") // create directories if they do not exist if _, err := os.Stat(clientPath); os.IsNotExist(err) { @@ -189,7 +189,7 @@ func (o *JsonDB) GetUsers() ([]model.User, error) { for _, i := range results { user := model.User{} - if err := json.Unmarshal([]byte(i), &user); err != nil { + if err := json.Unmarshal(i, &user); err != nil { return users, fmt.Errorf("cannot decode user json structure: %v", err) } users = append(users, user) @@ -267,7 +267,7 @@ func (o *JsonDB) GetClients(hasQRCode bool) ([]model.ClientData, error) { clientData := model.ClientData{} // get client info - if err := json.Unmarshal([]byte(f), &client); err != nil { + if err := json.Unmarshal(f, &client); err != nil { return clients, fmt.Errorf("cannot decode client json structure: %v", err) } @@ -278,7 +278,7 @@ func (o *JsonDB) GetClients(hasQRCode bool) ([]model.ClientData, error) { png, err := qrcode.Encode(util.BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256) if err == nil { - clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png)) + clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString(png) } else { fmt.Print("Cannot generate QR code: ", err) } @@ -315,7 +315,7 @@ func (o *JsonDB) GetClientByID(clientID string, qrCodeSettings model.QRCodeSetti png, err := qrcode.Encode(util.BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256) if err == nil { - clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png)) + clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString(png) } else { fmt.Print("Cannot generate QR code: ", err) } diff --git a/store/jsondb/jsondb_wake_on_lan.go b/store/jsondb/jsondb_wake_on_lan.go index 661ba05..bf43fcf 100644 --- a/store/jsondb/jsondb_wake_on_lan.go +++ b/store/jsondb/jsondb_wake_on_lan.go @@ -23,7 +23,7 @@ func (o *JsonDB) GetWakeOnLanHosts() ([]model.WakeOnLanHost, error) { host := model.WakeOnLanHost{} // get client info - if err := json.Unmarshal([]byte(f), &host); err != nil { + if err := json.Unmarshal(f, &host); err != nil { return hosts, fmt.Errorf("cannot decode client json structure: %v", err) } diff --git a/telegram/bot.go b/telegram/bot.go index 7e33de7..7842f63 100644 --- a/telegram/bot.go +++ b/telegram/bot.go @@ -26,8 +26,8 @@ var ( Bot *echotron.API BotMutex sync.RWMutex - floodWait = make(map[int64]int64, 0) - floodMessageSent = make(map[int64]struct{}, 0) + floodWait = make(map[int64]int64) + floodMessageSent = make(map[int64]struct{}) ) func Start(initDeps TgBotInitDependencies) (err error) { @@ -84,12 +84,15 @@ func Start(initDeps TgBotInitDependencies) (err error) { continue } floodMessageSent[userid] = struct{}{} - bot.SendMessage( + _, err := bot.SendMessage( fmt.Sprintf("You can only request your configs once per %d minutes", FloodWait), userid, &echotron.MessageOptions{ ReplyToMessageID: update.Message.ID, }) + if err != nil { + log.Errorf("Failed to send telegram message. Error %v", err) + } continue } floodWait[userid] = time.Now().Unix() @@ -100,12 +103,15 @@ func Start(initDeps TgBotInitDependencies) (err error) { for _, f := range failed { messageText += f + "\n" } - bot.SendMessage( + _, err := bot.SendMessage( messageText, userid, &echotron.MessageOptions{ ReplyToMessageID: update.Message.ID, }) + if err != nil { + log.Errorf("Failed to send telegram message. Error %v", err) + } } } } diff --git a/templates/users_settings.html b/templates/users_settings.html index 6fb8d69..11a8ef8 100644 --- a/templates/users_settings.html +++ b/templates/users_settings.html @@ -96,7 +96,7 @@ Users Settings $.ajax({ cache: false, method: 'GET', - url: '{{.basePath}}/getusers', + url: '{{.basePath}}/get-users', dataType: 'json', contentType: "application/json", success: function (data) { diff --git a/util/cache.go b/util/cache.go index 8037c30..b9694b9 100644 --- a/util/cache.go +++ b/util/cache.go @@ -3,5 +3,5 @@ package util import "sync" var IPToSubnetRange = map[string]uint16{} -var TgUseridToClientID = map[int64]([]string){} +var TgUseridToClientID = map[int64][]string{} var TgUseridToClientIDMutex sync.RWMutex diff --git a/util/hash.go b/util/hash.go index 0ee0ec9..2dc6b28 100644 --- a/util/hash.go +++ b/util/hash.go @@ -2,6 +2,7 @@ package util import ( "encoding/base64" + "errors" "fmt" "golang.org/x/crypto/bcrypt" ) @@ -20,7 +21,7 @@ func VerifyHash(base64Hash string, plaintext string) (bool, error) { return false, fmt.Errorf("cannot decode base64 hash: %w", err) } err = bcrypt.CompareHashAndPassword(hash, []byte(plaintext)) - if err == bcrypt.ErrMismatchedHashAndPassword { + if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) { return false, nil } if err != nil { diff --git a/util/util.go b/util/util.go index 337745a..88b7089 100644 --- a/util/util.go +++ b/util/util.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "io/fs" - "io/ioutil" "math/rand" "net" "os" @@ -189,7 +188,7 @@ func GetInterfaceIPs() ([]model.Interface, error) { return nil, err } - var interfaceList = []model.Interface{} + var interfaceList []model.Interface // get interface's ip addresses for _, i := range ifaces { @@ -230,9 +229,9 @@ func GetPublicIP() (model.Interface, error) { consensus := externalip.NewConsensus(&cfg, nil) // add trusted voters - consensus.AddVoter(externalip.NewHTTPSource("http://checkip.amazonaws.com/"), 1) + consensus.AddVoter(externalip.NewHTTPSource("https://checkip.amazonaws.com/"), 1) consensus.AddVoter(externalip.NewHTTPSource("http://whatismyip.akamai.com"), 1) - consensus.AddVoter(externalip.NewHTTPSource("http://ifconfig.top"), 1) + consensus.AddVoter(externalip.NewHTTPSource("https://ifconfig.top"), 1) publicInterface := model.Interface{} publicInterface.Name = "Public Address" @@ -244,7 +243,7 @@ func GetPublicIP() (model.Interface, error) { publicInterface.IPAddress = ip.String() } - // error handling happend above, no need to pass it through + // error handling happened above, no need to pass it through return publicInterface, nil } @@ -292,7 +291,7 @@ func GetAllocatedIPs(ignoreClientID string) ([]string, error) { // append client's addresses to the result for _, f := range records { client := model.Client{} - if err := json.Unmarshal([]byte(f), &client); err != nil { + if err := json.Unmarshal(f, &client); err != nil { return nil, err } @@ -336,15 +335,15 @@ func GetBroadcastIP(n *net.IPNet) net.IP { // GetBroadcastAndNetworkAddrsLookup get the ip address that can't be used with current server interfaces func GetBroadcastAndNetworkAddrsLookup(interfaceAddresses []string) map[string]bool { - list := make(map[string]bool, 0) + list := make(map[string]bool) for _, ifa := range interfaceAddresses { - _, net, err := net.ParseCIDR(ifa) + _, netAddr, err := net.ParseCIDR(ifa) if err != nil { continue } - broadcastAddr := GetBroadcastIP(net).String() - networkAddr := net.IP.String() + broadcastAddr := GetBroadcastIP(netAddr).String() + networkAddr := netAddr.IP.String() list[broadcastAddr] = true list[networkAddr] = true } @@ -354,14 +353,14 @@ func GetBroadcastAndNetworkAddrsLookup(interfaceAddresses []string) map[string]b // GetAvailableIP get the ip address that can be allocated from an CIDR // We need interfaceAddresses to find real broadcast and network addresses func GetAvailableIP(cidr string, allocatedList, interfaceAddresses []string) (string, error) { - ip, net, err := net.ParseCIDR(cidr) + ip, netAddr, err := net.ParseCIDR(cidr) if err != nil { return "", err } unavailableIPs := GetBroadcastAndNetworkAddrsLookup(interfaceAddresses) - for ip := ip.Mask(net.Mask); net.Contains(ip); inc(ip) { + for ip := ip.Mask(netAddr.Mask); netAddr.Contains(ip); inc(ip) { available := true suggestedAddr := ip.String() for _, allocatedAddr := range allocatedList { @@ -386,7 +385,7 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip // clientCIDR must be in CIDR format if ip == nil { - return false, fmt.Errorf("Invalid ip allocation input %s. Must be in CIDR format", clientCIDR) + return false, fmt.Errorf("invalid ip allocation input %s. Must be in CIDR format", clientCIDR) } // return false immediately if the ip is already in use (in ipAllocatedList) @@ -398,7 +397,7 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip // even if it is not in use, we still need to check if it // belongs to a network of the server. - var isValid bool = false + var isValid = false for _, serverCIDR := range serverAddresses { _, serverNet, _ := net.ParseCIDR(serverCIDR) if serverNet.Contains(ip) { @@ -437,7 +436,7 @@ func findSubnetRangeForIP(cidr string) (uint16, error) { } } } - return 0, fmt.Errorf("Subnet range not found for this IP") + return 0, fmt.Errorf("subnet range not found for this IP") } // FillClientSubnetRange to fill subnet ranges client belongs to, does nothing if SRs are not found @@ -470,11 +469,11 @@ func ValidateAndFixSubnetRanges(db store.IStore) error { var serverSubnets []*net.IPNet for _, addr := range server.Interface.Addresses { addr = strings.TrimSpace(addr) - _, net, err := net.ParseCIDR(addr) + _, netAddr, err := net.ParseCIDR(addr) if err != nil { return err } - serverSubnets = append(serverSubnets, net) + serverSubnets = append(serverSubnets, netAddr) } for _, rng := range SubnetRangesOrder { @@ -544,7 +543,7 @@ func WriteWireGuardServerConfig(tmplDir fs.FS, serverConfig model.Server, client // if set, read wg.conf template from WgConfTemplate if len(WgConfTemplate) > 0 { - fileContentBytes, err := ioutil.ReadFile(WgConfTemplate) + fileContentBytes, err := os.ReadFile(WgConfTemplate) if err != nil { return err } From 769883f020430d6e80b190ee99a23a55ad8ee26e Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Sun, 31 Dec 2023 21:08:13 +0100 Subject: [PATCH 149/162] add PersistentKeepalive config to server-side Peer config (#515) --- README.md | 12 +++++++----- templates/wg.conf | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e3a5617..9aa4e11 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ A web user interface to manage your WireGuard setup. - Friendly UI - Authentication -- Manage extra client information (name, email, etc) -- Retrieve client config using QR code / file / email +- Manage extra client information (name, email, etc.) +- Retrieve client config using QR code / file / email / Telegram ![wireguard-ui 0.3.7](https://user-images.githubusercontent.com/37958026/177041280-e3e7ca16-d4cf-4e95-9920-68af15e780dd.png) @@ -195,8 +195,7 @@ 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. +this feature work. ## Build @@ -214,7 +213,9 @@ or docker compose build --build-arg=GIT_COMMIT=$(git rev-parse --short HEAD) ``` -:information_source: A container image is avaialble on [Docker Hub](https://hub.docker.com/r/ngoduykhanh/wireguard-ui) which you can pull and use +:information_source: A container image is available on [Docker Hub](https://hub.docker.com/r/ngoduykhanh/wireguard-ui) +which you can pull and use + ``` docker pull ngoduykhanh/wireguard-ui ```` @@ -228,6 +229,7 @@ Prepare the assets directory ``` Then build your executable + ```sh go build -o wireguard-ui ``` diff --git a/templates/wg.conf b/templates/wg.conf index 793876e..a8389cf 100644 --- a/templates/wg.conf +++ b/templates/wg.conf @@ -22,7 +22,8 @@ Table = {{ .globalSettings.Table }} # Update at: {{ .Client.UpdatedAt }} [Peer] PublicKey = {{ .Client.PublicKey }} -{{if .Client.PresharedKey }}PresharedKey = {{ .Client.PresharedKey }}{{end}} +{{if .Client.PresharedKey}}PresharedKey = {{ .Client.PresharedKey }}{{end}} AllowedIPs = {{$first :=true}}{{range .Client.AllocatedIPs }}{{if $first}}{{$first = false}}{{else}},{{end}}{{.}}{{end}}{{range .Client.ExtraAllowedIPs }},{{.}}{{end}} -{{if .Client.Endpoint }}Endpoint = {{ .Client.Endpoint }}{{end}} +{{if $.globalSettings.PersistentKeepalive}}PersistentKeepalive = {{ $.globalSettings.PersistentKeepalive }}{{end}} +{{if .Client.Endpoint}}Endpoint = {{ .Client.Endpoint }}{{end}} {{end}}{{end}} From b3c22aa81f4de1a2e5636efd47749e8cc32eb4b3 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Sun, 31 Dec 2023 21:46:30 +0100 Subject: [PATCH 150/162] Add golangci-lint CI (#516) --- .github/workflows/docker-build.yml | 2 +- .github/workflows/lint.yml | 31 ++++++++++++++++++++++++++++++ .github/workflows/release.yml | 8 ++++---- .gitignore | 3 ++- .golangci.yml | 26 +++++++++++++++++++++++++ assets/.gitkeep | 0 emailer/smtp.go | 3 ++- handler/middlewares.go | 3 ++- handler/routes.go | 23 ++-------------------- handler/routes_wake_on_lan.go | 7 ++++--- main.go | 1 - store/jsondb/jsondb.go | 3 --- store/jsondb/jsondb_wake_on_lan.go | 1 - util/hash.go | 1 + 14 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .golangci.yml create mode 100644 assets/.gitkeep diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 47418d7..696e5b5 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -11,7 +11,7 @@ jobs: build-image: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # set environment - name: Set BUILD_TIME env diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..39fc51f --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Lint + +on: + push: + branches: + - master + pull_request: + branches: + - master + +permissions: + contents: read + pull-requests: read + checks: write + +jobs: + lint: + name: Lint + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v3 + with: + go-version: "1.21" + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.54 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a00dbca..585d196 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: - 7 steps: # get the source code - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # set environment - name: Set APP_VERSION env @@ -35,9 +35,9 @@ jobs: uses: managedkaos/print-env@v1.0 # setup node - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: '14' + node-version: '20' registry-url: 'https://registry.npmjs.org' # prepare assets @@ -53,7 +53,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} goos: ${{ matrix.goos }} goarch: ${{ matrix.goarch }} - goversion: "https://dl.google.com/go/go1.16.1.linux-amd64.tar.gz" + goversion: "https://dl.google.com/go/go1.21.5.linux-amd64.tar.gz" pre_command: export CGO_ENABLED=0 binary_name: "wireguard-ui" build_flags: -v diff --git a/.gitignore b/.gitignore index 610b977..556c687 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,8 @@ wireguard-ui # Dependency directories and files (remove the comment below to include it) vendor/ -assets/ +assets/* +!assets/.gitkeep node_modules/ # IDEs diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..ead9b57 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,26 @@ +run: + timeout: 5m + skip-dirs: + - .github + - hack + - vendor +linters: + disable-all: true + enable: + - gofmt + - revive + - goimports + - govet + - unused + - whitespace + - misspell + fast: false +linters-settings: + gofmt: + simplify: false + revive: + rules: + - name: exported + disabled: true +issues: + exclude-use-default: false diff --git a/assets/.gitkeep b/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/emailer/smtp.go b/emailer/smtp.go index a721d6f..2586924 100644 --- a/emailer/smtp.go +++ b/emailer/smtp.go @@ -3,9 +3,10 @@ package emailer import ( "crypto/tls" "fmt" - mail "github.com/xhit/go-simple-mail/v2" "strings" "time" + + mail "github.com/xhit/go-simple-mail/v2" ) type SmtpMail struct { diff --git a/handler/middlewares.go b/handler/middlewares.go index 231cbf5..b03ef46 100644 --- a/handler/middlewares.go +++ b/handler/middlewares.go @@ -1,8 +1,9 @@ package handler import ( - "github.com/labstack/echo/v4" "net/http" + + "github.com/labstack/echo/v4" ) // ContentTypeJson checks that the requests have the Content-Type header set to "application/json". diff --git a/handler/routes.go b/handler/routes.go index 4dc95c6..513b0a6 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -131,7 +131,6 @@ func Login(db store.IStore) echo.HandlerFunc { // GetUsers handler return a JSON list of all users func GetUsers(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - usersList, err := db.GetUsers() if err != nil { return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ @@ -344,7 +343,6 @@ func RemoveUser(db store.IStore) echo.HandlerFunc { // WireGuardClients handler func WireGuardClients(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - clientDataList, err := db.GetClients(true) if err != nil { return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ @@ -362,7 +360,6 @@ func WireGuardClients(db store.IStore) echo.HandlerFunc { // GetClients handler return a JSON list of Wireguard client data func GetClients(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - clientDataList, err := db.GetClients(true) if err != nil { return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ @@ -381,7 +378,6 @@ func GetClients(db store.IStore) echo.HandlerFunc { // GetClient handler returns a JSON object of Wireguard client data func GetClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - clientID := c.Param("id") if _, err := xid.FromString(clientID); err != nil { @@ -406,7 +402,6 @@ func GetClient(db store.IStore) echo.HandlerFunc { // NewClient handler func NewClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - var client model.Client c.Bind(&client) @@ -475,7 +470,6 @@ func NewClient(db store.IStore) echo.HandlerFunc { return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Duplicate Public Key"}) } } - } if client.PresharedKey == "" { @@ -544,14 +538,14 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon globalSettings, _ := db.GetGlobalSettings() config := util.BuildClientConfig(*clientData.Client, server, globalSettings) - cfgAtt := emailer.Attachment{"wg0.conf", []byte(config)} + cfgAtt := emailer.Attachment{Name: "wg0.conf", Data: []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()}) } - qrAtt := emailer.Attachment{"wg.png", qrdata} + qrAtt := emailer.Attachment{Name: "wg.png", Data: qrdata} attachments = []emailer.Attachment{cfgAtt, qrAtt} } else { attachments = []emailer.Attachment{cfgAtt} @@ -620,7 +614,6 @@ func SendTelegramClient(db store.IStore) echo.HandlerFunc { // UpdateClient handler to update client information func UpdateClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - var _client model.Client c.Bind(&_client) @@ -694,7 +687,6 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { if client.PrivateKey != "" { client.PrivateKey = "" } - } // update Wireguard Client PresharedKey @@ -733,7 +725,6 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { // SetClientStatus handler to enable / disable a client func SetClientStatus(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - data := make(map[string]interface{}) err := json.NewDecoder(c.Request().Body).Decode(&data) @@ -806,7 +797,6 @@ func DownloadClient(db store.IStore) echo.HandlerFunc { // RemoveClient handler func RemoveClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - client := new(model.Client) c.Bind(client) @@ -829,7 +819,6 @@ func RemoveClient(db store.IStore) echo.HandlerFunc { // WireGuardServer handler func WireGuardServer(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - server, err := db.GetServer() if err != nil { log.Error("Cannot get server config: ", err) @@ -846,7 +835,6 @@ func WireGuardServer(db store.IStore) echo.HandlerFunc { // WireGuardServerInterfaces handler func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - var serverInterface model.ServerInterface c.Bind(&serverInterface) @@ -872,7 +860,6 @@ func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc { // WireGuardServerKeyPair handler to generate private and public keys func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - // gen Wireguard key pair key, err := wgtypes.GeneratePrivateKey() if err != nil { @@ -897,7 +884,6 @@ func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { // GlobalSettings handler func GlobalSettings(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - globalSettings, err := db.GetGlobalSettings() if err != nil { log.Error("Cannot get global settings: ", err) @@ -930,7 +916,6 @@ func Status(db store.IStore) echo.HandlerFunc { Peers []PeerVM } return func(c echo.Context) error { - wgClient, err := wgctrl.New() if err != nil { return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{ @@ -1011,7 +996,6 @@ func Status(db store.IStore) echo.HandlerFunc { // GlobalSettingSubmit handler to update the global settings func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - var globalSettings model.GlobalSetting c.Bind(&globalSettings) @@ -1037,7 +1021,6 @@ func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc { // MachineIPAddresses handler to get local interface ip addresses func MachineIPAddresses() echo.HandlerFunc { return func(c echo.Context) error { - // get private ip addresses interfaceList, err := util.GetInterfaceIPs() if err != nil { @@ -1068,7 +1051,6 @@ func GetOrderedSubnetRanges() echo.HandlerFunc { // SuggestIPAllocation handler to get the list of ip address for client func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - server, err := db.GetServer() if err != nil { log.Error("Cannot fetch server config from database: ", err) @@ -1135,7 +1117,6 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { // ApplyServerConfig handler to write config file and restart Wireguard server func ApplyServerConfig(db store.IStore, tmplDir fs.FS) echo.HandlerFunc { return func(c echo.Context) error { - server, err := db.GetServer() if err != nil { log.Error("Cannot get server config: ", err) diff --git a/handler/routes_wake_on_lan.go b/handler/routes_wake_on_lan.go index 43a6186..1747a1e 100644 --- a/handler/routes_wake_on_lan.go +++ b/handler/routes_wake_on_lan.go @@ -2,14 +2,15 @@ package handler import ( "fmt" + "net" + "net/http" + "time" + "github.com/labstack/echo/v4" "github.com/labstack/gommon/log" "github.com/ngoduykhanh/wireguard-ui/model" "github.com/ngoduykhanh/wireguard-ui/store" "github.com/sabhiram/go-wol/wol" - "net" - "net/http" - "time" ) type WakeOnLanHostSavePayload struct { diff --git a/main.go b/main.go index e11cf29..3d67e70 100644 --- a/main.go +++ b/main.go @@ -73,7 +73,6 @@ var embeddedTemplates embed.FS 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.StringVar(&flagBindAddress, "bind-address", util.LookupEnvOrString("BIND_ADDRESS", flagBindAddress), "Address:Port to which the app will be bound.") diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 8b5f84e..5d01066 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -33,7 +33,6 @@ func New(dbPath string) (*JsonDB, error) { dbPath: dbPath, } return &ans, nil - } func (o *JsonDB) Init() error { @@ -77,7 +76,6 @@ func (o *JsonDB) Init() error { // server's key pair if _, err := os.Stat(serverKeyPairPath); os.IsNotExist(err) { - key, err := wgtypes.GeneratePrivateKey() if err != nil { return scribble.ErrMissingCollection @@ -193,7 +191,6 @@ func (o *JsonDB) GetUsers() ([]model.User, error) { return users, fmt.Errorf("cannot decode user json structure: %v", err) } users = append(users, user) - } return users, err } diff --git a/store/jsondb/jsondb_wake_on_lan.go b/store/jsondb/jsondb_wake_on_lan.go index bf43fcf..d210d61 100644 --- a/store/jsondb/jsondb_wake_on_lan.go +++ b/store/jsondb/jsondb_wake_on_lan.go @@ -76,7 +76,6 @@ func (o *JsonDB) SaveWakeOnLanHost(host model.WakeOnLanHost) error { } return output - } func (o *JsonDB) DeleteWakeOnHost(host model.WakeOnLanHost) error { diff --git a/util/hash.go b/util/hash.go index 2dc6b28..3733451 100644 --- a/util/hash.go +++ b/util/hash.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "errors" "fmt" + "golang.org/x/crypto/bcrypt" ) From 46b09348e37fbb23778b7e366f8c36251b2eb2f6 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Thu, 4 Jan 2024 10:46:38 +0100 Subject: [PATCH 151/162] add iptables package (#520) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 33da3fa..c532e42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,7 +61,7 @@ FROM alpine:3.19 RUN addgroup -S wgui && \ adduser -S -D -G wgui wgui -RUN apk --no-cache add ca-certificates wireguard-tools jq +RUN apk --no-cache add ca-certificates wireguard-tools jq iptables WORKDIR /app From fa33d3f66e307cdad41ed560b30a1cd618670920 Mon Sep 17 00:00:00 2001 From: 0xCA <0xCA@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:11:20 +0500 Subject: [PATCH 152/162] Session improvements (#510) --- README.md | 1 + handler/routes.go | 23 ++++-- handler/session.go | 168 ++++++++++++++++++++++++++++++++++++++++- main.go | 20 +++-- router/router.go | 12 ++- store/jsondb/jsondb.go | 10 +++ util/cache.go | 1 + util/config.go | 37 ++++----- util/util.go | 38 ++++++++++ 9 files changed, 274 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9aa4e11..74c446e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ docker-compose up | `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 | +| `SESSION_MAX_DURATION` | Max time in days a remembered session is refreshed and valid. Non-refreshed session is valid for 7 days max, regardless of this setting. | 90 | | `SUBNET_RANGES` | The list of address subdivision ranges. Format: `SR Name:10.0.1.0/24; SR2:10.0.2.0/24,10.0.3.0/24` Each CIDR must be inside one of the server interfaces. | 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` | diff --git a/handler/routes.go b/handler/routes.go index 513b0a6..ef01d08 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -93,32 +93,41 @@ func Login(db store.IStore) echo.HandlerFunc { } if userCorrect && passwordCorrect { - // TODO: refresh the token ageMax := 0 - expiration := time.Now().Add(24 * time.Hour) if rememberMe { - ageMax = 86400 - expiration.Add(144 * time.Hour) + ageMax = 86400 * 7 } + + cookiePath := util.GetCookiePath() + sess, _ := session.Get("session", c) sess.Options = &sessions.Options{ - Path: util.BasePath, + Path: cookiePath, MaxAge: ageMax, HttpOnly: true, + SameSite: http.SameSiteLaxMode, } // set session_token tokenUID := xid.New().String() + now := time.Now().UTC().Unix() sess.Values["username"] = dbuser.Username + sess.Values["user_hash"] = util.GetDBUserCRC32(dbuser) sess.Values["admin"] = dbuser.Admin sess.Values["session_token"] = tokenUID + sess.Values["max_age"] = ageMax + sess.Values["created_at"] = now + sess.Values["updated_at"] = now sess.Save(c.Request(), c.Response()) // set session_token in cookie cookie := new(http.Cookie) cookie.Name = "session_token" + cookie.Path = cookiePath cookie.Value = tokenUID - cookie.Expires = expiration + cookie.MaxAge = ageMax + cookie.HttpOnly = true + cookie.SameSite = http.SameSiteLaxMode c.SetCookie(cookie) return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Logged in successfully"}) @@ -256,7 +265,7 @@ func UpdateUser(db store.IStore) echo.HandlerFunc { log.Infof("Updated user information successfully") if previousUsername == currentUser(c) { - setUser(c, user.Username, user.Admin) + setUser(c, user.Username, user.Admin, util.GetDBUserCRC32(user)) } return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated user information successfully"}) diff --git a/handler/session.go b/handler/session.go index 4cede6e..b660d9c 100644 --- a/handler/session.go +++ b/handler/session.go @@ -3,7 +3,9 @@ package handler import ( "fmt" "net/http" + "time" + "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/ngoduykhanh/wireguard-ui/util" @@ -23,6 +25,15 @@ func ValidSession(next echo.HandlerFunc) echo.HandlerFunc { } } +// RefreshSession must only be used after ValidSession middleware +// RefreshSession checks if the session is eligible for the refresh, but doesn't check if it's fully valid +func RefreshSession(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + doRefreshSession(c) + return next(c) + } +} + func NeedsAdmin(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if !isAdmin(c) { @@ -41,9 +52,146 @@ func isValidSession(c echo.Context) bool { if err != nil || sess.Values["session_token"] != cookie.Value { return false } + + // Check time bounds + createdAt := getCreatedAt(sess) + updatedAt := getUpdatedAt(sess) + maxAge := getMaxAge(sess) + // Temporary session is considered valid within 24h if browser is not closed before + // This value is not saved and is used as virtual expiration + if maxAge == 0 { + maxAge = 86400 + } + expiration := updatedAt + int64(maxAge) + now := time.Now().UTC().Unix() + if updatedAt > now || expiration < now || createdAt+util.SessionMaxDuration < now { + return false + } + + // Check if user still exists and unchanged + username := fmt.Sprintf("%s", sess.Values["username"]) + userHash := getUserHash(sess) + if uHash, ok := util.DBUsersToCRC32[username]; !ok || userHash != uHash { + return false + } + return true } +// Refreshes a "remember me" session when the user visits web pages (not API) +// Session must be valid before calling this function +// Refresh is performed at most once per 24h +func doRefreshSession(c echo.Context) { + if util.DisableLogin { + return + } + + sess, _ := session.Get("session", c) + maxAge := getMaxAge(sess) + if maxAge <= 0 { + return + } + + oldCookie, err := c.Cookie("session_token") + if err != nil || sess.Values["session_token"] != oldCookie.Value { + return + } + + // Refresh no sooner than 24h + createdAt := getCreatedAt(sess) + updatedAt := getUpdatedAt(sess) + expiration := updatedAt + int64(getMaxAge(sess)) + now := time.Now().UTC().Unix() + if updatedAt > now || expiration < now || now-updatedAt < 86_400 || createdAt+util.SessionMaxDuration < now { + return + } + + cookiePath := util.GetCookiePath() + + sess.Values["updated_at"] = now + sess.Options = &sessions.Options{ + Path: cookiePath, + MaxAge: maxAge, + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + } + sess.Save(c.Request(), c.Response()) + + cookie := new(http.Cookie) + cookie.Name = "session_token" + cookie.Path = cookiePath + cookie.Value = oldCookie.Value + cookie.MaxAge = maxAge + cookie.HttpOnly = true + cookie.SameSite = http.SameSiteLaxMode + c.SetCookie(cookie) +} + +// Get time in seconds this session is valid without updating +func getMaxAge(sess *sessions.Session) int { + if util.DisableLogin { + return 0 + } + + maxAge := sess.Values["max_age"] + + switch typedMaxAge := maxAge.(type) { + case int: + return typedMaxAge + default: + return 0 + } +} + +// Get a timestamp in seconds of the time the session was created +func getCreatedAt(sess *sessions.Session) int64 { + if util.DisableLogin { + return 0 + } + + createdAt := sess.Values["created_at"] + + switch typedCreatedAt := createdAt.(type) { + case int64: + return typedCreatedAt + default: + return 0 + } +} + +// Get a timestamp in seconds of the last session update +func getUpdatedAt(sess *sessions.Session) int64 { + if util.DisableLogin { + return 0 + } + + lastUpdate := sess.Values["updated_at"] + + switch typedLastUpdate := lastUpdate.(type) { + case int64: + return typedLastUpdate + default: + return 0 + } +} + +// Get CRC32 of a user at the moment of log in +// Any changes to user will result in logout of other (not updated) sessions +func getUserHash(sess *sessions.Session) uint32 { + if util.DisableLogin { + return 0 + } + + userHash := sess.Values["user_hash"] + + switch typedUserHash := userHash.(type) { + case uint32: + return typedUserHash + default: + return 0 + } +} + // currentUser to get username of logged in user func currentUser(c echo.Context) string { if util.DisableLogin { @@ -66,9 +214,10 @@ func isAdmin(c echo.Context) bool { return admin == "true" } -func setUser(c echo.Context, username string, admin bool) { +func setUser(c echo.Context, username string, admin bool, userCRC32 uint32) { sess, _ := session.Get("session", c) sess.Values["username"] = username + sess.Values["user_hash"] = userCRC32 sess.Values["admin"] = admin sess.Save(c.Request(), c.Response()) } @@ -77,7 +226,24 @@ func setUser(c echo.Context, username string, admin bool) { func clearSession(c echo.Context) { sess, _ := session.Get("session", c) sess.Values["username"] = "" + sess.Values["user_hash"] = 0 sess.Values["admin"] = false sess.Values["session_token"] = "" + sess.Values["max_age"] = -1 + sess.Options.MaxAge = -1 sess.Save(c.Request(), c.Response()) + + cookiePath := util.GetCookiePath() + + cookie, err := c.Cookie("session_token") + if err != nil { + cookie = new(http.Cookie) + } + + cookie.Name = "session_token" + cookie.Path = cookiePath + cookie.MaxAge = -1 + cookie.HttpOnly = true + cookie.SameSite = http.SameSiteLaxMode + c.SetCookie(cookie) } diff --git a/main.go b/main.go index 3d67e70..1125746 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "crypto/sha512" "embed" "flag" "fmt" @@ -48,6 +49,7 @@ var ( flagTelegramAllowConfRequest = false flagTelegramFloodWait = 60 flagSessionSecret = util.RandomString(32) + flagSessionMaxDuration = 90 flagWgConfTemplate string flagBasePath string flagSubnetRanges string @@ -91,6 +93,7 @@ func init() { 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(&flagSubnetRanges, "subnet-ranges", util.LookupEnvOrString("SUBNET_RANGES", flagSubnetRanges), "IP ranges to choose from when assigning an IP for a client.") + flag.IntVar(&flagSessionMaxDuration, "session-max-duration", util.LookupEnvOrInt("SESSION_MAX_DURATION", flagSessionMaxDuration), "Max time in days a remembered session is refreshed and valid.") var ( smtpPasswordLookup = util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword) @@ -135,7 +138,8 @@ func init() { util.SendgridApiKey = flagSendgridApiKey util.EmailFrom = flagEmailFrom util.EmailFromName = flagEmailFromName - util.SessionSecret = []byte(flagSessionSecret) + util.SessionSecret = sha512.Sum512([]byte(flagSessionSecret)) + util.SessionMaxDuration = int64(flagSessionMaxDuration) * 86_400 // Store in seconds util.WgConfTemplate = flagWgConfTemplate util.BasePath = util.ParseBasePath(flagBasePath) util.SubnetRanges = util.ParseSubnetRanges(flagSubnetRanges) @@ -204,7 +208,7 @@ func main() { // register routes app := router.New(tmplDir, extraData, util.SessionSecret) - app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession) + app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession, handler.RefreshSession) // 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 @@ -214,8 +218,8 @@ func main() { app.GET(util.BasePath+"/login", handler.LoginPage()) app.POST(util.BasePath+"/login", handler.Login(db), handler.ContentTypeJson) app.GET(util.BasePath+"/logout", handler.Logout(), handler.ValidSession) - app.GET(util.BasePath+"/profile", handler.LoadProfile(), handler.ValidSession) - app.GET(util.BasePath+"/users-settings", handler.UsersSettings(), handler.ValidSession, handler.NeedsAdmin) + app.GET(util.BasePath+"/profile", handler.LoadProfile(), handler.ValidSession, handler.RefreshSession) + app.GET(util.BasePath+"/users-settings", handler.UsersSettings(), handler.ValidSession, handler.RefreshSession, handler.NeedsAdmin) 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.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/remove-user", handler.RemoveUser(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) @@ -241,19 +245,19 @@ func main() { app.POST(util.BasePath+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson) app.POST(util.BasePath+"/remove-client", handler.RemoveClient(db), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession) - app.GET(util.BasePath+"/wg-server", handler.WireGuardServer(db), handler.ValidSession, handler.NeedsAdmin) + app.GET(util.BasePath+"/wg-server", handler.WireGuardServer(db), handler.ValidSession, handler.RefreshSession, handler.NeedsAdmin) app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) - app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin) + app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.RefreshSession, handler.NeedsAdmin) app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin) - app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession) + app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession, handler.RefreshSession) app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) app.GET(util.BasePath+"/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession) app.GET(util.BasePath+"/api/subnet-ranges", handler.GetOrderedSubnetRanges(), handler.ValidSession) app.GET(util.BasePath+"/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession) app.POST(util.BasePath+"/api/apply-wg-config", handler.ApplyServerConfig(db, tmplDir), handler.ValidSession, handler.ContentTypeJson) - app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession) + app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession, handler.RefreshSession) app.POST(util.BasePath+"/wake_on_lan_host", handler.SaveWakeOnLanHost(db), handler.ValidSession, handler.ContentTypeJson) app.DELETE(util.BasePath+"/wake_on_lan_host/:mac_address", handler.DeleteWakeOnHost(db), handler.ValidSession, handler.ContentTypeJson) app.PUT(util.BasePath+"/wake_on_lan_host/:mac_address", handler.WakeOnHost(db), handler.ValidSession, handler.ContentTypeJson) diff --git a/router/router.go b/router/router.go index 569ebaf..59d352e 100644 --- a/router/router.go +++ b/router/router.go @@ -48,9 +48,17 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c } // New function -func New(tmplDir fs.FS, extraData map[string]interface{}, secret []byte) *echo.Echo { +func New(tmplDir fs.FS, extraData map[string]interface{}, secret [64]byte) *echo.Echo { e := echo.New() - e.Use(session.Middleware(sessions.NewCookieStore(secret))) + + cookiePath := util.GetCookiePath() + + cookieStore := sessions.NewCookieStore(secret[:32], secret[32:]) + cookieStore.Options.Path = cookiePath + cookieStore.Options.HttpOnly = true + cookieStore.MaxAge(86400 * 7) + + e.Use(session.Middleware(cookieStore)) // read html template file to string tmplBaseString, err := util.StringFromEmbedFile(tmplDir, "base.html") diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index 5d01066..1401b2c 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -161,6 +161,14 @@ func (o *JsonDB) Init() error { } // init cache + for _, i := range results { + user := model.User{} + + if err := json.Unmarshal([]byte(i), &user); err == nil { + util.DBUsersToCRC32[user.Username] = util.GetDBUserCRC32(user) + } + } + clients, err := o.GetClients(false) if err != nil { return nil @@ -214,11 +222,13 @@ func (o *JsonDB) SaveUser(user model.User) error { if err != nil { return err } + util.DBUsersToCRC32[user.Username] = util.GetDBUserCRC32(user) return output } // DeleteUser func to remove user from the database func (o *JsonDB) DeleteUser(username string) error { + delete(util.DBUsersToCRC32, username) return o.conn.Delete("users", username) } diff --git a/util/cache.go b/util/cache.go index b9694b9..48b37ea 100644 --- a/util/cache.go +++ b/util/cache.go @@ -5,3 +5,4 @@ import "sync" var IPToSubnetRange = map[string]uint16{} var TgUseridToClientID = map[int64][]string{} var TgUseridToClientIDMutex sync.RWMutex +var DBUsersToCRC32 = map[string]uint32{} diff --git a/util/config.go b/util/config.go index 796775c..4af6bd2 100644 --- a/util/config.go +++ b/util/config.go @@ -9,24 +9,25 @@ import ( // Runtime config var ( - DisableLogin bool - BindAddress string - SmtpHostname string - SmtpPort int - SmtpUsername string - SmtpPassword string - SmtpNoTLSCheck bool - SmtpEncryption string - SmtpAuthType string - SmtpHelo string - SendgridApiKey string - EmailFrom string - EmailFromName string - SessionSecret []byte - WgConfTemplate string - BasePath string - SubnetRanges map[string]([]*net.IPNet) - SubnetRangesOrder []string + DisableLogin bool + BindAddress string + SmtpHostname string + SmtpPort int + SmtpUsername string + SmtpPassword string + SmtpNoTLSCheck bool + SmtpEncryption string + SmtpAuthType string + SmtpHelo string + SendgridApiKey string + EmailFrom string + EmailFromName string + SessionSecret [64]byte + SessionMaxDuration int64 + WgConfTemplate string + BasePath string + SubnetRanges map[string]([]*net.IPNet) + SubnetRangesOrder []string ) const ( diff --git a/util/util.go b/util/util.go index 88b7089..06b87c3 100644 --- a/util/util.go +++ b/util/util.go @@ -2,9 +2,12 @@ package util import ( "bufio" + "bytes" + "encoding/gob" "encoding/json" "errors" "fmt" + "hash/crc32" "io" "io/fs" "math/rand" @@ -827,3 +830,38 @@ func filterStringSlice(s []string, excludedStr string) []string { } return filtered } + +func GetDBUserCRC32(dbuser model.User) uint32 { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + if err := enc.Encode(dbuser); err != nil { + panic("model.User is gob-incompatible, session verification is impossible") + } + return crc32.ChecksumIEEE(buf.Bytes()) +} + +func ConcatMultipleSlices(slices ...[]byte) []byte { + var totalLen int + + for _, s := range slices { + totalLen += len(s) + } + + result := make([]byte, totalLen) + + var i int + + for _, s := range slices { + i += copy(result[i:], s) + } + + return result +} + +func GetCookiePath() string { + cookiePath := BasePath + if cookiePath == "" { + cookiePath = "/" + } + return cookiePath +} From a22e807d2ac695de15f8d7b9e302c825477fa9f9 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Sat, 6 Jan 2024 10:09:46 +0100 Subject: [PATCH 153/162] chore: update docker build workflow Keep the 'v' in app version so we are able to query to GitHub release api to fetch its release information --- .github/workflows/docker-build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 696e5b5..8960a48 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -33,14 +33,13 @@ jobs: ## if [[ '${{ github.ref }}' == *"refs/tags/"* ]]; then github_tag="${GITHUB_REF#refs/*/}" + app_version=${github_tag} SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" if [[ "$github_tag" =~ $SEMVER_REGEX ]]; then github_tag=$(echo "${github_tag}" | sed 's/^v//') fi - app_version=${github_tag} - container_images=$(cat < Date: Sun, 7 Jan 2024 09:44:05 +0100 Subject: [PATCH 154/162] chore: update release workflow --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 585d196..4205bde 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ jobs: name: Release Go Binary runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: # build and publish in parallel: linux/386, linux/amd64, darwin/386, darwin/amd64 goos: [linux, freebsd, darwin] From 99104e429557a4cd6bda4b0c2550a656cdfe9bb7 Mon Sep 17 00:00:00 2001 From: Khanh Ngo Date: Sun, 7 Jan 2024 10:45:05 +0100 Subject: [PATCH 155/162] fix: revert wgctrl module version (#525) --- go.mod | 2 +- go.sum | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ea102f7..6255955 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/crypto v0.17.0 golang.org/x/mod v0.14.0 //golang.zx2c4.com/wireguard v0.0.20200121 // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c gopkg.in/go-playground/validator.v9 v9.31.0 ) diff --git a/go.sum b/go.sum index b160f6a..16f05fe 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,12 @@ github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -27,8 +33,16 @@ github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8L github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8= github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= github.com/jessevdk/go-flags v0.0.0-20150816100521-1acbbaff2f34/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= +github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= +github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= +github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= +github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= +github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= @@ -45,8 +59,19 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= +github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= +github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= +github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= @@ -85,15 +110,51 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA= github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -101,12 +162,23 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 73108f7f21904a7afe0cc1829f1b80d84fb04555 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 10 Jan 2024 03:52:23 +0800 Subject: [PATCH 156/162] Fix updateSearchList js error (#524) --- templates/base.html | 3 --- templates/clients.html | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/base.html b/templates/base.html index ab086e1..0c2b9ee 100644 --- a/templates/base.html +++ b/templates/base.html @@ -396,8 +396,6 @@ toastr.options.positionClass = 'toast-top-right-fix'; updateApplyConfigVisibility() - // from clients.html - updateSearchList() }); @@ -435,7 +433,6 @@ } }); } - // populateClient function for render new client info // on the client page. diff --git a/templates/clients.html b/templates/clients.html index cf11e0a..8c24d4e 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -391,11 +391,12 @@ Wireguard Clients ); }); }); -} + }