2021-03-21 07:36:11 -04:00
package wireguard
2020-11-05 13:37:51 -05:00
2021-04-02 17:48:30 -04:00
// WireGuard documentation: https://manpages.debian.org/unstable/wireguard-tools/wg.8.en.html
2020-11-05 13:37:51 -05:00
import (
2020-11-06 06:21:47 -05:00
"bytes"
2020-11-05 13:37:51 -05:00
"crypto/md5"
"fmt"
"net"
2020-11-09 14:26:34 -05:00
"regexp"
"sort"
2020-11-05 13:37:51 -05:00
"strings"
"time"
2021-04-03 16:38:22 -04:00
"github.com/gin-gonic/gin"
2020-11-07 12:36:23 -05:00
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
2020-11-05 13:37:51 -05:00
"github.com/h44z/wg-portal/internal/common"
2021-02-24 15:24:45 -05:00
"github.com/pkg/errors"
2021-02-08 16:56:02 -05:00
"github.com/sirupsen/logrus"
2020-11-06 06:21:47 -05:00
"github.com/skip2/go-qrcode"
2020-11-05 13:37:51 -05:00
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"gorm.io/gorm"
)
2020-11-07 12:36:23 -05:00
//
// CUSTOM VALIDATORS ----------------------------------------------------------------------------
//
var cidrList validator . Func = func ( fl validator . FieldLevel ) bool {
cidrListStr := fl . Field ( ) . String ( )
2020-11-08 04:26:18 -05:00
cidrList := common . ParseStringList ( cidrListStr )
2020-11-07 12:36:23 -05:00
for i := range cidrList {
_ , _ , err := net . ParseCIDR ( cidrList [ i ] )
if err != nil {
return false
}
}
return true
}
var ipList validator . Func = func ( fl validator . FieldLevel ) bool {
ipListStr := fl . Field ( ) . String ( )
2020-11-08 04:26:18 -05:00
ipList := common . ParseStringList ( ipListStr )
2020-11-07 12:36:23 -05:00
for i := range ipList {
ip := net . ParseIP ( ipList [ i ] )
if ip == nil {
return false
}
}
return true
}
func init ( ) {
if v , ok := binding . Validator . Engine ( ) . ( * validator . Validate ) ; ok {
2021-02-08 16:56:02 -05:00
_ = v . RegisterValidation ( "cidrlist" , cidrList )
_ = v . RegisterValidation ( "iplist" , ipList )
2020-11-07 12:36:23 -05:00
}
}
2020-11-07 04:31:48 -05:00
//
2021-02-21 17:23:58 -05:00
// PEER ----------------------------------------------------------------------------------------
2020-11-07 04:31:48 -05:00
//
2021-02-21 17:23:58 -05:00
type Peer struct {
2021-04-03 16:38:22 -04:00
Peer * wgtypes . Peer ` gorm:"-" ` // WireGuard peer
Device * Device ` gorm:"foreignKey:DeviceName" binding:"-" ` // linked WireGuard device
2021-02-24 15:24:45 -05:00
Config string ` gorm:"-" `
2020-11-05 13:37:51 -05:00
2021-04-03 16:38:22 -04:00
UID string ` form:"uid" binding:"required,alphanum" ` // uid for html identification
DeviceName string ` gorm:"index" form:"device" binding:"required" `
2021-04-05 18:07:05 -04:00
DeviceType DeviceType ` gorm:"-" form:"devicetype" binding:"required,oneof=client server" `
2021-04-03 16:38:22 -04:00
Identifier string ` form:"identifier" binding:"required,max=64" ` // Identifier AND Email make a WireGuard peer unique
Email string ` gorm:"index" form:"mail" binding:"required,email" `
IgnoreGlobalSettings bool ` form:"ignoreglobalsettings" `
2021-04-03 13:11:05 -04:00
IsOnline bool ` gorm:"-" `
IsNew bool ` gorm:"-" `
LastHandshake string ` gorm:"-" `
LastHandshakeTime string ` gorm:"-" `
2021-04-02 17:48:30 -04:00
// Core WireGuard Settings
2021-04-03 16:38:22 -04:00
PublicKey string ` gorm:"primaryKey" form:"pubkey" binding:"required,base64" ` // the public key of the peer itself
2021-04-03 13:11:05 -04:00
PresharedKey string ` form:"presharedkey" binding:"omitempty,base64" `
AllowedIPsStr string ` form:"allowedip" binding:"cidrlist" ` // a comma separated list of IPs that are used in the client config file
Endpoint string ` form:"endpoint" binding:"omitempty,hostname_port" `
PersistentKeepalive int ` form:"keepalive" binding:"gte=0" `
2021-04-02 17:48:30 -04:00
// Misc. WireGuard Settings
2021-04-05 14:00:11 -04:00
PrivateKey string ` form:"privkey" binding:"omitempty,base64" `
IPsStr string ` form:"ip" binding:"cidrlist,required_if=DeviceType server" ` // a comma separated list of IPs of the client
DNSStr string ` form:"dns" binding:"iplist" ` // comma separated list of the DNS servers for the client
2021-04-03 16:38:22 -04:00
// Global Device Settings (can be ignored, only make sense if device is in server mode)
Mtu int ` form:"mtu" binding:"gte=0,lte=1500" `
2020-11-05 13:37:51 -05:00
DeactivatedAt * time . Time
CreatedBy string
UpdatedBy string
CreatedAt time . Time
UpdatedAt time . Time
}
2021-04-03 13:11:05 -04:00
func ( p * Peer ) SetIPAddresses ( addresses ... string ) {
p . IPsStr = common . ListToString ( addresses )
}
func ( p Peer ) GetIPAddresses ( ) [ ] string {
return common . ParseStringList ( p . IPsStr )
}
func ( p * Peer ) SetDNSServers ( addresses ... string ) {
p . DNSStr = common . ListToString ( addresses )
}
func ( p Peer ) GetDNSServers ( ) [ ] string {
return common . ParseStringList ( p . DNSStr )
}
func ( p * Peer ) SetAllowedIPs ( addresses ... string ) {
p . AllowedIPsStr = common . ListToString ( addresses )
}
func ( p Peer ) GetAllowedIPs ( ) [ ] string {
return common . ParseStringList ( p . AllowedIPsStr )
}
2021-04-05 14:00:11 -04:00
func ( p Peer ) GetConfig ( _ * Device ) wgtypes . PeerConfig {
publicKey , _ := wgtypes . ParseKey ( p . PublicKey )
2021-04-05 12:38:38 -04:00
2020-11-05 13:37:51 -05:00
var presharedKey * wgtypes . Key
2021-02-21 17:23:58 -05:00
if p . PresharedKey != "" {
presharedKeyTmp , _ := wgtypes . ParseKey ( p . PresharedKey )
2020-11-05 13:37:51 -05:00
presharedKey = & presharedKeyTmp
}
2021-04-03 16:38:22 -04:00
var endpoint * net . UDPAddr
if p . Endpoint != "" {
addr , err := net . ResolveUDPAddr ( "udp" , p . Endpoint )
if err == nil {
endpoint = addr
}
}
var keepAlive * time . Duration
if p . PersistentKeepalive != 0 {
keepAliveDuration := time . Duration ( p . PersistentKeepalive ) * time . Second
keepAlive = & keepAliveDuration
}
peerAllowedIPs := p . GetAllowedIPs ( )
allowedIPs := make ( [ ] net . IPNet , len ( peerAllowedIPs ) )
for i , ip := range peerAllowedIPs {
_ , ipNet , err := net . ParseCIDR ( ip )
if err == nil {
allowedIPs [ i ] = * ipNet
}
}
2020-11-05 13:37:51 -05:00
cfg := wgtypes . PeerConfig {
PublicKey : publicKey ,
Remove : false ,
UpdateOnly : false ,
PresharedKey : presharedKey ,
2021-04-03 16:38:22 -04:00
Endpoint : endpoint ,
PersistentKeepaliveInterval : keepAlive ,
2020-11-05 13:37:51 -05:00
ReplaceAllowedIPs : true ,
2021-04-03 16:38:22 -04:00
AllowedIPs : allowedIPs ,
2020-11-05 13:37:51 -05:00
}
return cfg
}
2021-02-21 17:23:58 -05:00
func ( p Peer ) GetConfigFile ( device Device ) ( [ ] byte , error ) {
var tplBuff bytes . Buffer
2021-04-03 16:38:22 -04:00
err := templateCache . ExecuteTemplate ( & tplBuff , "peer.tpl" , gin . H {
"Peer" : p ,
"Interface" : device ,
2021-02-21 17:23:58 -05:00
} )
if err != nil {
2021-02-26 16:17:04 -05:00
return nil , errors . Wrap ( err , "failed to execute client template" )
2021-02-21 17:23:58 -05:00
}
return tplBuff . Bytes ( ) , nil
}
func ( p Peer ) GetQRCode ( ) ( [ ] byte , error ) {
png , err := qrcode . Encode ( p . Config , qrcode . Medium , 250 )
2020-11-06 06:21:47 -05:00
if err != nil {
2021-02-08 16:56:02 -05:00
logrus . WithFields ( logrus . Fields {
2020-11-06 06:21:47 -05:00
"err" : err ,
} ) . Error ( "failed to create qrcode" )
2021-02-26 16:17:04 -05:00
return nil , errors . Wrap ( err , "failed to encode qrcode" )
2020-11-06 06:21:47 -05:00
}
return png , nil
}
2021-02-21 17:23:58 -05:00
func ( p Peer ) IsValid ( ) bool {
if p . PublicKey == "" {
2020-11-07 04:31:48 -05:00
return false
}
return true
}
2021-02-21 17:23:58 -05:00
func ( p Peer ) GetConfigFileName ( ) string {
2020-11-09 14:26:34 -05:00
reg := regexp . MustCompile ( "[^a-zA-Z0-9_-]+" )
2021-02-21 17:23:58 -05:00
return reg . ReplaceAllString ( strings . ReplaceAll ( p . Identifier , " " , "-" ) , "" ) + ".conf"
2020-11-09 14:26:34 -05:00
}
2020-11-07 04:31:48 -05:00
//
// DEVICE --------------------------------------------------------------------------------------
//
2021-04-02 17:48:30 -04:00
type DeviceType string
const (
DeviceTypeServer DeviceType = "server"
DeviceTypeClient DeviceType = "client"
)
2020-11-05 13:37:51 -05:00
type Device struct {
2020-11-06 06:21:47 -05:00
Interface * wgtypes . Device ` gorm:"-" `
2021-04-05 18:07:05 -04:00
Type DeviceType ` form:"devicetype" binding:"required,oneof=client server" `
2021-04-03 13:11:05 -04:00
DeviceName string ` form:"device" gorm:"primaryKey" binding:"required,alphanum" `
DisplayName string ` form:"displayname" binding:"omitempty,max=200" `
2021-04-02 17:48:30 -04:00
// Core WireGuard Settings (Interface section)
PrivateKey string ` form:"privkey" binding:"required,base64" `
2021-04-03 17:54:35 -04:00
ListenPort int ` form:"port" binding:"required_if=Type server,omitempty,gt=0,lt=65535" `
2021-04-03 13:11:05 -04:00
FirewallMark int32 ` form:"firewallmark" binding:"gte=0" `
2021-04-02 17:48:30 -04:00
// Misc. WireGuard Settings
2021-04-03 13:11:05 -04:00
PublicKey string ` form:"pubkey" binding:"required,base64" `
Mtu int ` form:"mtu" binding:"gte=0,lte=1500" ` // the interface MTU, wg-quick addition
IPsStr string ` form:"ip" binding:"required,cidrlist" ` // comma separated list of the IPs of the client, wg-quick addition
DNSStr string ` form:"dns" binding:"iplist" ` // comma separated list of the DNS servers of the client, wg-quick addition
RoutingTable string ` form:"routingtable" ` // the routing table, wg-quick addition
PreUp string ` form:"preup" ` // pre up script, wg-quick addition
PostUp string ` form:"postup" ` // post up script, wg-quick addition
PreDown string ` form:"predown" ` // pre down script, wg-quick addition
PostDown string ` form:"postdown" ` // post down script, wg-quick addition
SaveConfig bool ` form:"saveconfig" ` // if set to `true', the configuration is saved from the current state of the interface upon shutdown, wg-quick addition
2021-04-02 17:48:30 -04:00
// Settings that are applied to all peer by default
2021-04-03 17:54:35 -04:00
DefaultEndpoint string ` form:"endpoint" binding:"required_if=Type server,omitempty,hostname_port" `
2021-04-03 13:11:05 -04:00
DefaultAllowedIPsStr string ` form:"allowedip" binding:"cidrlist" ` // comma separated list of IPs that are used in the client config file
DefaultPersistentKeepalive int ` form:"keepalive" binding:"gte=0" `
2021-04-02 17:48:30 -04:00
CreatedAt time . Time
UpdatedAt time . Time
2020-11-05 13:37:51 -05:00
}
2020-11-06 06:21:47 -05:00
func ( d Device ) IsValid ( ) bool {
2021-04-03 13:11:05 -04:00
switch d . Type {
case DeviceTypeServer :
if d . PublicKey == "" {
return false
}
if len ( d . GetIPAddresses ( ) ) == 0 {
return false
}
if d . DefaultEndpoint == "" {
return false
}
case DeviceTypeClient :
if d . PublicKey == "" {
return false
}
if len ( d . GetIPAddresses ( ) ) == 0 {
return false
}
2020-11-05 13:37:51 -05:00
}
return true
}
2021-04-03 13:11:05 -04:00
func ( d * Device ) SetIPAddresses ( addresses ... string ) {
d . IPsStr = common . ListToString ( addresses )
}
func ( d Device ) GetIPAddresses ( ) [ ] string {
return common . ParseStringList ( d . IPsStr )
}
func ( d * Device ) SetDNSServers ( addresses ... string ) {
d . DNSStr = common . ListToString ( addresses )
}
func ( d Device ) GetDNSServers ( ) [ ] string {
return common . ParseStringList ( d . DNSStr )
}
func ( d * Device ) SetDefaultAllowedIPs ( addresses ... string ) {
d . DefaultAllowedIPsStr = common . ListToString ( addresses )
}
func ( d Device ) GetDefaultAllowedIPs ( ) [ ] string {
return common . ParseStringList ( d . DefaultAllowedIPsStr )
}
2021-02-21 17:23:58 -05:00
func ( d Device ) GetConfig ( ) wgtypes . Config {
2020-11-07 04:31:48 -05:00
var privateKey * wgtypes . Key
if d . PrivateKey != "" {
pKey , _ := wgtypes . ParseKey ( d . PrivateKey )
privateKey = & pKey
}
2021-04-03 16:38:22 -04:00
fwMark := int ( d . FirewallMark )
2020-11-07 04:31:48 -05:00
cfg := wgtypes . Config {
2021-04-03 16:38:22 -04:00
PrivateKey : privateKey ,
ListenPort : & d . ListenPort ,
FirewallMark : & fwMark ,
2020-11-07 04:31:48 -05:00
}
return cfg
}
2021-02-24 15:24:45 -05:00
func ( d Device ) GetConfigFile ( peers [ ] Peer ) ( [ ] byte , error ) {
2020-11-09 14:26:34 -05:00
var tplBuff bytes . Buffer
2021-04-03 16:38:22 -04:00
err := templateCache . ExecuteTemplate ( & tplBuff , "interface.tpl" , gin . H {
"Peers" : peers ,
"Interface" : d ,
2020-11-09 14:26:34 -05:00
} )
if err != nil {
2021-02-26 16:17:04 -05:00
return nil , errors . Wrap ( err , "failed to execute server template" )
2020-11-09 14:26:34 -05:00
}
return tplBuff . Bytes ( ) , nil
}
2020-11-07 04:31:48 -05:00
//
2021-02-24 15:24:45 -05:00
// PEER-MANAGER --------------------------------------------------------------------------------
2020-11-07 04:31:48 -05:00
//
2021-02-24 15:24:45 -05:00
type PeerManager struct {
2021-03-21 07:36:11 -04:00
db * gorm . DB
wg * Manager
2020-11-05 13:37:51 -05:00
}
2021-03-21 07:36:11 -04:00
func NewPeerManager ( db * gorm . DB , wg * Manager ) ( * PeerManager , error ) {
2021-03-22 08:00:02 -04:00
pm := & PeerManager { db : db , wg : wg }
2020-11-05 13:37:51 -05:00
2021-04-05 18:07:05 -04:00
// check if old device table exists (from version <= 1.0.3), if so migrate it.
if db . Migrator ( ) . HasColumn ( & Device { } , "endpoint" ) {
if err := db . Migrator ( ) . RenameColumn ( & Device { } , "endpoint" , "default_endpoint" ) ; err != nil {
return nil , errors . Wrapf ( err , "failed to migrate old database structure for column endpoint" )
}
}
if db . Migrator ( ) . HasColumn ( & Device { } , "allowed_ips_str" ) {
if err := db . Migrator ( ) . RenameColumn ( & Device { } , "allowed_ips_str" , "default_allowed_ips_str" ) ; err != nil {
return nil , errors . Wrapf ( err , "failed to migrate old database structure for column allowed_ips_str" )
}
}
if db . Migrator ( ) . HasColumn ( & Device { } , "persistent_keepalive" ) {
if err := db . Migrator ( ) . RenameColumn ( & Device { } , "persistent_keepalive" , "default_persistent_keepalive" ) ; err != nil {
return nil , errors . Wrapf ( err , "failed to migrate old database structure for column persistent_keepalive" )
}
}
2021-03-22 08:00:02 -04:00
if err := pm . db . AutoMigrate ( & Peer { } , & Device { } ) ; err != nil {
2021-02-24 15:24:45 -05:00
return nil , errors . WithMessage ( err , "failed to migrate peer database" )
2020-11-05 13:37:51 -05:00
}
2021-04-05 18:07:05 -04:00
if err := pm . initFromPhysicalInterface ( ) ; err != nil {
return nil , errors . WithMessagef ( err , "unable to initialize peer manager" )
}
2021-03-22 08:00:02 -04:00
// check if peers without device name exist (from version <= 1.0.3), if so assign them to the default device.
peers := make ( [ ] Peer , 0 )
pm . db . Find ( & peers )
for i := range peers {
if peers [ i ] . DeviceName == "" {
peers [ i ] . DeviceName = wg . Cfg . GetDefaultDeviceName ( )
pm . db . Save ( & peers [ i ] )
}
}
2021-04-05 12:38:38 -04:00
// validate and update existing peers if needed
for _ , deviceName := range wg . Cfg . DeviceNames {
dev := pm . GetDevice ( deviceName )
2021-04-05 18:07:05 -04:00
peers := pm . GetAllPeers ( deviceName )
2021-04-05 12:38:38 -04:00
for i := range peers {
if err := pm . fixPeerDefaultData ( & peers [ i ] , & dev ) ; err != nil {
return nil , errors . WithMessagef ( err , "unable to fix peers for interface %s" , deviceName )
}
}
}
2021-03-22 08:00:02 -04:00
return pm , nil
2020-11-05 13:37:51 -05:00
}
2021-04-05 18:07:05 -04:00
// initFromPhysicalInterface read all WireGuard peers from the WireGuard interface configuration. If a peer does not
2021-03-21 07:36:11 -04:00
// exist in the local database, it gets created.
2021-04-05 18:07:05 -04:00
func ( m * PeerManager ) initFromPhysicalInterface ( ) error {
2021-03-21 07:36:11 -04:00
for _ , deviceName := range m . wg . Cfg . DeviceNames {
peers , err := m . wg . GetPeerList ( deviceName )
if err != nil {
return errors . Wrapf ( err , "failed to get peer list for device %s" , deviceName )
2020-12-18 15:54:57 -05:00
}
2021-03-21 07:36:11 -04:00
device , err := m . wg . GetDeviceInfo ( deviceName )
if err != nil {
return errors . Wrapf ( err , "failed to get device info for device %s" , deviceName )
}
var ipAddresses [ ] string
var mtu int
if m . wg . Cfg . ManageIPAddresses {
if ipAddresses , err = m . wg . GetIPAddress ( deviceName ) ; err != nil {
return errors . Wrapf ( err , "failed to get ip address for device %s" , deviceName )
}
if mtu , err = m . wg . GetMTU ( deviceName ) ; err != nil {
return errors . Wrapf ( err , "failed to get MTU for device %s" , deviceName )
}
2020-12-18 15:54:57 -05:00
}
2020-11-05 13:37:51 -05:00
2021-04-03 17:54:35 -04:00
// Check if device already exists in database, if not, create it
if err := m . validateOrCreateDevice ( * device , ipAddresses , mtu ) ; err != nil {
return errors . WithMessagef ( err , "failed to validate device %s" , device . Name )
}
// Check if entries already exist in database, if not, create them
2021-03-21 07:36:11 -04:00
for _ , peer := range peers {
if err := m . validateOrCreatePeer ( deviceName , peer ) ; err != nil {
return errors . WithMessagef ( err , "failed to validate peer %s for device %s" , peer . PublicKey , deviceName )
}
}
2020-11-05 13:37:51 -05:00
}
2020-11-07 04:31:48 -05:00
return nil
2020-11-05 13:37:51 -05:00
}
2021-03-21 07:36:11 -04:00
// validateOrCreatePeer checks if the given WireGuard peer already exists in the database, if not, the peer entry will be created
2021-04-03 17:54:35 -04:00
// assumption: server mode is used
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) validateOrCreatePeer ( device string , wgPeer wgtypes . Peer ) error {
2021-02-21 17:23:58 -05:00
peer := Peer { }
2021-04-05 18:07:05 -04:00
m . db . Where ( "public_key = ?" , wgPeer . PublicKey . String ( ) ) . FirstOrInit ( & peer )
2021-04-05 12:38:38 -04:00
dev := m . GetDevice ( device )
2020-11-05 13:37:51 -05:00
2021-02-21 17:23:58 -05:00
if peer . PublicKey == "" { // peer not found, create
peer . UID = fmt . Sprintf ( "u%x" , md5 . Sum ( [ ] byte ( wgPeer . PublicKey . String ( ) ) ) )
2021-04-05 12:38:38 -04:00
if dev . Type == DeviceTypeServer {
peer . PublicKey = wgPeer . PublicKey . String ( )
peer . Identifier = "Autodetected Client (" + peer . PublicKey [ 0 : 8 ] + ")"
} else if dev . Type == DeviceTypeClient {
2021-04-05 14:00:11 -04:00
peer . PublicKey = wgPeer . PublicKey . String ( )
2021-04-05 12:38:38 -04:00
if wgPeer . Endpoint != nil {
peer . Endpoint = wgPeer . Endpoint . String ( )
}
2021-04-05 14:00:11 -04:00
peer . Identifier = "Autodetected Endpoint (" + peer . PublicKey [ 0 : 8 ] + ")"
2021-04-05 12:38:38 -04:00
}
2021-02-21 17:23:58 -05:00
if wgPeer . PresharedKey != ( wgtypes . Key { } ) {
peer . PresharedKey = wgPeer . PresharedKey . String ( )
2020-11-05 13:37:51 -05:00
}
2021-02-21 17:23:58 -05:00
peer . Email = "autodetected@example.com"
peer . UpdatedAt = time . Now ( )
peer . CreatedAt = time . Now ( )
2021-04-03 17:54:35 -04:00
IPs := make ( [ ] string , len ( wgPeer . AllowedIPs ) ) // use allowed IP's as the peer IP's
2021-02-21 17:23:58 -05:00
for i , ip := range wgPeer . AllowedIPs {
2021-04-03 13:11:05 -04:00
IPs [ i ] = ip . String ( )
2020-11-05 13:37:51 -05:00
}
2021-04-03 13:11:05 -04:00
peer . SetIPAddresses ( IPs ... )
2021-03-21 07:36:11 -04:00
peer . DeviceName = device
2020-11-05 13:37:51 -05:00
2021-03-21 07:36:11 -04:00
res := m . db . Create ( & peer )
2020-11-05 13:37:51 -05:00
if res . Error != nil {
2021-02-24 15:24:45 -05:00
return errors . Wrapf ( res . Error , "failed to create autodetected peer %s" , peer . PublicKey )
2020-11-05 13:37:51 -05:00
}
}
2021-04-05 18:07:05 -04:00
if peer . DeviceName == "" {
peer . DeviceName = device
res := m . db . Save ( & peer )
if res . Error != nil {
return errors . Wrapf ( res . Error , "failed to update autodetected peer %s" , peer . PublicKey )
}
}
2020-11-07 04:31:48 -05:00
return nil
}
2021-03-21 07:36:11 -04:00
// validateOrCreateDevice checks if the given WireGuard device already exists in the database, if not, the peer entry will be created
func ( m * PeerManager ) validateOrCreateDevice ( dev wgtypes . Device , ipAddresses [ ] string , mtu int ) error {
2020-11-07 04:31:48 -05:00
device := Device { }
2021-03-21 07:36:11 -04:00
m . db . Where ( "device_name = ?" , dev . Name ) . FirstOrInit ( & device )
2020-11-07 04:31:48 -05:00
if device . PublicKey == "" { // device not found, create
2021-04-03 17:54:35 -04:00
device . Type = DeviceTypeServer // imported device, we assume that server mode is used
2020-11-07 04:31:48 -05:00
device . PublicKey = dev . PublicKey . String ( )
device . PrivateKey = dev . PrivateKey . String ( )
device . DeviceName = dev . Name
device . ListenPort = dev . ListenPort
2021-04-03 17:54:35 -04:00
device . FirewallMark = int32 ( dev . FirewallMark )
2020-11-07 04:31:48 -05:00
device . Mtu = 0
2021-04-02 17:48:30 -04:00
device . DefaultPersistentKeepalive = 16 // Default
2020-12-18 15:54:57 -05:00
device . IPsStr = strings . Join ( ipAddresses , ", " )
2021-03-21 07:36:11 -04:00
if mtu == DefaultMTU {
2020-12-18 16:07:55 -05:00
mtu = 0
}
2020-12-18 15:54:57 -05:00
device . Mtu = mtu
2020-11-07 04:31:48 -05:00
2021-03-21 07:36:11 -04:00
res := m . db . Create ( & device )
2020-11-07 04:31:48 -05:00
if res . Error != nil {
2021-02-24 15:24:45 -05:00
return errors . Wrapf ( res . Error , "failed to create autodetected device" )
2020-11-07 04:31:48 -05:00
}
}
2021-04-05 18:07:05 -04:00
if device . Type == "" {
device . Type = DeviceTypeServer // from version <= 1.0.3, only server mode devices were supported
res := m . db . Save ( & device )
if res . Error != nil {
return errors . Wrapf ( res . Error , "failed to update autodetected device" )
}
}
2020-11-07 04:31:48 -05:00
return nil
}
2021-03-21 07:36:11 -04:00
// populatePeerData enriches the peer struct with WireGuard live data like last handshake, ...
func ( m * PeerManager ) populatePeerData ( peer * Peer ) {
2020-11-07 04:31:48 -05:00
// Set config file
2021-03-21 07:36:11 -04:00
tmpCfg , _ := peer . GetConfigFile ( m . GetDevice ( peer . DeviceName ) )
2021-02-24 15:24:45 -05:00
peer . Config = string ( tmpCfg )
2020-11-06 06:21:47 -05:00
2020-11-07 04:31:48 -05:00
// set data from WireGuard interface
2021-03-21 07:36:11 -04:00
peer . Peer , _ = m . wg . GetPeer ( peer . DeviceName , peer . PublicKey )
2021-02-24 15:24:45 -05:00
peer . LastHandshake = "never"
peer . LastHandshakeTime = "Never connected, or user is disabled."
if peer . Peer != nil {
since := time . Since ( peer . Peer . LastHandshakeTime )
2020-11-09 14:26:34 -05:00
sinceSeconds := int ( since . Round ( time . Second ) . Seconds ( ) )
2021-03-21 07:36:11 -04:00
sinceMinutes := sinceSeconds / 60
2020-11-09 14:26:34 -05:00
sinceSeconds -= sinceMinutes * 60
if sinceMinutes > 2 * 10080 { // 2 weeks
2021-02-24 15:24:45 -05:00
peer . LastHandshake = "a while ago"
2020-11-09 14:26:34 -05:00
} else if sinceMinutes > 10080 { // 1 week
2021-02-24 15:24:45 -05:00
peer . LastHandshake = "a week ago"
2020-11-09 14:26:34 -05:00
} else {
2021-02-24 15:24:45 -05:00
peer . LastHandshake = fmt . Sprintf ( "%02dm %02ds" , sinceMinutes , sinceSeconds )
2020-11-09 14:26:34 -05:00
}
2021-02-24 15:24:45 -05:00
peer . LastHandshakeTime = peer . Peer . LastHandshakeTime . Format ( time . UnixDate )
2020-11-09 14:26:34 -05:00
}
2021-02-24 15:24:45 -05:00
peer . IsOnline = false
2020-11-07 04:31:48 -05:00
}
2021-04-05 12:38:38 -04:00
// fixPeerDefaultData tries to fill all required fields for the given peer
2021-04-05 14:00:11 -04:00
// also tries to migrate data if the database schema changed
2021-04-05 12:38:38 -04:00
func ( m * PeerManager ) fixPeerDefaultData ( peer * Peer , device * Device ) error {
updatePeer := false
switch device . Type {
case DeviceTypeServer :
if peer . Endpoint == "" {
peer . Endpoint = device . DefaultEndpoint
updatePeer = true
}
case DeviceTypeClient :
}
if updatePeer {
return m . UpdatePeer ( * peer )
}
return nil
}
2021-03-21 07:36:11 -04:00
// populateDeviceData enriches the device struct with WireGuard live data like interface information
func ( m * PeerManager ) populateDeviceData ( device * Device ) {
2020-11-07 04:31:48 -05:00
// set data from WireGuard interface
2021-03-21 07:36:11 -04:00
device . Interface , _ = m . wg . GetDeviceInfo ( device . DeviceName )
2020-11-07 04:31:48 -05:00
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetAllPeers ( device string ) [ ] Peer {
2021-02-21 17:23:58 -05:00
peers := make ( [ ] Peer , 0 )
2021-03-21 07:36:11 -04:00
m . db . Where ( "device_name = ?" , device ) . Find ( & peers )
2020-11-07 04:31:48 -05:00
2021-02-21 17:23:58 -05:00
for i := range peers {
2021-03-21 07:36:11 -04:00
m . populatePeerData ( & peers [ i ] )
2020-11-07 04:31:48 -05:00
}
2021-02-21 17:23:58 -05:00
return peers
2020-11-07 04:31:48 -05:00
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetActivePeers ( device string ) [ ] Peer {
2021-02-21 17:23:58 -05:00
peers := make ( [ ] Peer , 0 )
2021-03-21 07:36:11 -04:00
m . db . Where ( "device_name = ? AND deactivated_at IS NULL" , device ) . Find ( & peers )
2020-11-09 14:26:34 -05:00
2021-02-21 17:23:58 -05:00
for i := range peers {
2021-03-21 07:36:11 -04:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 14:26:34 -05:00
}
2021-02-21 17:23:58 -05:00
return peers
2020-11-09 14:26:34 -05:00
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetFilteredAndSortedPeers ( device , sortKey , sortDirection , search string ) [ ] Peer {
2021-02-21 17:23:58 -05:00
peers := make ( [ ] Peer , 0 )
2021-03-21 07:36:11 -04:00
m . db . Where ( "device_name = ?" , device ) . Find ( & peers )
2020-11-09 14:26:34 -05:00
2021-02-21 17:23:58 -05:00
filteredPeers := make ( [ ] Peer , 0 , len ( peers ) )
for i := range peers {
2021-03-21 07:36:11 -04:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 14:26:34 -05:00
if search == "" ||
2021-02-21 17:23:58 -05:00
strings . Contains ( peers [ i ] . Email , search ) ||
strings . Contains ( peers [ i ] . Identifier , search ) ||
strings . Contains ( peers [ i ] . PublicKey , search ) {
filteredPeers = append ( filteredPeers , peers [ i ] )
2020-11-09 14:26:34 -05:00
}
}
2021-04-05 17:18:02 -04:00
sortPeers ( sortKey , sortDirection , filteredPeers )
2020-11-09 14:26:34 -05:00
2021-02-21 17:23:58 -05:00
return filteredPeers
2020-11-09 14:26:34 -05:00
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetSortedPeersForEmail ( sortKey , sortDirection , email string ) [ ] Peer {
2021-02-21 17:23:58 -05:00
peers := make ( [ ] Peer , 0 )
2021-03-21 07:36:11 -04:00
m . db . Where ( "email = ?" , email ) . Find ( & peers )
2020-11-09 17:24:14 -05:00
2021-02-21 17:23:58 -05:00
for i := range peers {
2021-03-21 07:36:11 -04:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 17:24:14 -05:00
}
2021-04-05 17:18:02 -04:00
sortPeers ( sortKey , sortDirection , peers )
return peers
}
func sortPeers ( sortKey string , sortDirection string , peers [ ] Peer ) {
2021-02-21 17:23:58 -05:00
sort . Slice ( peers , func ( i , j int ) bool {
2020-11-09 17:24:14 -05:00
var sortValueLeft string
var sortValueRight string
switch sortKey {
case "id" :
2021-02-21 17:23:58 -05:00
sortValueLeft = peers [ i ] . Identifier
sortValueRight = peers [ j ] . Identifier
2020-11-09 17:24:14 -05:00
case "pubKey" :
2021-02-21 17:23:58 -05:00
sortValueLeft = peers [ i ] . PublicKey
sortValueRight = peers [ j ] . PublicKey
2020-11-09 17:24:14 -05:00
case "mail" :
2021-02-21 17:23:58 -05:00
sortValueLeft = peers [ i ] . Email
sortValueRight = peers [ j ] . Email
2020-11-09 17:24:14 -05:00
case "ip" :
2021-02-21 17:23:58 -05:00
sortValueLeft = peers [ i ] . IPsStr
sortValueRight = peers [ j ] . IPsStr
2021-04-05 17:18:02 -04:00
case "endpoint" :
sortValueLeft = peers [ i ] . Endpoint
sortValueRight = peers [ j ] . Endpoint
2020-11-09 17:24:14 -05:00
case "handshake" :
2021-02-21 17:23:58 -05:00
if peers [ i ] . Peer == nil {
2020-11-09 17:24:14 -05:00
return true
2021-02-21 17:23:58 -05:00
} else if peers [ j ] . Peer == nil {
2020-11-09 17:24:14 -05:00
return false
}
2021-02-21 17:23:58 -05:00
sortValueLeft = peers [ i ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
sortValueRight = peers [ j ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
2020-11-09 17:24:14 -05:00
}
if sortDirection == "asc" {
return sortValueLeft < sortValueRight
} else {
return sortValueLeft > sortValueRight
}
} )
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetDevice ( device string ) Device {
dev := Device { }
2020-11-07 04:31:48 -05:00
2021-03-21 07:36:11 -04:00
m . db . Where ( "device_name = ?" , device ) . First ( & dev )
m . populateDeviceData ( & dev )
2020-11-07 04:31:48 -05:00
2021-03-21 07:36:11 -04:00
return dev
2020-11-06 06:21:47 -05:00
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetPeerByKey ( publicKey string ) Peer {
2021-02-21 17:23:58 -05:00
peer := Peer { }
2021-03-21 07:36:11 -04:00
m . db . Where ( "public_key = ?" , publicKey ) . FirstOrInit ( & peer )
m . populatePeerData ( & peer )
2021-02-21 17:23:58 -05:00
return peer
2020-11-07 04:31:48 -05:00
}
2020-11-06 06:21:47 -05:00
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetPeersByMail ( mail string ) [ ] Peer {
2021-02-21 17:23:58 -05:00
var peers [ ] Peer
2021-03-21 07:36:11 -04:00
m . db . Where ( "email = ?" , mail ) . Find ( & peers )
2021-02-21 17:23:58 -05:00
for i := range peers {
2021-03-21 07:36:11 -04:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 17:24:14 -05:00
}
2020-11-05 13:37:51 -05:00
2021-02-21 17:23:58 -05:00
return peers
2020-11-05 13:37:51 -05:00
}
2021-03-21 07:36:11 -04:00
// ---- Database helpers -----
func ( m * PeerManager ) CreatePeer ( peer Peer ) error {
2021-02-21 17:23:58 -05:00
peer . UID = fmt . Sprintf ( "u%x" , md5 . Sum ( [ ] byte ( peer . PublicKey ) ) )
peer . UpdatedAt = time . Now ( )
peer . CreatedAt = time . Now ( )
2020-11-05 13:37:51 -05:00
2021-03-21 07:36:11 -04:00
res := m . db . Create ( & peer )
2020-11-05 13:37:51 -05:00
if res . Error != nil {
2021-02-21 17:23:58 -05:00
logrus . Errorf ( "failed to create peer: %v" , res . Error )
2021-02-26 16:17:04 -05:00
return errors . Wrap ( res . Error , "failed to create peer" )
2020-11-05 13:37:51 -05:00
}
return nil
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) UpdatePeer ( peer Peer ) error {
2021-02-21 17:23:58 -05:00
peer . UpdatedAt = time . Now ( )
2020-11-05 13:37:51 -05:00
2021-03-21 07:36:11 -04:00
res := m . db . Save ( & peer )
2020-11-05 13:37:51 -05:00
if res . Error != nil {
2021-02-21 17:23:58 -05:00
logrus . Errorf ( "failed to update peer: %v" , res . Error )
2021-02-26 16:17:04 -05:00
return errors . Wrap ( res . Error , "failed to update peer" )
2020-11-05 13:37:51 -05:00
}
return nil
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) DeletePeer ( peer Peer ) error {
res := m . db . Delete ( & peer )
2020-11-09 05:06:02 -05:00
if res . Error != nil {
2021-02-21 17:23:58 -05:00
logrus . Errorf ( "failed to delete peer: %v" , res . Error )
2021-02-26 16:17:04 -05:00
return errors . Wrap ( res . Error , "failed to delete peer" )
2020-11-09 05:06:02 -05:00
}
return nil
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) UpdateDevice ( device Device ) error {
2020-11-07 04:31:48 -05:00
device . UpdatedAt = time . Now ( )
2021-03-21 07:36:11 -04:00
res := m . db . Save ( & device )
2020-11-07 04:31:48 -05:00
if res . Error != nil {
2021-02-08 16:56:02 -05:00
logrus . Errorf ( "failed to update device: %v" , res . Error )
2021-02-26 16:17:04 -05:00
return errors . Wrap ( res . Error , "failed to update device" )
2020-11-07 04:31:48 -05:00
}
return nil
}
2021-03-21 07:36:11 -04:00
// ---- IP helpers ----
func ( m * PeerManager ) GetAllReservedIps ( device string ) ( [ ] string , error ) {
2020-11-05 13:37:51 -05:00
reservedIps := make ( [ ] string , 0 )
2021-03-21 07:36:11 -04:00
peers := m . GetAllPeers ( device )
2021-02-24 15:24:45 -05:00
for _ , user := range peers {
2021-04-03 13:11:05 -04:00
for _ , cidr := range user . GetIPAddresses ( ) {
2020-11-09 05:06:02 -05:00
if cidr == "" {
continue
}
2020-11-05 13:37:51 -05:00
ip , _ , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 16:17:04 -05:00
return nil , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 13:37:51 -05:00
}
2020-11-09 05:06:02 -05:00
reservedIps = append ( reservedIps , ip . String ( ) )
2020-11-05 13:37:51 -05:00
}
}
2021-03-21 07:36:11 -04:00
dev := m . GetDevice ( device )
2021-04-03 13:11:05 -04:00
for _ , cidr := range dev . GetIPAddresses ( ) {
2020-11-09 05:06:02 -05:00
if cidr == "" {
continue
}
2020-11-06 06:21:47 -05:00
ip , _ , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 16:17:04 -05:00
return nil , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 13:37:51 -05:00
}
2020-11-09 05:06:02 -05:00
reservedIps = append ( reservedIps , ip . String ( ) )
2020-11-05 13:37:51 -05:00
}
return reservedIps , nil
}
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) IsIPReserved ( device string , cidr string ) bool {
reserved , err := m . GetAllReservedIps ( device )
2020-11-09 05:06:02 -05:00
if err != nil {
return true // in case something failed, assume the ip is reserved
}
ip , ipnet , err := net . ParseCIDR ( cidr )
if err != nil {
return true
}
// this two addresses are not usable
broadcastAddr := common . BroadcastAddr ( ipnet ) . String ( )
networkAddr := ipnet . IP . String ( )
address := ip . String ( )
if address == broadcastAddr || address == networkAddr {
return true
}
for _ , r := range reserved {
if address == r {
return true
}
}
return false
}
2020-11-05 13:37:51 -05:00
// GetAvailableIp search for an available ip in cidr against a list of reserved ips
2021-03-21 07:36:11 -04:00
func ( m * PeerManager ) GetAvailableIp ( device string , cidr string ) ( string , error ) {
reserved , err := m . GetAllReservedIps ( device )
2020-11-09 05:06:02 -05:00
if err != nil {
2021-03-21 07:36:11 -04:00
return "" , errors . WithMessagef ( err , "failed to get all reserved IP addresses for %s" , device )
2020-11-09 05:06:02 -05:00
}
2020-11-05 13:37:51 -05:00
ip , ipnet , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 16:17:04 -05:00
return "" , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 13:37:51 -05:00
}
// this two addresses are not usable
broadcastAddr := common . BroadcastAddr ( ipnet ) . String ( )
networkAddr := ipnet . IP . String ( )
for ip := ip . Mask ( ipnet . Mask ) ; ipnet . Contains ( ip ) ; common . IncreaseIP ( ip ) {
ok := true
address := ip . String ( )
for _ , r := range reserved {
if address == r {
ok = false
break
}
}
if ok && address != networkAddr && address != broadcastAddr {
2020-11-09 05:06:02 -05:00
netMask := "/32"
if common . IsIPv6 ( address ) {
netMask = "/128"
}
return address + netMask , nil
2020-11-05 13:37:51 -05:00
}
}
return "" , errors . New ( "no more available address from cidr" )
}