This commit is contained in:
Christoph Haas
2021-02-26 22:17:04 +01:00
parent 8ea82c1916
commit 9faa459c44
19 changed files with 228 additions and 165 deletions

View File

@@ -3,14 +3,13 @@ package server
import (
"sort"
"github.com/h44z/wg-portal/internal/authentication"
"github.com/gin-gonic/gin"
"github.com/h44z/wg-portal/internal/authentication"
"github.com/h44z/wg-portal/internal/users"
"github.com/sirupsen/logrus"
)
// Auth auth struct
// AuthManager keeps track of available authentication providers.
type AuthManager struct {
Server *Server
Group *gin.RouterGroup // basic group for all providers (/auth)
@@ -38,7 +37,7 @@ func (auth *AuthManager) RegisterProviderWithoutError(provider authentication.Au
auth.RegisterProvider(provider)
}
// GetProvider get provider with name
// GetProvider get provider by name
func (auth *AuthManager) GetProvider(name string) authentication.AuthProvider {
for _, provider := range auth.providers {
if provider.GetName() == name {
@@ -48,15 +47,23 @@ func (auth *AuthManager) GetProvider(name string) authentication.AuthProvider {
return nil
}
// GetProviders return registered providers
// GetProviders return registered providers.
// Returned providers are ordered by provider priority.
func (auth *AuthManager) GetProviders() (providers []authentication.AuthProvider) {
for _, provider := range auth.providers {
providers = append(providers, provider)
}
// order by priority
sort.SliceStable(providers, func(i, j int) bool {
return providers[i].GetPriority() < providers[j].GetPriority()
})
return
}
// GetProviders return registered providers
// GetProvidersForType return registered providers for the given type.
// Returned providers are ordered by provider priority.
func (auth *AuthManager) GetProvidersForType(typ authentication.AuthProviderType) (providers []authentication.AuthProvider) {
for _, provider := range auth.providers {
if provider.GetType() == typ {

View File

@@ -8,7 +8,6 @@ import (
"github.com/h44z/wg-portal/internal/authentication"
"github.com/h44z/wg-portal/internal/users"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
func (s *Server) GetLogin(c *gin.Context) {
@@ -85,10 +84,6 @@ func (s *Server) PostLogin(c *gin.Context) {
loginProvider = provider
// create new user in the database (or reactivate him)
if user, err = s.users.GetOrCreateUserUnscoped(email); err != nil {
s.GetHandleError(c, http.StatusInternalServerError, "login error", "failed to create new user")
return
}
userData, err := loginProvider.GetUserModel(&authentication.AuthContext{
Username: email,
})
@@ -96,23 +91,25 @@ func (s *Server) PostLogin(c *gin.Context) {
s.GetHandleError(c, http.StatusInternalServerError, "login error", err.Error())
return
}
user.Firstname = userData.Firstname
user.Lastname = userData.Lastname
user.Email = userData.Email
user.Phone = userData.Phone
user.IsAdmin = userData.IsAdmin
user.Source = users.UserSource(loginProvider.GetName())
user.DeletedAt = gorm.DeletedAt{} // reset deleted flag
if err = s.users.UpdateUser(user); err != nil {
if err := s.CreateUser(users.User{
Email: userData.Email,
Source: users.UserSource(loginProvider.GetName()),
IsAdmin: userData.IsAdmin,
Firstname: userData.Firstname,
Lastname: userData.Lastname,
Phone: userData.Phone,
}); err != nil {
s.GetHandleError(c, http.StatusInternalServerError, "login error", "failed to update user data")
return
}
user = s.users.GetUser(username)
break
}
}
// Check if user is authenticated
if email == "" || loginProvider == nil {
if email == "" || loginProvider == nil || user == nil {
c.Redirect(http.StatusSeeOther, "/auth/login?err=authfail")
return
}
@@ -126,17 +123,9 @@ func (s *Server) PostLogin(c *gin.Context) {
sessionData.Lastname = user.Lastname
// Check if user already has a peer setup, if not create one
if s.config.Core.CreateDefaultPeer {
peers := s.peers.GetPeersByMail(sessionData.Email)
if len(peers) == 0 { // Create vpn peer
err := s.CreatePeer(Peer{
Identifier: sessionData.Firstname + " " + sessionData.Lastname + " (Default)",
Email: sessionData.Email,
CreatedBy: sessionData.Email,
UpdatedBy: sessionData.Email,
})
logrus.Errorf("Failed to automatically create vpn peer for %s: %v", sessionData.Email, err)
}
if err := s.CreateUserDefaultPeer(user.Email); err != nil {
// Not a fatal error, just log it...
logrus.Errorf("failed to automatically create vpn peer for %s: %v", sessionData.Email, err)
}
if err := UpdateSessionData(c, sessionData); err != nil {

View File

@@ -4,6 +4,8 @@ import (
"net/http"
"strconv"
"github.com/pkg/errors"
"github.com/gin-gonic/gin"
)
@@ -145,7 +147,7 @@ func (s *Server) updateFormInSession(c *gin.Context, formData interface{}) error
currentSession.FormData = formData
if err := UpdateSessionData(c, currentSession); err != nil {
return err
return errors.WithMessage(err, "failed to update form in session")
}
return nil
@@ -158,13 +160,13 @@ func (s *Server) setNewPeerFormInSession(c *gin.Context) (SessionData, error) {
if currentSession.FormData == nil || c.Query("formerr") == "" {
user, err := s.PrepareNewPeer()
if err != nil {
return currentSession, err
return currentSession, errors.WithMessage(err, "failed to prepare new peer")
}
currentSession.FormData = user
}
if err := UpdateSessionData(c, currentSession); err != nil {
return currentSession, err
return currentSession, errors.WithMessage(err, "failed to update peer form in session")
}
return currentSession, nil
@@ -179,7 +181,7 @@ func (s *Server) setFormInSession(c *gin.Context, formData interface{}) (Session
}
if err := UpdateSessionData(c, currentSession); err != nil {
return currentSession, err
return currentSession, errors.WithMessage(err, "failed to set form in session")
}
return currentSession, nil

View File

@@ -7,7 +7,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/h44z/wg-portal/internal/users"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
@@ -141,31 +140,7 @@ func (s *Server) PostAdminUsersEdit(c *gin.Context) {
}
formUser.IsAdmin = c.PostForm("isadmin") == "true"
// Update peers
if disabled != currentUser.DeletedAt.Valid {
if disabled {
// disable all peers for the given user
for _, peer := range s.peers.GetPeersByMail(currentUser.Email) {
now := time.Now()
peer.DeactivatedAt = &now
if err := s.UpdatePeer(peer, now); err != nil {
logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
}
}
} else {
// enable all peers for the given user
for _, peer := range s.peers.GetPeersByMail(currentUser.Email) {
now := time.Now()
peer.DeactivatedAt = nil
if err := s.UpdatePeer(peer, now); err != nil {
logrus.Errorf("failed to update activated peer %s: %v", peer.PublicKey, err)
}
}
}
}
// Update in database
if err := s.users.UpdateUser(&formUser); err != nil {
if err := s.UpdateUser(formUser); err != nil {
_ = s.updateFormInSession(c, formUser)
SetFlashMessage(c, "failed to update user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/users/edit?pkey="+urlEncodedKey+"&formerr=update")
@@ -242,28 +217,14 @@ func (s *Server) PostAdminUsersCreate(c *gin.Context) {
}
formUser.IsAdmin = c.PostForm("isadmin") == "true"
formUser.Source = users.UserSourceDatabase
if err := s.users.CreateUser(&formUser); err != nil {
formUser.CreatedAt = time.Time{} // reset created time
if err := s.CreateUser(formUser); err != nil {
_ = s.updateFormInSession(c, formUser)
SetFlashMessage(c, "failed to add user: "+err.Error(), "danger")
c.Redirect(http.StatusSeeOther, "/admin/users/create?formerr=create")
return
}
// Check if user already has a peer setup, if not create one
if s.config.Core.CreateDefaultPeer {
peers := s.peers.GetPeersByMail(formUser.Email)
if len(peers) == 0 { // Create vpn peer
err := s.CreatePeer(Peer{
Identifier: formUser.Firstname + " " + formUser.Lastname + " (Default)",
Email: formUser.Email,
CreatedBy: formUser.Email,
UpdatedBy: formUser.Email,
})
logrus.Errorf("Failed to automatically create vpn peer for %s: %v", formUser.Email, err)
}
}
SetFlashMessage(c, "user created successfully", "success")
c.Redirect(http.StatusSeeOther, "/admin/users/")
}

View File

@@ -124,7 +124,7 @@ func (p Peer) GetConfig() wgtypes.PeerConfig {
func (p Peer) GetConfigFile(device Device) ([]byte, error) {
tpl, err := template.New("client").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(wireguard.ClientCfgTpl)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to parse client template")
}
var tplBuff bytes.Buffer
@@ -137,7 +137,7 @@ func (p Peer) GetConfigFile(device Device) ([]byte, error) {
Server: device,
})
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to execute client template")
}
return tplBuff.Bytes(), nil
@@ -149,7 +149,7 @@ func (p Peer) GetQRCode() ([]byte, error) {
logrus.WithFields(logrus.Fields{
"err": err,
}).Error("failed to create qrcode")
return nil, err
return nil, errors.Wrap(err, "failed to encode qrcode")
}
return png, nil
}
@@ -247,7 +247,7 @@ func (d Device) GetConfig() wgtypes.Config {
func (d Device) GetConfigFile(peers []Peer) ([]byte, error) {
tpl, err := template.New("server").Funcs(template.FuncMap{"StringsJoin": strings.Join}).Parse(wireguard.DeviceCfgTpl)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to parse server template")
}
var tplBuff bytes.Buffer
@@ -260,7 +260,7 @@ func (d Device) GetConfigFile(peers []Peer) ([]byte, error) {
Server: d,
})
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to execute server template")
}
return tplBuff.Bytes(), nil
@@ -582,7 +582,7 @@ func (u *PeerManager) CreatePeer(peer Peer) error {
res := u.db.Create(&peer)
if res.Error != nil {
logrus.Errorf("failed to create peer: %v", res.Error)
return res.Error
return errors.Wrap(res.Error, "failed to create peer")
}
return nil
@@ -596,7 +596,7 @@ func (u *PeerManager) UpdatePeer(peer Peer) error {
res := u.db.Save(&peer)
if res.Error != nil {
logrus.Errorf("failed to update peer: %v", res.Error)
return res.Error
return errors.Wrap(res.Error, "failed to update peer")
}
return nil
@@ -606,7 +606,7 @@ func (u *PeerManager) DeletePeer(peer Peer) error {
res := u.db.Delete(&peer)
if res.Error != nil {
logrus.Errorf("failed to delete peer: %v", res.Error)
return res.Error
return errors.Wrap(res.Error, "failed to delete peer")
}
return nil
@@ -621,7 +621,7 @@ func (u *PeerManager) UpdateDevice(device Device) error {
res := u.db.Save(&device)
if res.Error != nil {
logrus.Errorf("failed to update device: %v", res.Error)
return res.Error
return errors.Wrap(res.Error, "failed to update device")
}
return nil
@@ -637,7 +637,7 @@ func (u *PeerManager) GetAllReservedIps() ([]string, error) {
}
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to parse cidr")
}
reservedIps = append(reservedIps, ip.String())
}
@@ -650,7 +650,7 @@ func (u *PeerManager) GetAllReservedIps() ([]string, error) {
}
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to parse cidr")
}
reservedIps = append(reservedIps, ip.String())
@@ -691,11 +691,11 @@ func (u *PeerManager) IsIPReserved(cidr string) bool {
func (u *PeerManager) GetAvailableIp(cidr string) (string, error) {
reserved, err := u.GetAllReservedIps()
if err != nil {
return "", err
return "", errors.WithMessage(err, "failed to get all reserved IP addresses")
}
ip, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return "", err
return "", errors.Wrap(err, "failed to parse cidr")
}
// this two addresses are not usable

View File

@@ -84,8 +84,8 @@ func (s *Server) Setup(ctx context.Context) error {
dir := s.getExecutableDirectory()
rDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
logrus.Infof("Real working directory: %s", rDir)
logrus.Infof("Current working directory: %s", dir)
logrus.Infof("real working directory: %s", rDir)
logrus.Infof("current working directory: %s", dir)
// Init rand
rand.Seed(time.Now().UnixNano())
@@ -166,7 +166,7 @@ func (s *Server) Setup(ctx context.Context) error {
return errors.Wrap(err, "unable to pare mail template")
}
logrus.Infof("Setup of service completed!")
logrus.Infof("setup of service completed!")
return nil
}
@@ -201,7 +201,7 @@ func (s *Server) Run() {
func (s *Server) getExecutableDirectory() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
logrus.Errorf("Failed to get executable directory: %v", err)
logrus.Errorf("failed to get executable directory: %v", err)
}
if _, err := os.Stat(filepath.Join(dir, "assets")); os.IsNotExist(err) {
@@ -240,7 +240,7 @@ func GetSessionData(c *gin.Context) SessionData {
}
session.Set(SessionIdentifier, sessionData)
if err := session.Save(); err != nil {
logrus.Errorf("Failed to store session: %v", err)
logrus.Errorf("failed to store session: %v", err)
}
}
@@ -251,7 +251,7 @@ func GetFlashes(c *gin.Context) []FlashData {
session := sessions.Default(c)
flashes := session.Flashes()
if err := session.Save(); err != nil {
logrus.Errorf("Failed to store session after setting flash: %v", err)
logrus.Errorf("failed to store session after setting flash: %v", err)
}
flashData := make([]FlashData, len(flashes))
@@ -266,8 +266,8 @@ func UpdateSessionData(c *gin.Context, data SessionData) error {
session := sessions.Default(c)
session.Set(SessionIdentifier, data)
if err := session.Save(); err != nil {
logrus.Errorf("Failed to store session: %v", err)
return err
logrus.Errorf("failed to store session: %v", err)
return errors.Wrap(err, "failed to store session")
}
return nil
}
@@ -276,8 +276,8 @@ func DestroySessionData(c *gin.Context) error {
session := sessions.Default(c)
session.Delete(SessionIdentifier)
if err := session.Save(); err != nil {
logrus.Errorf("Failed to destroy session: %v", err)
return err
logrus.Errorf("failed to destroy session: %v", err)
return errors.Wrap(err, "failed to destroy session")
}
return nil
}
@@ -289,7 +289,7 @@ func SetFlashMessage(c *gin.Context, message, typ string) {
Type: typ,
})
if err := session.Save(); err != nil {
logrus.Errorf("Failed to store session after setting flash: %v", err)
logrus.Errorf("failed to store session after setting flash: %v", err)
}
}

View File

@@ -8,8 +8,11 @@ import (
"time"
"github.com/h44z/wg-portal/internal/common"
"github.com/h44z/wg-portal/internal/users"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"gorm.io/gorm"
)
func (s *Server) PrepareNewPeer() (Peer, error) {
@@ -22,18 +25,18 @@ func (s *Server) PrepareNewPeer() (Peer, error) {
for i := range device.IPs {
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
if err != nil {
return Peer{}, err
return Peer{}, errors.WithMessage(err, "failed to get available IP addresses")
}
peer.IPs[i] = freeIP
}
peer.IPsStr = common.ListToString(peer.IPs)
psk, err := wgtypes.GenerateKey()
if err != nil {
return Peer{}, err
return Peer{}, errors.Wrap(err, "failed to generate key")
}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return Peer{}, err
return Peer{}, errors.Wrap(err, "failed to generate private key")
}
peer.PresharedKey = psk.String()
peer.PrivateKey = key.String()
@@ -57,18 +60,18 @@ func (s *Server) CreatePeerByEmail(email, identifierSuffix string, disabled bool
for i := range device.IPs {
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
if err != nil {
return err
return errors.WithMessage(err, "failed to get available IP addresses")
}
peer.IPs[i] = freeIP
}
peer.IPsStr = common.ListToString(peer.IPs)
psk, err := wgtypes.GenerateKey()
if err != nil {
return err
return errors.Wrap(err, "failed to generate key")
}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return err
return errors.Wrap(err, "failed to generate private key")
}
peer.PresharedKey = psk.String()
peer.PrivateKey = key.String()
@@ -92,7 +95,7 @@ func (s *Server) CreatePeer(peer Peer) error {
for i := range device.IPs {
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
if err != nil {
return err
return errors.WithMessage(err, "failed to get available IP addresses")
}
peer.IPs[i] = freeIP
}
@@ -101,11 +104,11 @@ func (s *Server) CreatePeer(peer Peer) error {
if peer.PrivateKey == "" { // if private key is empty create a new one
psk, err := wgtypes.GenerateKey()
if err != nil {
return err
return errors.Wrap(err, "failed to generate key")
}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
return err
return errors.Wrap(err, "failed to generate private key")
}
peer.PresharedKey = psk.String()
peer.PrivateKey = key.String()
@@ -116,13 +119,13 @@ func (s *Server) CreatePeer(peer Peer) error {
// Create WireGuard interface
if peer.DeactivatedAt == nil {
if err := s.wg.AddPeer(peer.GetConfig()); err != nil {
return err
return errors.WithMessage(err, "failed to add WireGuard peer")
}
}
// Create in database
if err := s.peers.CreatePeer(peer); err != nil {
return err
return errors.WithMessage(err, "failed to create peer")
}
return s.WriteWireGuardConfigFile()
@@ -142,12 +145,12 @@ func (s *Server) UpdatePeer(peer Peer, updateTime time.Time) error {
err = s.wg.AddPeer(peer.GetConfig())
}
if err != nil {
return err
return errors.WithMessage(err, "failed to update WireGuard peer")
}
// Update in database
if err := s.peers.UpdatePeer(peer); err != nil {
return err
return errors.WithMessage(err, "failed to update peer")
}
return s.WriteWireGuardConfigFile()
@@ -156,12 +159,12 @@ func (s *Server) UpdatePeer(peer Peer, updateTime time.Time) error {
func (s *Server) DeletePeer(peer Peer) error {
// Delete WireGuard peer
if err := s.wg.RemovePeer(peer.PublicKey); err != nil {
return err
return errors.WithMessage(err, "failed to remove WireGuard peer")
}
// Delete in database
if err := s.peers.DeletePeer(peer); err != nil {
return err
return errors.WithMessage(err, "failed to remove peer")
}
return s.WriteWireGuardConfigFile()
@@ -173,7 +176,7 @@ func (s *Server) RestoreWireGuardInterface() error {
for i := range activePeers {
if activePeers[i].Peer == nil {
if err := s.wg.AddPeer(activePeers[i].GetConfig()); err != nil {
return err
return errors.WithMessage(err, "failed to add WireGuard peer")
}
}
}
@@ -186,16 +189,109 @@ func (s *Server) WriteWireGuardConfigFile() error {
return nil // writing disabled
}
if err := syscall.Access(s.config.WG.WireGuardConfig, syscall.O_RDWR); err != nil {
return err
return errors.Wrap(err, "failed to check WireGuard config access rights")
}
device := s.peers.GetDevice()
cfg, err := device.GetConfigFile(s.peers.GetActivePeers())
if err != nil {
return err
return errors.WithMessage(err, "failed to get config file")
}
if err := ioutil.WriteFile(s.config.WG.WireGuardConfig, cfg, 0644); err != nil {
return err
return errors.Wrap(err, "failed to write WireGuard config file")
}
return nil
}
func (s *Server) CreateUser(user users.User) error {
if user.Email == "" {
return errors.New("cannot create user with empty email address")
}
// Check if user already exists, if so re-enable
if existingUser := s.users.GetUserUnscoped(user.Email); existingUser != nil {
user.DeletedAt = gorm.DeletedAt{} // reset deleted flag to enable that user again
return s.UpdateUser(user)
}
// Create user in database
if err := s.users.CreateUser(&user); err != nil {
return errors.WithMessage(err, "failed to create user in manager")
}
// Check if user already has a peer setup, if not, create one
return s.CreateUserDefaultPeer(user.Email)
}
func (s *Server) UpdateUser(user users.User) error {
if user.DeletedAt.Valid {
return s.DeleteUser(user)
}
currentUser := s.users.GetUserUnscoped(user.Email)
// Update in database
if err := s.users.UpdateUser(&user); err != nil {
return errors.WithMessage(err, "failed to update user in manager")
}
// If user was deleted (disabled), reactivate it's peers
if currentUser.DeletedAt.Valid {
for _, peer := range s.peers.GetPeersByMail(user.Email) {
now := time.Now()
peer.DeactivatedAt = nil
if err := s.UpdatePeer(peer, now); err != nil {
logrus.Errorf("failed to update (re)activated peer %s for %s: %v", peer.PublicKey, user.Email, err)
}
}
}
return nil
}
func (s *Server) DeleteUser(user users.User) error {
currentUser := s.users.GetUserUnscoped(user.Email)
// Update in database
if err := s.users.DeleteUser(&user); err != nil {
return errors.WithMessage(err, "failed to delete user in manager")
}
// If user was active, disable it's peers
if !currentUser.DeletedAt.Valid {
for _, peer := range s.peers.GetPeersByMail(user.Email) {
now := time.Now()
peer.DeactivatedAt = &now
if err := s.UpdatePeer(peer, now); err != nil {
logrus.Errorf("failed to update deactivated peer %s for %s: %v", peer.PublicKey, user.Email, err)
}
}
}
return nil
}
func (s *Server) CreateUserDefaultPeer(email string) error {
// Check if user is active, if not, quit
var existingUser *users.User
if existingUser = s.users.GetUser(email); existingUser == nil {
return nil
}
// Check if user already has a peer setup, if not, create one
if s.config.Core.CreateDefaultPeer {
peers := s.peers.GetPeersByMail(email)
if len(peers) == 0 { // Create default vpn peer
if err := s.CreatePeer(Peer{
Identifier: existingUser.Firstname + " " + existingUser.Lastname + " (Default)",
Email: existingUser.Email,
CreatedBy: existingUser.Email,
UpdatedBy: existingUser.Email,
}); err != nil {
return errors.WithMessagef(err, "failed to automatically create vpn peer for %s", email)
}
}
}
return nil
}