Single binary build

Use go rice for embedding the static files and templates to the binary file
This commit is contained in:
Khanh Ngo 2020-05-21 11:43:03 +07:00
parent c56f042217
commit af16649b3f
No known key found for this signature in database
GPG key ID: D5FAA6A16150E49E
7 changed files with 115 additions and 12 deletions

5
.gitignore vendored
View file

@ -12,10 +12,11 @@ wireguard-ui
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Dependency directories (remove the comment below to include it) # Dependency directories and files (remove the comment below to include it)
vendor/ vendor/
assets/ assets/
node_modules node_modules/
rice-box.go
# IDEs # IDEs
.vscode .vscode

View file

@ -8,15 +8,24 @@ A web user interface to manage your WireGuard setup.
- Retrieve configs using QR code / file - Retrieve configs using QR code / file
## Run WireGuard-UI ## Run WireGuard-UI
Only docker option for now, please refer to 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: Default username and password are `admin`.
### 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:
``` ```
docker-compose up docker-compose up
``` ```
Default username and password are `admin`. ### Using binary file
Download the binary file from the release and run it with command:
```
./wireguard-ui
```
## Auto restart WireGuard daemon ## Auto restart WireGuard daemon
WireGuard-UI only takes care of configuration generation. You can use systemd to watch for the changes and restart the service. Following is an example: WireGuard-UI only takes care of configuration generation. You can use systemd to watch for the changes and restart the service. Following is an example:
@ -52,6 +61,32 @@ systemctl enable wgui.{path,service}
systemctl start wgui.{path,service} systemctl start wgui.{path,service}
``` ```
## Build
### Build docker image
Go to the project root directory and run the following command:
```
docker build -t wireguard-ui .
```
### Build binary file
You can embed resources by generating Go source code
```
rice embed-go
go build -o wireguard-ui
```
Or, append resources to executable as zip file
```
go build -o wireguard-ui
rice append --exec wireguard-ui
```
## Screenshot ## Screenshot
![wireguard-ui](https://user-images.githubusercontent.com/6447444/80270680-76adf980-86e4-11ea-8ca1-9237f0dfa249.png) ![wireguard-ui](https://user-images.githubusercontent.com/6447444/80270680-76adf980-86e4-11ea-8ca1-9237f0dfa249.png)

1
go.mod
View file

@ -3,6 +3,7 @@ module github.com/ngoduykhanh/wireguard-ui
go 1.14 go 1.14
require ( require (
github.com/GeertJohan/go.rice v1.0.0
github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd github.com/glendc/go-external-ip v0.0.0-20170425150139-139229dcdddd
github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/gorilla/sessions v1.2.0 github.com/gorilla/sessions v1.2.0

8
go.sum
View file

@ -1,4 +1,8 @@
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/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/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/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/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw=
@ -7,6 +11,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/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/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@ -39,6 +45,7 @@ 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/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 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8=
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw= 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/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-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-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -75,6 +82,7 @@ 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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
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/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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

10
main.go
View file

@ -2,7 +2,10 @@ package main
import ( import (
"fmt" "fmt"
"github.com/labstack/echo/v4"
"net/http"
"github.com/GeertJohan/go.rice"
"github.com/ngoduykhanh/wireguard-ui/handler" "github.com/ngoduykhanh/wireguard-ui/handler"
"github.com/ngoduykhanh/wireguard-ui/router" "github.com/ngoduykhanh/wireguard-ui/router"
"github.com/ngoduykhanh/wireguard-ui/util" "github.com/ngoduykhanh/wireguard-ui/util"
@ -15,6 +18,9 @@ func main() {
fmt.Print("Cannot init database: ", err) fmt.Print("Cannot init database: ", err)
} }
// the file server for rice. "assets" is the folder where the files come from.
assetHandler := http.FileServer(rice.MustFindBox("assets").HTTPBox())
// register routes // register routes
app := router.New() app := router.New()
@ -34,5 +40,9 @@ func main() {
app.GET("/api/machine-ips", handler.MachineIPAddresses()) app.GET("/api/machine-ips", handler.MachineIPAddresses())
app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation()) app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation())
app.GET("/api/apply-wg-config", handler.ApplyServerConfig()) app.GET("/api/apply-wg-config", handler.ApplyServerConfig())
// servers other static files
app.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler)))
app.Logger.Fatal(app.Start("0.0.0.0:5000")) app.Logger.Fatal(app.Start("0.0.0.0:5000"))
} }

View file

@ -5,6 +5,7 @@ import (
"io" "io"
"text/template" "text/template"
"github.com/GeertJohan/go.rice"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/labstack/echo-contrib/session" "github.com/labstack/echo-contrib/session"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -36,11 +37,44 @@ func New() *echo.Echo {
e := echo.New() e := echo.New()
e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret")))) e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))
// create go rice box for embedded template
templateBox, err := rice.FindBox("../templates")
if err != nil {
log.Fatal(err)
}
// read html template file to string
tmplBaseString, err := templateBox.String("base.html")
if err != nil {
log.Fatal(err)
}
tmplLoginString, err := templateBox.String("login.html")
if err != nil {
log.Fatal(err)
}
tmplClientsString, err := templateBox.String("clients.html")
if err != nil {
log.Fatal(err)
}
tmplServerString, err := templateBox.String("server.html")
if err != nil {
log.Fatal(err)
}
tmplGlobalSettingsString, err := templateBox.String("global_settings.html")
if err != nil {
log.Fatal(err)
}
// create template list
templates := make(map[string]*template.Template) templates := make(map[string]*template.Template)
templates["login.html"] = template.Must(template.ParseFiles("templates/login.html")) templates["login.html"] = template.Must(template.New("login").Parse(tmplLoginString))
templates["clients.html"] = template.Must(template.ParseFiles("templates/clients.html", "templates/base.html")) templates["clients.html"] = template.Must(template.New("clients").Parse(tmplBaseString + tmplClientsString))
templates["server.html"] = template.Must(template.ParseFiles("templates/server.html", "templates/base.html")) templates["server.html"] = template.Must(template.New("server").Parse(tmplBaseString + tmplServerString))
templates["global_settings.html"] = template.Must(template.ParseFiles("templates/global_settings.html", "templates/base.html")) templates["global_settings.html"] = template.Must(template.New("global_settings").Parse(tmplBaseString + tmplGlobalSettingsString))
e.Logger.SetLevel(log.DEBUG) e.Logger.SetLevel(log.DEBUG)
e.Pre(middleware.RemoveTrailingSlash()) e.Pre(middleware.RemoveTrailingSlash())
@ -51,7 +85,6 @@ func New() *echo.Echo {
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE}, AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
})) }))
e.Validator = NewValidator() e.Validator = NewValidator()
e.Static("/static", "assets")
e.Renderer = &TemplateRegistry{ e.Renderer = &TemplateRegistry{
templates: templates, templates: templates,
} }

View file

@ -10,6 +10,7 @@ import (
"text/template" "text/template"
"time" "time"
rice "github.com/GeertJohan/go.rice"
externalip "github.com/glendc/go-external-ip" externalip "github.com/glendc/go-external-ip"
"github.com/ngoduykhanh/wireguard-ui/model" "github.com/ngoduykhanh/wireguard-ui/model"
"github.com/sdomino/scribble" "github.com/sdomino/scribble"
@ -268,7 +269,7 @@ func GetAvailableIP(cidr string, allocatedList []string) (string, error) {
} }
} }
return "", errors.New("No more available ip address") return "", errors.New("no more available ip address")
} }
// ValidateIPAllocation to validate the list of client's ip allocation // ValidateIPAllocation to validate the list of client's ip allocation
@ -313,11 +314,25 @@ func ValidateIPAllocation(serverAddresses []string, ipAllocatedList []string, ip
// WriteWireGuardServerConfig to write Wireguard server config. e.g. wg0.conf // WriteWireGuardServerConfig to write Wireguard server config. e.g. wg0.conf
func WriteWireGuardServerConfig(serverConfig model.Server, clientDataList []model.ClientData, globalSettings model.GlobalSetting) error { func WriteWireGuardServerConfig(serverConfig model.Server, clientDataList []model.ClientData, globalSettings model.GlobalSetting) error {
t, err := template.ParseFiles("templates/wg.conf") // create go rice box for wireguard config
templateBox, err := rice.FindBox("../templates")
if err != nil { if err != nil {
return err return err
} }
// read wg.conf template file to string
tmplWireguardConf, err := templateBox.String("wg.conf")
if err != nil {
return err
}
// parse the template
t, err := template.New("wg_config").Parse(tmplWireguardConf)
if err != nil {
return err
}
// write config file to disk
f, err := os.Create(globalSettings.ConfigFilePath) f, err := os.Create(globalSettings.ConfigFilePath)
if err != nil { if err != nil {
return err return err