*: allows for BASE_PATH configuration (#183)

This commit is contained in:
Quentin Machu 2022-04-25 00:17:13 -07:00 committed by GitHub
parent 90bb2851bf
commit 87b08a8f7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 108 additions and 84 deletions

View file

@ -13,8 +13,11 @@ RUN apk add --update --no-cache ${BUILD_DEPENDENCIES}
WORKDIR /build
# Add sources
COPY . /build
# Add dependencies
COPY go.mod /build
COPY go.sum /build
COPY package.json /build
COPY yarn.lock /build
# Prepare assets
RUN yarn install --pure-lockfile --production && \
@ -39,13 +42,16 @@ RUN mkdir -p assets/plugins && \
/build/node_modules/jquery-tags-input/ \
assets/plugins/
# Move custom assets
RUN cp -r /build/custom/ assets/
# Get go modules and build tool
RUN go mod download && \
go get github.com/GeertJohan/go.rice/rice
# Add sources
COPY . /build
# Move custom assets
RUN cp -r /build/custom/ assets/
# Build
RUN rice embed-go && \
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -o wg-ui .

View file

@ -35,6 +35,8 @@ Set the `SESSION_SECRET` environment variable to a random value.
To use custom `wg.conf` template set the `WG_CONF_TEMPLATE` environment variable to a path to such file. Make sure `wireguard-ui` will be able to work with it - use [default template](templates/wg.conf) for reference.
Set the `BASE_PATH` environment variable if you run wireguard-ui under a subpath of your reverse proxy virtual host (e.g. /wireguard).
In order to sent the wireguard configuration to clients via email, set the following environment variables:
- using SendGrid API

View file

@ -26,7 +26,7 @@ function renderClientList(data) {
</div>
<div class="info-box-content">
<div class="btn-group">
<a href="/download?clientid=${obj.Client.id}" class="btn btn-outline-primary btn-sm">Download</a>
<a href="download?clientid=${obj.Client.id}" class="btn btn-outline-primary btn-sm">Download</a>
</div>
<div class="btn-group">
<button type="button" class="btn btn-outline-primary btn-sm" data-toggle="modal"

View file

@ -53,7 +53,7 @@ func Login(db store.IStore) echo.HandlerFunc {
// TODO: refresh the token
sess, _ := session.Get("session", c)
sess.Options = &sessions.Options{
Path: "/",
Path: util.BasePath,
MaxAge: 86400,
HttpOnly: true,
}
@ -82,7 +82,7 @@ func Login(db store.IStore) echo.HandlerFunc {
func Logout() echo.HandlerFunc {
return func(c echo.Context) error {
clearSession(c)
return c.Redirect(http.StatusTemporaryRedirect, "/login")
return c.Redirect(http.StatusTemporaryRedirect, util.BasePath + "/login")
}
}

View file

@ -14,9 +14,9 @@ func ValidSession(next echo.HandlerFunc) echo.HandlerFunc {
if !isValidSession(c) {
nextURL := c.Request().URL
if nextURL != nil && c.Request().Method == http.MethodGet {
return c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("/login?next=%s", c.Request().URL))
return c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf(util.BasePath + "/login?next=%s", c.Request().URL))
} else {
return c.Redirect(http.StatusTemporaryRedirect, "/login")
return c.Redirect(http.StatusTemporaryRedirect, util.BasePath + "/login")
}
}
return next(c)

60
main.go
View file

@ -35,6 +35,7 @@ var (
flagEmailFromName string = "WireGuard UI"
flagSessionSecret string
flagWgConfTemplate string
flagBasePath string
)
const (
@ -62,6 +63,7 @@ func init() {
flag.StringVar(&flagEmailFromName, "email-from-name", util.LookupEnvOrString("EMAIL_FROM_NAME", flagEmailFromName), "'From' email name.")
flag.StringVar(&flagSessionSecret, "session-secret", util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret), "The key used to encrypt session cookies.")
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.Parse()
// update runtime config
@ -78,6 +80,7 @@ func init() {
util.EmailFromName = flagEmailFromName
util.SessionSecret = []byte(flagSessionSecret)
util.WgConfTemplate = flagWgConfTemplate
util.BasePath = util.ParseBasePath(flagBasePath)
// print app information
fmt.Println("Wireguard UI")
@ -93,7 +96,7 @@ func init() {
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() {
@ -107,6 +110,7 @@ func main() {
// set app extra data
extraData := make(map[string]string)
extraData["appVersion"] = appVersion
extraData["basePath"] = util.BasePath
// create rice box for embedded template
tmplBox := rice.MustFindBox("templates")
@ -117,11 +121,11 @@ func main() {
// register routes
app := router.New(tmplBox, extraData, util.SessionSecret)
app.GET("/", handler.WireGuardClients(db), handler.ValidSession)
app.GET(util.BasePath, handler.WireGuardClients(db), handler.ValidSession)
if !util.DisableLogin {
app.GET("/login", handler.LoginPage())
app.POST("/login", handler.Login(db))
app.GET(util.BasePath + "/login", handler.LoginPage())
app.POST(util.BasePath + "/login", handler.Login(db))
}
var sendmail emailer.Emailer
@ -131,32 +135,32 @@ func main() {
sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom)
}
app.GET("/_health", handler.Health())
app.GET("/logout", handler.Logout(), handler.ValidSession)
app.POST("/new-client", handler.NewClient(db), handler.ValidSession)
app.POST("/update-client", handler.UpdateClient(db), handler.ValidSession)
app.POST("/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
app.POST("/client/set-status", handler.SetClientStatus(db), handler.ValidSession)
app.POST("/remove-client", handler.RemoveClient(db), handler.ValidSession)
app.GET("/download", handler.DownloadClient(db), handler.ValidSession)
app.GET("/wg-server", handler.WireGuardServer(db), handler.ValidSession)
app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession)
app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession)
app.GET("/global-settings", handler.GlobalSettings(db), handler.ValidSession)
app.POST("/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession)
app.GET("/status", handler.Status(db), handler.ValidSession)
app.GET("/api/clients", handler.GetClients(db), handler.ValidSession)
app.GET("/api/client/:id", handler.GetClient(db), handler.ValidSession)
app.GET("/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession)
app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession)
app.GET("/api/apply-wg-config", handler.ApplyServerConfig(db, tmplBox), handler.ValidSession)
app.GET("/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession)
app.POST("/wake_on_lan_host", handler.SaveWakeOnLanHost(db), handler.ValidSession)
app.DELETE("/wake_on_lan_host/:mac_address", handler.DeleteWakeOnHost(db), handler.ValidSession)
app.PUT("/wake_on_lan_host/:mac_address", handler.WakeOnHost(db), handler.ValidSession)
app.GET(util.BasePath + "/_health", handler.Health())
app.GET(util.BasePath + "/logout", handler.Logout(), handler.ValidSession)
app.POST(util.BasePath + "/new-client", handler.NewClient(db), handler.ValidSession)
app.POST(util.BasePath + "/update-client", handler.UpdateClient(db), handler.ValidSession)
app.POST(util.BasePath + "/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
app.POST(util.BasePath + "/client/set-status", handler.SetClientStatus(db), handler.ValidSession)
app.POST(util.BasePath + "/remove-client", handler.RemoveClient(db), handler.ValidSession)
app.GET(util.BasePath + "/download", handler.DownloadClient(db), handler.ValidSession)
app.GET(util.BasePath + "/wg-server", handler.WireGuardServer(db), handler.ValidSession)
app.POST(util.BasePath + "/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession)
app.POST(util.BasePath + "/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession)
app.GET(util.BasePath + "/global-settings", handler.GlobalSettings(db), handler.ValidSession)
app.POST(util.BasePath + "/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession)
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.GET(util.BasePath + "/api/apply-wg-config", handler.ApplyServerConfig(db, tmplBox), handler.ValidSession)
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)
app.DELETE(util.BasePath + "/wake_on_lan_host/:mac_address", handler.DeleteWakeOnHost(db), handler.ValidSession)
app.PUT(util.BasePath + "/wake_on_lan_host/:mac_address", handler.WakeOnHost(db), handler.ValidSession)
// servers other static files
app.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler)))
app.GET(util.BasePath + "/static/*", echo.WrapHandler(http.StripPrefix(util.BasePath + "/static/", assetHandler)))
app.Logger.Fatal(app.Start(util.BindAddress))
}

View file

@ -10,19 +10,19 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Font Awesome -->
<link rel="stylesheet" href="static/plugins/fontawesome-free/css/all.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css">
<!-- iCheck for checkboxes and radio inputs -->
<link rel="stylesheet" href="static/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<!-- Select2 -->
<link rel="stylesheet" href="static/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/select2/css/select2.min.css">
<!-- Toastr -->
<link rel="stylesheet" href="static/plugins/toastr/toastr.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/toastr/toastr.min.css">
<!-- Jquery Tags Input -->
<link rel="stylesheet" href="static/plugins/jquery-tags-input/dist/jquery.tagsinput.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/jquery-tags-input/dist/jquery.tagsinput.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- overlayScrollbars -->
<link rel="stylesheet" href="static/dist/css/adminlte.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/dist/css/adminlte.min.css">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
@ -65,7 +65,7 @@
data-target="#modal_apply_config"><i class="nav-icon fas fa-check"></i> Apply
Config</button>
{{if .baseData.CurrentUser}}
<button onclick="location.href='/logout';" style="margin-left: 0.5em;" type="button"
<button onclick="location.href='{{.basePath}}/logout';" style="margin-left: 0.5em;" type="button"
class="btn btn-outline-danger btn-sm"><i class="nav-icon fas fa-sign-out-alt"></i> Logout</button>
{{end}}
</div>
@ -75,7 +75,7 @@
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="/" class="brand-link">
<a href="{{.basePath}}" class="brand-link">
<span class="brand-text">&nbsp; WIREGUARD UI</span>
</a>
@ -96,7 +96,7 @@
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
<li class="nav-header">MAIN</li>
<li class="nav-item">
<a href="/" class="nav-link {{if eq .baseData.Active ""}}active{{end}}">
<a href="{{.basePath}}" class="nav-link {{if eq .baseData.Active ""}}active{{end}}">
<i class="nav-icon fas fa-user-secret"></i>
<p>
Wireguard Clients
@ -104,7 +104,7 @@
</a>
</li>
<li class="nav-item">
<a href="/wg-server" class="nav-link {{if eq .baseData.Active "wg-server" }}active{{end}}">
<a href="{{.basePath}}/wg-server" class="nav-link {{if eq .baseData.Active "wg-server" }}active{{end}}">
<i class="nav-icon fas fa-server"></i>
<p>
Wireguard Server
@ -113,7 +113,7 @@
</li>
<li class="nav-header">SETTINGS</li>
<li class="nav-item">
<a href="/global-settings" class="nav-link {{if eq .baseData.Active "global-settings" }}active{{end}}">
<a href="{{.basePath}}/global-settings" class="nav-link {{if eq .baseData.Active "global-settings" }}active{{end}}">
<i class="nav-icon fas fa-cog"></i>
<p>
Global Settings
@ -122,7 +122,7 @@
</li>
<li class="nav-header">UTILITIES</li>
<li class="nav-item">
<a href="/status" class="nav-link {{if eq .baseData.Active "status" }}active{{end}}">
<a href="{{.basePath}}/status" class="nav-link {{if eq .baseData.Active "status" }}active{{end}}">
<i class="nav-icon fas fa-signal"></i>
<p>
Status
@ -130,7 +130,7 @@
</a>
</li>
<li class="nav-item">
<a href="/wake_on_lan_hosts" class="nav-link {{if eq .baseData.Active "wake_on_lan_hosts" }}active{{end}}">
<a href="{{.basePath}}/wake_on_lan_hosts" class="nav-link {{if eq .baseData.Active "wake_on_lan_hosts" }}active{{end}}">
<i class="nav-icon fas fa-solid fa-power-off"></i>
<p>
WoL Hosts
@ -295,21 +295,21 @@
<!-- ./wrapper -->
<!-- jQuery -->
<script src="static/plugins/jquery/jquery.min.js"></script>
<script src="{{.basePath}}/static/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{.basePath}}/static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Select2 -->
<script src="static/plugins/select2/js/select2.full.min.js"></script>
<script src="{{.basePath}}/static/plugins/select2/js/select2.full.min.js"></script>
<!-- jquery-validation -->
<script src="static/plugins/jquery-validation/jquery.validate.min.js"></script>
<script src="{{.basePath}}/static/plugins/jquery-validation/jquery.validate.min.js"></script>
<!-- Toastr -->
<script src="static/plugins/toastr/toastr.min.js"></script>
<script src="{{.basePath}}/static/plugins/toastr/toastr.min.js"></script>
<!-- Jquery Tags Input -->
<script src="static/plugins/jquery-tags-input/dist/jquery.tagsinput.min.js"></script>
<script src="{{.basePath}}/static/plugins/jquery-tags-input/dist/jquery.tagsinput.min.js"></script>
<!-- AdminLTE App -->
<script src="static/dist/js/adminlte.min.js"></script>
<script src="{{.basePath}}/static/dist/js/adminlte.min.js"></script>
<!-- Custom js -->
<script src="static/custom/js/helper.js"></script>
<script src="{{.basePath}}/static/custom/js/helper.js"></script>
<script>
// initialize all tooltips
$(function () {
@ -322,7 +322,7 @@
$.ajax({
cache: false,
method: 'GET',
url: '/api/client/' + client_id,
url: '{{.basePath}}/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {
@ -368,7 +368,7 @@
$.ajax({
cache: false,
method: 'POST',
url: '/new-client',
url: '{{.basePath}}/new-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -393,7 +393,7 @@
$.ajax({
cache: false,
method: 'GET',
url: '/api/suggest-client-ips',
url: '{{.basePath}}/api/suggest-client-ips',
dataType: 'json',
contentType: "application/json",
success: function(data) {
@ -495,7 +495,7 @@
$.ajax({
cache: false,
method: 'GET',
url: '/api/apply-wg-config',
url: '{{.basePath}}/api/apply-wg-config',
dataType: 'json',
contentType: "application/json",
success: function(data) {

View file

@ -189,7 +189,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'GET',
url: '/api/clients',
url: '{{.basePath}}/api/clients',
dataType: 'json',
contentType: "application/json",
success: function (data) {
@ -207,7 +207,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'POST',
url: '/client/set-status',
url: '{{.basePath}}/client/set-status',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -276,7 +276,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'POST',
url: '/remove-client',
url: '{{.basePath}}/remove-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -339,7 +339,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'GET',
url: '/api/client/' + client_id,
url: '{{.basePath}}/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {
@ -384,7 +384,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'POST',
url: '/email-client',
url: '{{.basePath}}/email-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -431,7 +431,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'POST',
url: '/update-client',
url: '{{.basePath}}/update-client',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -465,7 +465,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'GET',
url: '/api/client/' + client_id,
url: '{{.basePath}}/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {
@ -491,7 +491,7 @@ Wireguard Clients
$.ajax({
cache: false,
method: 'GET',
url: '/api/client/' + client_id,
url: '{{.basePath}}/api/client/' + client_id,
dataType: 'json',
contentType: "application/json",
success: function (resp) {

View file

@ -156,7 +156,7 @@ Global Settings
$.ajax({
cache: false,
method: 'POST',
url: '/global-settings',
url: '{{.basePath}}/global-settings',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),

View file

@ -9,13 +9,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Font Awesome -->
<link rel="stylesheet" href="static/plugins/fontawesome-free/css/all.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- icheck bootstrap -->
<link rel="stylesheet" href="static/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="static/dist/css/adminlte.min.css">
<link rel="stylesheet" href="{{.basePath}}/static/dist/css/adminlte.min.css">
<!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
</head>
@ -71,11 +71,11 @@
</div>
<!-- /.login-box -->
<!-- jQuery -->
<script src="static/plugins/jquery/jquery.min.js"></script>
<script src="{{.basePath}}/static/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{.basePath}}/static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App -->
<script src="static/dist/js/adminlte.min.js"></script>
<script src="{{.basePath}}/static/dist/js/adminlte.min.js"></script>
</body>
<script>
@ -85,7 +85,7 @@
if (nextURL) {
window.location.href = nextURL;
} else {
window.location.href = '/';
window.location.href = '/wireguard/';
}
}
</script>
@ -100,12 +100,11 @@
const username = $("#username").val();
const password = $("#password").val();
const data = {"username": username, "password": password}
console.log(data);
$.ajax({
cache: false,
method: 'POST',
url: '/login',
url: '{{.basePath}}/login',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),

View file

@ -136,7 +136,7 @@ Wireguard Server Settings
$.ajax({
cache: false,
method: 'POST',
url: '/wg-server/interfaces',
url: '{{.basePath}}/wg-server/interfaces',
dataType: 'json',
contentType: "application/json",
data: JSON.stringify(data),
@ -210,7 +210,7 @@ Wireguard Server Settings
$.ajax({
cache: false,
method: 'POST',
url: '/wg-server/keypair',
url: '{{.basePath}}/wg-server/keypair',
dataType: 'json',
contentType: "application/json",
success: function(data) {

View file

@ -119,5 +119,5 @@
{{end}}
{{define "bottom_js"}}
<script src="/static/custom/js/wake_on_lan_hosts.js"></script>
<script src="{{.basePath}}/static/custom/js/wake_on_lan_hosts.js"></script>
{{end}}

View file

@ -1,5 +1,7 @@
package util
import "strings"
// Runtime config
var (
DisableLogin bool
@ -17,6 +19,7 @@ var (
EmailContent string
SessionSecret []byte
WgConfTemplate string
BasePath string
)
const (
@ -32,3 +35,13 @@ const (
UsernameEnvVar = "WGUI_USERNAME"
PasswordEnvVar = "WGUI_PASSWORD"
)
func ParseBasePath(basePath string) string {
if !strings.HasPrefix(basePath, "/") {
basePath = "/" + basePath
}
if strings.HasSuffix(basePath, "/") {
basePath = strings.TrimSuffix(basePath, "/")
}
return basePath
}