mirror of
https://github.com/h44z/wg-portal.git
synced 2025-12-14 10:36:18 +00:00
WIP: support for multiple WireGuard devices (#2)
This commit is contained in:
@@ -4,9 +4,12 @@ import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/wireguard"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/common"
|
||||
"github.com/h44z/wg-portal/internal/users"
|
||||
"github.com/pkg/errors"
|
||||
@@ -15,28 +18,29 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (s *Server) PrepareNewPeer() (Peer, error) {
|
||||
device := s.peers.GetDevice()
|
||||
// PrepareNewPeer initiates a new peer for the given WireGuard device.
|
||||
func (s *Server) PrepareNewPeer(device string) (wireguard.Peer, error) {
|
||||
dev := s.peers.GetDevice(device)
|
||||
|
||||
peer := Peer{}
|
||||
peer := wireguard.Peer{}
|
||||
peer.IsNew = true
|
||||
peer.AllowedIPsStr = device.AllowedIPsStr
|
||||
peer.IPs = make([]string, len(device.IPs))
|
||||
for i := range device.IPs {
|
||||
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
|
||||
peer.AllowedIPsStr = dev.AllowedIPsStr
|
||||
peer.IPs = make([]string, len(dev.IPs))
|
||||
for i := range dev.IPs {
|
||||
freeIP, err := s.peers.GetAvailableIp(device, dev.IPs[i])
|
||||
if err != nil {
|
||||
return Peer{}, errors.WithMessage(err, "failed to get available IP addresses")
|
||||
return wireguard.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{}, errors.Wrap(err, "failed to generate key")
|
||||
return wireguard.Peer{}, errors.Wrap(err, "failed to generate key")
|
||||
}
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return Peer{}, errors.Wrap(err, "failed to generate private key")
|
||||
return wireguard.Peer{}, errors.Wrap(err, "failed to generate private key")
|
||||
}
|
||||
peer.PresharedKey = psk.String()
|
||||
peer.PrivateKey = key.String()
|
||||
@@ -46,54 +50,39 @@ func (s *Server) PrepareNewPeer() (Peer, error) {
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
func (s *Server) CreatePeerByEmail(email, identifierSuffix string, disabled bool) error {
|
||||
// CreatePeerByEmail creates a new peer for the given email. If no user with the specified email was found, a new one
|
||||
// will be created.
|
||||
func (s *Server) CreatePeerByEmail(device, email, identifierSuffix string, disabled bool) error {
|
||||
user, err := s.users.GetOrCreateUser(email)
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed to load/create related user %s", email)
|
||||
}
|
||||
|
||||
device := s.peers.GetDevice()
|
||||
peer := Peer{}
|
||||
peer.User = user
|
||||
peer.AllowedIPsStr = device.AllowedIPsStr
|
||||
peer.IPs = make([]string, len(device.IPs))
|
||||
for i := range device.IPs {
|
||||
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get available IP addresses")
|
||||
}
|
||||
peer.IPs[i] = freeIP
|
||||
}
|
||||
peer.IPsStr = common.ListToString(peer.IPs)
|
||||
psk, err := wgtypes.GenerateKey()
|
||||
peer, err := s.PrepareNewPeer(device)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate key")
|
||||
return errors.WithMessage(err, "failed to prepare new peer")
|
||||
}
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate private key")
|
||||
}
|
||||
peer.PresharedKey = psk.String()
|
||||
peer.PrivateKey = key.String()
|
||||
peer.PublicKey = key.PublicKey().String()
|
||||
peer.UID = fmt.Sprintf("u%x", md5.Sum([]byte(peer.PublicKey)))
|
||||
peer.Email = email
|
||||
peer.Identifier = fmt.Sprintf("%s %s (%s)", user.Firstname, user.Lastname, identifierSuffix)
|
||||
|
||||
now := time.Now()
|
||||
if disabled {
|
||||
peer.DeactivatedAt = &now
|
||||
}
|
||||
|
||||
return s.CreatePeer(peer)
|
||||
return s.CreatePeer(device, peer)
|
||||
}
|
||||
|
||||
func (s *Server) CreatePeer(peer Peer) error {
|
||||
device := s.peers.GetDevice()
|
||||
peer.AllowedIPsStr = device.AllowedIPsStr
|
||||
// CreatePeer creates the new peer in the database. If the peer has no assigned ip addresses, a new one will be assigned
|
||||
// automatically. Also, if the private key is empty, a new key-pair will be generated.
|
||||
// This function also configures the new peer on the physical WireGuard interface if the peer is not deactivated.
|
||||
func (s *Server) CreatePeer(device string, peer wireguard.Peer) error {
|
||||
dev := s.peers.GetDevice(device)
|
||||
peer.AllowedIPsStr = dev.AllowedIPsStr
|
||||
if peer.IPs == nil || len(peer.IPs) == 0 {
|
||||
peer.IPs = make([]string, len(device.IPs))
|
||||
for i := range device.IPs {
|
||||
freeIP, err := s.peers.GetAvailableIp(device.IPs[i])
|
||||
peer.IPs = make([]string, len(dev.IPs))
|
||||
for i := range dev.IPs {
|
||||
freeIP, err := s.peers.GetAvailableIp(device, dev.IPs[i])
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get available IP addresses")
|
||||
}
|
||||
@@ -114,11 +103,12 @@ func (s *Server) CreatePeer(peer Peer) error {
|
||||
peer.PrivateKey = key.String()
|
||||
peer.PublicKey = key.PublicKey().String()
|
||||
}
|
||||
peer.DeviceName = dev.DeviceName
|
||||
peer.UID = fmt.Sprintf("u%x", md5.Sum([]byte(peer.PublicKey)))
|
||||
|
||||
// Create WireGuard interface
|
||||
if peer.DeactivatedAt == nil {
|
||||
if err := s.wg.AddPeer(peer.GetConfig()); err != nil {
|
||||
if err := s.wg.AddPeer(device, peer.GetConfig()); err != nil {
|
||||
return errors.WithMessage(err, "failed to add WireGuard peer")
|
||||
}
|
||||
}
|
||||
@@ -128,21 +118,22 @@ func (s *Server) CreatePeer(peer Peer) error {
|
||||
return errors.WithMessage(err, "failed to create peer")
|
||||
}
|
||||
|
||||
return s.WriteWireGuardConfigFile()
|
||||
return s.WriteWireGuardConfigFile(device)
|
||||
}
|
||||
|
||||
func (s *Server) UpdatePeer(peer Peer, updateTime time.Time) error {
|
||||
// UpdatePeer updates the physical WireGuard interface and the database.
|
||||
func (s *Server) UpdatePeer(peer wireguard.Peer, updateTime time.Time) error {
|
||||
currentPeer := s.peers.GetPeerByKey(peer.PublicKey)
|
||||
|
||||
// Update WireGuard device
|
||||
var err error
|
||||
switch {
|
||||
case peer.DeactivatedAt == &updateTime:
|
||||
err = s.wg.RemovePeer(peer.PublicKey)
|
||||
err = s.wg.RemovePeer(peer.DeviceName, peer.PublicKey)
|
||||
case peer.DeactivatedAt == nil && currentPeer.Peer != nil:
|
||||
err = s.wg.UpdatePeer(peer.GetConfig())
|
||||
err = s.wg.UpdatePeer(peer.DeviceName, peer.GetConfig())
|
||||
case peer.DeactivatedAt == nil && currentPeer.Peer == nil:
|
||||
err = s.wg.AddPeer(peer.GetConfig())
|
||||
err = s.wg.AddPeer(peer.DeviceName, peer.GetConfig())
|
||||
}
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to update WireGuard peer")
|
||||
@@ -153,12 +144,13 @@ func (s *Server) UpdatePeer(peer Peer, updateTime time.Time) error {
|
||||
return errors.WithMessage(err, "failed to update peer")
|
||||
}
|
||||
|
||||
return s.WriteWireGuardConfigFile()
|
||||
return s.WriteWireGuardConfigFile(peer.DeviceName)
|
||||
}
|
||||
|
||||
func (s *Server) DeletePeer(peer Peer) error {
|
||||
// DeletePeer removes the peer from the physical WireGuard interface and the database.
|
||||
func (s *Server) DeletePeer(peer wireguard.Peer) error {
|
||||
// Delete WireGuard peer
|
||||
if err := s.wg.RemovePeer(peer.PublicKey); err != nil {
|
||||
if err := s.wg.RemovePeer(peer.DeviceName, peer.PublicKey); err != nil {
|
||||
return errors.WithMessage(err, "failed to remove WireGuard peer")
|
||||
}
|
||||
|
||||
@@ -167,15 +159,16 @@ func (s *Server) DeletePeer(peer Peer) error {
|
||||
return errors.WithMessage(err, "failed to remove peer")
|
||||
}
|
||||
|
||||
return s.WriteWireGuardConfigFile()
|
||||
return s.WriteWireGuardConfigFile(peer.DeviceName)
|
||||
}
|
||||
|
||||
func (s *Server) RestoreWireGuardInterface() error {
|
||||
activePeers := s.peers.GetActivePeers()
|
||||
// RestoreWireGuardInterface restores the state of the physical WireGuard interface from the database.
|
||||
func (s *Server) RestoreWireGuardInterface(device string) error {
|
||||
activePeers := s.peers.GetActivePeers(device)
|
||||
|
||||
for i := range activePeers {
|
||||
if activePeers[i].Peer == nil {
|
||||
if err := s.wg.AddPeer(activePeers[i].GetConfig()); err != nil {
|
||||
if err := s.wg.AddPeer(device, activePeers[i].GetConfig()); err != nil {
|
||||
return errors.WithMessage(err, "failed to add WireGuard peer")
|
||||
}
|
||||
}
|
||||
@@ -184,26 +177,29 @@ func (s *Server) RestoreWireGuardInterface() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) WriteWireGuardConfigFile() error {
|
||||
if s.config.WG.WireGuardConfig == "" {
|
||||
// WriteWireGuardConfigFile writes the configuration file for the physical WireGuard interface.
|
||||
func (s *Server) WriteWireGuardConfigFile(device string) error {
|
||||
if s.config.WG.ConfigDirectoryPath == "" {
|
||||
return nil // writing disabled
|
||||
}
|
||||
if err := syscall.Access(s.config.WG.WireGuardConfig, syscall.O_RDWR); err != nil {
|
||||
if err := syscall.Access(s.config.WG.ConfigDirectoryPath, syscall.O_RDWR); err != nil {
|
||||
return errors.Wrap(err, "failed to check WireGuard config access rights")
|
||||
}
|
||||
|
||||
device := s.peers.GetDevice()
|
||||
cfg, err := device.GetConfigFile(s.peers.GetActivePeers())
|
||||
dev := s.peers.GetDevice(device)
|
||||
cfg, err := dev.GetConfigFile(s.peers.GetActivePeers(device))
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get config file")
|
||||
}
|
||||
if err := ioutil.WriteFile(s.config.WG.WireGuardConfig, cfg, 0644); err != nil {
|
||||
filePath := path.Join(s.config.WG.ConfigDirectoryPath, dev.DeviceName+".conf")
|
||||
if err := ioutil.WriteFile(filePath, cfg, 0644); err != nil {
|
||||
return errors.Wrap(err, "failed to write WireGuard config file")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateUser(user users.User) error {
|
||||
// CreateUser creates the user in the database and optionally adds a default WireGuard peer for the user.
|
||||
func (s *Server) CreateUser(user users.User, device string) error {
|
||||
if user.Email == "" {
|
||||
return errors.New("cannot create user with empty email address")
|
||||
}
|
||||
@@ -220,9 +216,11 @@ func (s *Server) CreateUser(user users.User) error {
|
||||
}
|
||||
|
||||
// Check if user already has a peer setup, if not, create one
|
||||
return s.CreateUserDefaultPeer(user.Email)
|
||||
return s.CreateUserDefaultPeer(user.Email, device)
|
||||
}
|
||||
|
||||
// UpdateUser updates the user in the database. If the user is marked as deleted, it will get remove from the database.
|
||||
// Also, if the user is re-enabled, all it's linked WireGuard peers will be activated again.
|
||||
func (s *Server) UpdateUser(user users.User) error {
|
||||
if user.DeletedAt.Valid {
|
||||
return s.DeleteUser(user)
|
||||
@@ -249,6 +247,8 @@ func (s *Server) UpdateUser(user users.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser removes the user from the database.
|
||||
// Also, if the user has linked WireGuard peers, they will be deactivated.
|
||||
func (s *Server) DeleteUser(user users.User) error {
|
||||
currentUser := s.users.GetUserUnscoped(user.Email)
|
||||
|
||||
@@ -271,7 +271,7 @@ func (s *Server) DeleteUser(user users.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) CreateUserDefaultPeer(email string) error {
|
||||
func (s *Server) CreateUserDefaultPeer(email, device string) error {
|
||||
// Check if user is active, if not, quit
|
||||
var existingUser *users.User
|
||||
if existingUser = s.users.GetUser(email); existingUser == nil {
|
||||
@@ -282,7 +282,7 @@ func (s *Server) CreateUserDefaultPeer(email string) error {
|
||||
if s.config.Core.CreateDefaultPeer {
|
||||
peers := s.peers.GetPeersByMail(email)
|
||||
if len(peers) == 0 { // Create default vpn peer
|
||||
if err := s.CreatePeer(Peer{
|
||||
if err := s.CreatePeer(device, wireguard.Peer{
|
||||
Identifier: existingUser.Firstname + " " + existingUser.Lastname + " (Default)",
|
||||
Email: existingUser.Email,
|
||||
CreatedBy: existingUser.Email,
|
||||
|
||||
Reference in New Issue
Block a user