Private GIT

Skip to content
Snippets Groups Projects
Commit 024d2b4e authored by vx3r's avatar vx3r
Browse files

Initial commit

parents
Branches
No related tags found
No related merge requests found
.env 0 → 100644
SERVER=0.0.0.0
PORT=8080
GIN_MODE=debug
WG_CONF_DIR=./wireguard
WG_INTERFACE_NAME=wg0.conf
# Vendor
vendor/*
*.exe
.idea/*
wireguard
ui/.idea/*
ui/dist/*
.DS_Store
ui/node_modules
/dist
# local env files
ui/.env.local
ui/.env.*.local
.idea
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
stages:
- build
- deploy
build-back:
stage: build
image: golang:latest
script:
- GOOS=linux GOARCH=amd64 go build -o ${CI_PROJECT_NAME}-linux-amd64
artifacts:
paths:
- ${CI_PROJECT_NAME}-linux-amd64
build-front:
stage: build
image: node:10-alpine
script:
- cd ./ui
- npm install
- npm run build
- cd ..
artifacts:
paths:
- ui/dist
deploy:
stage: deploy
image: docker:latest
only:
- master
script:
- docker login -u ${CI_REGISTRY_USER} -p ${REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build --tag ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA} --tag ${CI_REGISTRY_IMAGE}:latest .
- docker push ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
- docker push ${CI_REGISTRY_IMAGE}:latest
FROM debian:stable-slim
WORKDIR /app
ADD wg-gen-web-linux-amd64 .
ADD .env .
ADD ui/dist ui/dist
RUN chmod +x ./wg-gen-web-linux-amd64
EXPOSE 8080
CMD ["/app/wg-gen-web-linux-amd64"]
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2013 Stephen Mathieson <me@stephenmathieson.com>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
\ No newline at end of file
# Wg Gen Web
Simple Web based configuration generator for [WireGuard](https://wireguard.com).
## Whay another one ?
All WireGuard UI implementation are trying to manage the WireGuard by applying configurations or creation network rules.
This implementation only generate configuration and its up to you to create network rules and apply configuration to WireGuard.
For example by monituring generated directory with [inotifywait](https://github.com/inotify-tools/inotify-tools/wiki).
The goal is to run Wg Gen Web in a container and WireGuard on host system.
## Features
* Self-serve and web based
* Automatically select IP from networks chosen for client
* QR-Code for convenient mobile client configuration
* Enable / Disable client
* Generation of `wg0.conf` after any modification
* Dockerized
* Pretty cool look
![Screenshot](Wg-Gen-Web.png)
## Running
The easiest way to run wireguard-ui is using the container image
```
docker run --rm -it -v /tmp/wireguard:/data -p 8080:8080 -e "WG_CONF_DIR=/data" vx3r/wg-gen-web:latest
```
docker compose snipped
```
version: '3.6'
services:
wg-gen-web:
image: vx3r/wg-gen-web:latest
container_name: wg-gen-web
restart: unless-stopped
expose:
- "8080/tcp"
environment:
- WG_CONF_DIR=/data
- WG_INTERFACE_NAME=wg0.conf
volumes:
- /mnt/raid-lv-data/docker-persistent-data/wg-gen-web:/data
```
## What is out of scope
* Generation or application of any `iptables` or `nftables` rules
* Application of configuration to WireGuard
## TODO
* Full setup example with `inotifywait` and `systemd`
* Multi-user support behind [Authelia](https://github.com/authelia/authelia) (suggestions / thoughts are welcome)
* Send configs by email to client
## License
* Do What the Fuck You Want to Public License. [LICENSE-WTFPL](LICENSE-WTFPL) or http://www.wtfpl.net
Wg-Gen-Web.png

150 KiB

package api
import (
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/skip2/go-qrcode"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/repository"
"net/http"
)
// ApplyRoutes applies router to gin Router
func ApplyRoutes(r *gin.Engine) {
client := r.Group("/api/v1.0/client")
{
client.POST("", createClient)
client.GET("/:id", readClient)
client.PATCH("/:id", updateClient)
client.DELETE("/:id", deleteClient)
client.GET("", readClients)
client.GET("/:id/config", configClient)
}
server := r.Group("/api/v1.0/server")
{
server.GET("", readServer)
server.PATCH("", updateServer)
}
}
func createClient(c *gin.Context) {
var data model.Client
if err := c.ShouldBindJSON(&data); err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to bind")
c.AbortWithStatus(http.StatusUnprocessableEntity)
return
}
client, err := repository.CreateClient(&data)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to create client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, client)
}
func readClient(c *gin.Context) {
id := c.Param("id")
client, err := repository.ReadClient(id)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to read client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, client)
}
func updateClient(c *gin.Context) {
var data model.Client
id := c.Param("id")
if err := c.ShouldBindJSON(&data); err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to bind")
c.AbortWithStatus(http.StatusUnprocessableEntity)
return
}
client, err := repository.UpdateClient(id, &data)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to update client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, client)
}
func deleteClient(c *gin.Context) {
id := c.Param("id")
err := repository.DeleteClient(id)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to remove client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, gin.H{})
}
func readClients(c *gin.Context) {
clients, err := repository.ReadClients()
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to list clients")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, clients)
}
func configClient(c *gin.Context) {
configData, err := repository.ReadClientConfig(c.Param("id"))
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to read client config")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
formatQr := c.DefaultQuery("qrcode", "false")
if formatQr == "false" {
// return config as txt file
c.Header("Content-Disposition", "attachment; filename=wg0.conf")
c.Data(http.StatusOK, "application/config", configData)
return
} else {
// return config as png qrcode
png, err := qrcode.Encode(string(configData), qrcode.Medium, 220)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to create qrcode")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.Data(http.StatusOK, "image/png", png)
return
}
}
func readServer(c *gin.Context) {
client, err := repository.ReadServer()
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to read client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, client)
}
func updateServer(c *gin.Context) {
var data model.Server
if err := c.ShouldBindJSON(&data); err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to bind")
c.AbortWithStatus(http.StatusUnprocessableEntity)
return
}
client, err := repository.UpdateServer(&data)
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Error("failed to update client")
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.JSON(http.StatusOK, client)
}
\ No newline at end of file
go.mod 0 → 100644
module gitlab.127-0-0-1.fr/vx3r/wg-gen-web
go 1.13
require (
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e
github.com/gin-contrib/cors v1.3.0
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/gin v1.5.0
github.com/joho/godotenv v1.3.0
github.com/satori/go.uuid v1.2.0
github.com/sirupsen/logrus v1.4.2
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200114203027-fcfc50b29cbb
)
go.sum 0 → 100644
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk=
github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8=
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/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg=
github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 h1:xLG16iua01X7Gzms9045s2Y2niNpvSY/Zb1oBwgNYZY=
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2/go.mod h1:VhW/Ch/3FhimwZb8Oj+qJmdMmoB8r7lmJ5auRjm50oQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
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.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
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 h1:vySPY5Oxnn/8lxAPn2cK6kAzcZzYJl3KriSLO46OT18=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
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/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/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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20190503192946-f4e77d36d62c/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-20191003171128-d98b1b443823/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-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
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-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2YhuY9zevDG2ORt5qBUI=
golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.zx2c4.com/wireguard v0.0.20191012 h1:sdX+y3hrHkW8KJkjY7ZgzpT5Tqo8XnBkH55U1klphko=
golang.zx2c4.com/wireguard v0.0.20191012/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200114203027-fcfc50b29cbb h1:EZFZIHfDUPApqlA2wgF5LBAXKIKAxNckrehUTPYYAHc=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200114203027-fcfc50b29cbb/go.mod h1:vpFXH8L2bfaEJ/8I7DZ0CVOHsVydo6KeW9Iqh3qMa4g=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
main.go 0 → 100644
package main
import (
"fmt"
"github.com/danielkov/gin-helmet"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
"os"
"path/filepath"
)
func init() {
log.SetFormatter(&log.TextFormatter{})
log.SetOutput(os.Stderr)
log.SetLevel(log.DebugLevel)
}
func main() {
// load .env environment variables
err := godotenv.Load()
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Fatal("failed to initialize env")
}
// check directories or create it
if !util.DirectoryExists(filepath.Join(os.Getenv("WG_CONF_DIR"))) {
err = os.Mkdir(filepath.Join(os.Getenv("WG_CONF_DIR")), 0755)
if err != nil {
log.WithFields(log.Fields{
"err": err,
"dir": filepath.Join(os.Getenv("WG_CONF_DIR")),
}).Fatal("failed to mkdir")
}
}
if os.Getenv("GIN_MODE") == "release" {
// set gin release mode
gin.SetMode(gin.ReleaseMode)
// disable console color
gin.DisableConsoleColor()
}
// creates a gin router with default middleware: logger and recovery (crash-free) middleware
app := gin.Default()
// same as
config := cors.DefaultConfig()
config.AllowAllOrigins = true
app.Use(cors.New(config))
//app.Use(cors.Default())
// protection
app.Use(helmet.Default())
// no route redirect to frontend app
app.NoRoute(func(c *gin.Context) {
c.Redirect(301, "/index.html")
})
// serve static files
app.Use(static.Serve("/", static.LocalFile("./ui/dist", false)))
// apply api router
api.ApplyRoutes(app)
err = app.Run(fmt.Sprintf("%s:%s", os.Getenv("SERVER"), os.Getenv("PORT")))
if err != nil {
log.WithFields(log.Fields{
"err": err,
}).Fatal("failed to start server")
}
}
package model
import "time"
type Client struct {
Id string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Enable bool `json:"enable"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
AllowedIPs string `json:"allowedIPs"`
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
}
package model
import "time"
type Server struct {
Name string `json:"name"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Address string `json:"address"`
ListenPort int `json:"listenPort"`
PrivateKey string `json:"privateKey"`
PublicKey string `json:"publicKey"`
PresharedKey string `json:"presharedKey"`
Endpoint string `json:"endpoint"`
PersistentKeepalive int `json:"persistentKeepalive"`
Dns string `json:"dns"`
}
package repository
import (
"encoding/json"
"errors"
uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
func CreateClient(client *model.Client) (*model.Client, error) {
u := uuid.NewV4()
client.Id = u.String()
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return nil, err
}
client.PrivateKey = key.String()
client.PublicKey = key.PublicKey().String()
// find available IP address from selected networks
clients, err := ReadClients()
if err != nil {
return nil, err
}
reserverIps := make([]string, 0)
for _, client := range clients {
ips := strings.Split(client.Address, ",")
for i := range ips {
if util.IsIPv6(ips[i]){
ips[i] = strings.ReplaceAll(strings.TrimSpace(ips[i]), "/128","")
} else {
ips[i] = strings.ReplaceAll(strings.TrimSpace(ips[i]), "/32","")
}
}
reserverIps = append(reserverIps, ips...)
}
networks := strings.Split(client.Address, ",")
for i := range networks {
networks[i] = strings.TrimSpace(networks[i])
}
ips := make([]string, 0)
for _, network := range networks {
ip, err := util.GetAvailableIp(network, reserverIps)
if err != nil {
return nil, err
}
if util.IsIPv6(ip){
ip = ip + "/128"
} else {
ip = ip + "/32"
}
ips = append(ips, ip)
}
client.Address = strings.Join(ips, ",")
client.Created = time.Now().UTC()
client.Updated = client.Created
err = serialize(client.Id, client)
if err != nil {
return nil, err
}
v, err := deserialize(client.Id)
if err != nil {
return nil, err
}
client = v.(*model.Client)
return client, nil
}
func ReadClient(id string) (*model.Client, error) {
v, err := deserialize(id)
if err != nil {
return nil, err
}
client := v.(*model.Client)
return client, nil
}
func ReadClientConfig(id string) ([]byte, error) {
client, err := ReadClient(id)
if err != nil {
return nil, err
}
server, err := ReadServer()
if err != nil {
return nil, err
}
configDataWg, err := util.DumpClient(client, server)
if err != nil {
return nil, err
}
return configDataWg.Bytes(), nil
}
func UpdateClient(Id string, client *model.Client) (*model.Client, error) {
v, err := deserialize(Id)
if err != nil {
return nil, err
}
current := v.(*model.Client)
if current.Id != client.Id {
return nil, errors.New("records Id mismatch")
}
// keep keys
client.PrivateKey = current.PrivateKey
client.PublicKey = current.PublicKey
client.Updated = time.Now().UTC()
err = serialize(client.Id, client)
if err != nil {
return nil, err
}
v, err = deserialize(Id)
if err != nil {
return nil, err
}
client = v.(*model.Client)
return client, nil
}
func DeleteClient(id string) error {
path := filepath.Join(os.Getenv("WG_CONF_DIR"), id)
err := os.Remove(path)
if err != nil {
return err
}
// data modified, dump new config
return generateWgConfig()
}
func ReadClients() ([]*model.Client, error) {
clients := make([]*model.Client, 0)
files, err := ioutil.ReadDir(filepath.Join(os.Getenv("WG_CONF_DIR")))
if err != nil {
return nil, err
}
for _, f := range files {
// clients file name is an uuid
_, err := uuid.FromString(f.Name())
if err == nil {
c, err := deserialize(f.Name())
if err != nil {
log.WithFields(log.Fields{
"err": err,
"path": f.Name(),
}).Error("failed to deserialize client")
} else {
clients = append(clients, c.(*model.Client))
}
}
}
sort.Slice(clients, func(i, j int) bool {
return clients[i].Created.After(clients[j].Created)
})
return clients, nil
}
/*
* Return server object, create default one
*/
func ReadServer() (*model.Server, error) {
if !util.FileExists(filepath.Join(os.Getenv("WG_CONF_DIR"), "server.json")) {
server := &model.Server{}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return nil, err
}
server.PrivateKey = key.String()
server.PublicKey = key.PublicKey().String()
presharedKey, err := wgtypes.GenerateKey()
if err != nil {
return nil, err
}
server.PresharedKey = presharedKey.String()
server.Name = "Created with default values"
server.Endpoint = "wireguard.example.com:123"
server.ListenPort = 51820
server.Address = "fd9f:6666::10:6:6:1/112, 10.6.6.1/24"
server.Dns = "fd9f::10:0:0:2, 10.0.0.2"
server.PersistentKeepalive = 16
server.Created = time.Now().UTC()
server.Updated = server.Created
err = serialize("server.json", server)
if err != nil {
return nil, err
}
}
c, err := deserialize("server.json")
if err != nil {
return nil, err
}
return c.(*model.Server), nil
}
/*
* Update server, keep private values from existing one
*/
func UpdateServer(server *model.Server) (*model.Server, error) {
current, err := deserialize("server.json")
if err != nil {
return nil, err
}
server.PrivateKey = current.(*model.Server).PrivateKey
server.PublicKey = current.(*model.Server).PublicKey
server.PresharedKey = current.(*model.Server).PresharedKey
server.Updated = time.Now().UTC()
err = serialize("server.json", server)
if err != nil {
return nil, err
}
v, err := deserialize("server.json")
if err != nil {
return nil, err
}
server = v.(*model.Server)
return server, nil
}
/*
* Write object to disk
*/
func serialize(id string, c interface{}) error {
b, err := json.Marshal(c)
if err != nil {
return err
}
err = util.WriteFile(filepath.Join(os.Getenv("WG_CONF_DIR"), id), b)
if err != nil {
return err
}
// data modified, dump new config
return generateWgConfig()
}
/*
* Read client from disc
*/
func deserializeClient(data []byte) (*model.Client, error) {
var c *model.Client
err := json.Unmarshal(data, &c)
if err != nil {
return nil, err
}
return c, nil
}
/*
* Read server from disc
*/
func deserializeServer(data []byte) (*model.Server, error) {
var c *model.Server
err := json.Unmarshal(data, &c)
if err != nil {
return nil, err
}
return c, nil
}
func deserialize(id string) (interface{}, error) {
path := filepath.Join(os.Getenv("WG_CONF_DIR"), id)
b, err := util.ReadFile(path)
if err != nil {
return nil, err
}
if id == "server.json" {
return deserializeServer(b)
}
return deserializeClient(b)
}
/*
* Generate Wireguard interface configuration
*/
func generateWgConfig() error {
clients, err := ReadClients()
if err != nil {
return err
}
server, err := ReadServer()
if err != nil {
return err
}
configDataWg, err := util.DumpServerWg(clients, server)
if err != nil {
return err
}
err = util.WriteFile(filepath.Join(os.Getenv("WG_CONF_DIR"), os.Getenv("WG_INTERFACE_NAME")), configDataWg.Bytes())
if err != nil {
return err
}
return nil
}
\ No newline at end of file
> 1%
last 2 versions
# ui
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
This diff is collapsed.
{
"name": "ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^0.19.2",
"moment": "^2.24.0",
"vue": "^2.6.10",
"vue-moment": "^4.1.0",
"vue-plugin-axios": "^1.0.14",
"vue-router": "^3.1.3",
"vuetify": "^2.1.0"
},
"devDependencies": {
"@vue/cli-plugin-router": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"vue-cli-plugin-vuetify": "^2.0.3",
"vue-template-compiler": "^2.6.10",
"vuetify-loader": "^1.3.0"
}
}
ui/public/favicon.ico

4.19 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>ui</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but ui doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment