Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Add support for user/pass authentication #163

Merged
merged 1 commit into from
Mar 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Current stable release: [v1.3.0](https://github.com/EmbarkStudios/wg-ui/releases
* Self-serve and web based
* QR-Code for convenient mobile client configuration
* Optional multi-user support behind an authenticating proxy
* Simple authentication support
* Zero external dependencies - just a single binary using the wireguard kernel module
* Binary and container deployment

Expand Down Expand Up @@ -46,6 +47,14 @@ and

are the same.

### Authentication
You can configure basic authentication using the flags/environment variables `--auth-basic-user=<user>` and `--auth-basic-pass=<bcrypt hash>` The password is
a bcrypt hash that you can generate yourself using the docker container:
```
$ docker run -it embarkstudios/wireguard-ui:latest passwd mySecretPass
INFO[0001] Password Hash: $2a$14$D2jsPnpJixC0U0lyaGUd0OatV7QGzQ08yKV.gsmITVZgNevfZXj36
```

## Docker images

There are two ways to run wg-ui today, you can run it with kernel module installed on your host which is the best way to do it if you want performance.
Expand Down
31 changes: 24 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"

log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"gopkg.in/alecthomas/kingpin.v2"
)

Expand All @@ -14,7 +15,11 @@ var (
func main() {
kingpin.HelpFlag.Short('h')
kingpin.CommandLine.DefaultEnvars()
kingpin.Parse()
kingpin.Command("server", "Start server.").Default()
passwdCmd := kingpin.Command("passwd", "Generate password hash.")
passwdCmdPassword := passwdCmd.Arg("password", "The password to hash").Required().String()
cmd := kingpin.Parse()

switch strings.ToLower(*logLevel) {
case "debug":
log.SetLevel(log.DebugLevel)
Expand All @@ -28,11 +33,23 @@ func main() {
log.SetLevel(log.InfoLevel)
}

log.Info("Starting")

server := NewServer()
err := server.Start()
if err != nil {
log.Fatal(err)
switch cmd {
case "passwd":
bytes, err := bcrypt.GenerateFromPassword([]byte(*passwdCmdPassword), 14)
if err != nil {
log.Fatalf("generate password error: %v", err)
}
log.Infof("Password Hash: %s", string(bytes))
return
case "server":
log.Info("Starting")
server := NewServer()
err := server.Start()
if err != nil {
log.Fatal(err)
}
default:
log.Fatal("Unknown command")
}

}
23 changes: 22 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/skip2/go-qrcode"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
"golang.org/x/crypto/bcrypt"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"gopkg.in/alecthomas/kingpin.v2"
Expand All @@ -40,6 +41,8 @@ var (
natLink = kingpin.Flag("nat-device", "Network interface to masquerade").Default("wlp2s0").String()
clientIPRange = kingpin.Flag("client-ip-range", "Client IP CIDR").Default("172.31.255.0/24").String()
authUserHeader = kingpin.Flag("auth-user-header", "Header containing username").Default("X-Forwarded-User").String()
authBasicUser = kingpin.Flag("auth-basic-user", "Basic auth static username").Default("").String()
authBasicPass = kingpin.Flag("auth-basic-pass", "Basic auth static password").Default("").String()
maxNumberClientConfig = kingpin.Flag("max-number-client-config", "Max number of configs an client can use. 0 is unlimited").Default("0").Int()

wgLinkName = kingpin.Flag("wg-device-name", "WireGuard network device name").Default("wg0").String()
Expand Down Expand Up @@ -421,7 +424,25 @@ func (s *Server) Start() error {

log.WithField("listenAddr", *listenAddr).Info("Starting server")

return http.ListenAndServe(*listenAddr, s.userFromHeader(router))
return http.ListenAndServe(*listenAddr, s.basicAuth(s.userFromHeader(router)))
}

func (s *Server) basicAuth(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

// If we specified a user, require auth
if *authBasicUser != "" {
u, p, ok := r.BasicAuth()
if !ok || u != *authBasicUser || bcrypt.CompareHashAndPassword([]byte(*authBasicPass), []byte(p)) != nil {
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
}

handler.ServeHTTP(w, r)

})
}

func (s *Server) userFromHeader(handler http.Handler) http.Handler {
Expand Down