2021-03-21 12:36:11 +01:00
package wireguard
2020-11-05 19:37:51 +01:00
2021-04-02 23:48:30 +02:00
// WireGuard documentation: https://manpages.debian.org/unstable/wireguard-tools/wg.8.en.html
2020-11-05 19:37:51 +01:00
import (
2020-11-06 12:21:47 +01:00
"bytes"
2020-11-05 19:37:51 +01:00
"crypto/md5"
"fmt"
"net"
2020-11-09 11:06:02 +01:00
"reflect"
2020-11-09 20:26:34 +01:00
"regexp"
"sort"
2020-11-05 19:37:51 +01:00
"strings"
2020-11-06 12:21:47 +01:00
"text/template"
2020-11-05 19:37:51 +01:00
"time"
2020-11-07 18:36:23 +01:00
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
2020-11-05 19:37:51 +01:00
"github.com/h44z/wg-portal/internal/common"
2021-02-24 21:24:45 +01:00
"github.com/pkg/errors"
2021-02-08 22:56:02 +01:00
"github.com/sirupsen/logrus"
2020-11-06 12:21:47 +01:00
"github.com/skip2/go-qrcode"
2020-11-05 19:37:51 +01:00
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"gorm.io/gorm"
)
2020-11-07 18:36:23 +01:00
//
// CUSTOM VALIDATORS ----------------------------------------------------------------------------
//
var cidrList validator . Func = func ( fl validator . FieldLevel ) bool {
cidrListStr := fl . Field ( ) . String ( )
2020-11-08 10:26:18 +01:00
cidrList := common . ParseStringList ( cidrListStr )
2020-11-07 18:36:23 +01: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 10:26:18 +01:00
ipList := common . ParseStringList ( ipListStr )
2020-11-07 18:36:23 +01: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 22:56:02 +01:00
_ = v . RegisterValidation ( "cidrlist" , cidrList )
_ = v . RegisterValidation ( "iplist" , ipList )
2020-11-07 18:36:23 +01:00
}
}
2020-11-07 10:31:48 +01:00
//
2021-02-21 23:23:58 +01:00
// PEER ----------------------------------------------------------------------------------------
2020-11-07 10:31:48 +01:00
//
2021-02-21 23:23:58 +01:00
type Peer struct {
2021-04-02 23:48:30 +02:00
Peer * wgtypes . Peer ` gorm:"-" ` // WireGuard peer
Device * Device ` gorm:"foreignKey:DeviceName" ` // linked WireGuard device
2021-02-24 21:24:45 +01:00
Config string ` gorm:"-" `
2020-11-05 19:37:51 +01:00
2021-04-02 23:48:30 +02:00
UID string ` form:"uid" binding:"alphanum" ` // uid for html identification
IsOnline bool ` gorm:"-" `
IsNew bool ` gorm:"-" `
Identifier string ` form:"identifier" binding:"required,lt=64" ` // Identifier AND Email make a WireGuard peer unique
Email string ` gorm:"index" form:"mail" binding:"required,email" `
LastHandshake string ` gorm:"-" `
LastHandshakeTime string ` gorm:"-" `
IgnoreGlobalSettings bool ` form:"ignoreglobalsettings" `
DeviceName string ` gorm:"index" `
// Core WireGuard Settings
PublicKey string ` gorm:"primaryKey" form:"pubkey" binding:"required,base64" `
PresharedKey string ` form:"presharedkey" binding:"omitempty,base64" `
AllowedIPsStr string ` form:"allowedip" binding:"cidrlist" `
AllowedIPs [ ] string ` gorm:"-" ` // IPs that are used in the client config file
Endpoint string ` form:"endpoint" binding:"hostname_port" `
PersistentKeepalive int ` form:"keepalive" binding:"gte=0" `
// Misc. WireGuard Settings
PrivateKey string ` form:"privkey" binding:"omitempty,base64" `
IPsStr string ` form:"ip" binding:"cidrlist" `
IPs [ ] string ` gorm:"-" ` // The IPs of the client
DNSStr string ` form:"dns" binding:"iplist" ` // comma separated list of:
DNS [ ] string ` gorm:"-" ` // the DNS servers for the client
2020-11-05 19:37:51 +01:00
DeactivatedAt * time . Time
CreatedBy string
UpdatedBy string
CreatedAt time . Time
UpdatedAt time . Time
}
2021-02-21 23:23:58 +01:00
func ( p Peer ) GetConfig ( ) wgtypes . PeerConfig {
publicKey , _ := wgtypes . ParseKey ( p . PublicKey )
2020-11-05 19:37:51 +01:00
var presharedKey * wgtypes . Key
2021-02-21 23:23:58 +01:00
if p . PresharedKey != "" {
presharedKeyTmp , _ := wgtypes . ParseKey ( p . PresharedKey )
2020-11-05 19:37:51 +01:00
presharedKey = & presharedKeyTmp
}
cfg := wgtypes . PeerConfig {
PublicKey : publicKey ,
Remove : false ,
UpdateOnly : false ,
PresharedKey : presharedKey ,
Endpoint : nil ,
PersistentKeepaliveInterval : nil ,
ReplaceAllowedIPs : true ,
2021-02-21 23:23:58 +01:00
AllowedIPs : make ( [ ] net . IPNet , len ( p . IPs ) ) ,
2020-11-05 19:37:51 +01:00
}
2021-02-21 23:23:58 +01:00
for i , ip := range p . IPs {
2020-11-05 19:37:51 +01:00
_ , ipNet , err := net . ParseCIDR ( ip )
if err == nil {
cfg . AllowedIPs [ i ] = * ipNet
}
}
return cfg
}
2021-02-21 23:23:58 +01:00
func ( p Peer ) GetConfigFile ( device Device ) ( [ ] byte , error ) {
2021-03-21 12:36:11 +01:00
tpl , err := template . New ( "client" ) . Funcs ( template . FuncMap { "StringsJoin" : strings . Join } ) . Parse ( ClientCfgTpl )
2021-02-21 23:23:58 +01:00
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to parse client template" )
2021-02-21 23:23:58 +01:00
}
var tplBuff bytes . Buffer
err = tpl . Execute ( & tplBuff , struct {
Client Peer
Server Device
} {
Client : p ,
Server : device ,
} )
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to execute client template" )
2021-02-21 23:23:58 +01:00
}
return tplBuff . Bytes ( ) , nil
}
func ( p Peer ) GetQRCode ( ) ( [ ] byte , error ) {
png , err := qrcode . Encode ( p . Config , qrcode . Medium , 250 )
2020-11-06 12:21:47 +01:00
if err != nil {
2021-02-08 22:56:02 +01:00
logrus . WithFields ( logrus . Fields {
2020-11-06 12:21:47 +01:00
"err" : err ,
} ) . Error ( "failed to create qrcode" )
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to encode qrcode" )
2020-11-06 12:21:47 +01:00
}
return png , nil
}
2021-02-21 23:23:58 +01:00
func ( p Peer ) IsValid ( ) bool {
if p . PublicKey == "" {
2020-11-07 10:31:48 +01:00
return false
}
return true
}
2021-02-21 23:23:58 +01:00
func ( p Peer ) ToMap ( ) map [ string ] string {
2020-11-09 11:06:02 +01:00
out := make ( map [ string ] string )
2021-02-21 23:23:58 +01:00
v := reflect . ValueOf ( p )
2020-11-09 11:06:02 +01:00
if v . Kind ( ) == reflect . Ptr {
v = v . Elem ( )
}
typ := v . Type ( )
for i := 0 ; i < v . NumField ( ) ; i ++ {
// gets us a StructField
fi := typ . Field ( i )
if tagv := fi . Tag . Get ( "form" ) ; tagv != "" {
// set key of map to value in struct field
out [ tagv ] = v . Field ( i ) . String ( )
}
}
return out
}
2021-02-21 23:23:58 +01:00
func ( p Peer ) GetConfigFileName ( ) string {
2020-11-09 20:26:34 +01:00
reg := regexp . MustCompile ( "[^a-zA-Z0-9_-]+" )
2021-02-21 23:23:58 +01:00
return reg . ReplaceAllString ( strings . ReplaceAll ( p . Identifier , " " , "-" ) , "" ) + ".conf"
2020-11-09 20:26:34 +01:00
}
2020-11-07 10:31:48 +01:00
//
// DEVICE --------------------------------------------------------------------------------------
//
2021-04-02 23:48:30 +02:00
type DeviceType string
const (
DeviceTypeServer DeviceType = "server"
DeviceTypeClient DeviceType = "client"
DeviceTypeCustom DeviceType = "custom"
)
2020-11-05 19:37:51 +01:00
type Device struct {
2020-11-06 12:21:47 +01:00
Interface * wgtypes . Device ` gorm:"-" `
2021-04-02 23:48:30 +02:00
Type DeviceType ` form:"devicetype" `
DeviceName string ` form:"device" gorm:"primaryKey" binding:"required,alphanum" `
// Core WireGuard Settings (Interface section)
PrivateKey string ` form:"privkey" binding:"required,base64" `
ListenPort int ` form:"port" binding:"required,gt=0" `
FirewallMark int32 ` form:"firewallmark" `
// Misc. WireGuard Settings
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:
IPs [ ] string ` gorm:"-" ` // the IPs of the client, wg-quick addition
DNSStr string ` form:"dns" binding:"iplist" ` // comma separated list of:
DNS [ ] string ` gorm:"-" ` // 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
// Settings that are applied to all peer by default
DefaultEndpoint string ` form:"endpoint" binding:"required,hostname_port" `
DefaultAllowedIPsStr string ` form:"allowedip" binding:"cidrlist" `
DefaultAllowedIPs [ ] string ` gorm:"-" ` // IPs that are used in the client config file
DefaultPersistentKeepalive int ` form:"keepalive" binding:"gte=0" `
CreatedAt time . Time
UpdatedAt time . Time
2020-11-05 19:37:51 +01:00
}
2020-11-06 12:21:47 +01:00
func ( d Device ) IsValid ( ) bool {
2020-11-07 10:31:48 +01:00
if d . PublicKey == "" {
return false
}
2020-11-05 19:37:51 +01:00
if len ( d . IPs ) == 0 {
return false
}
2021-04-02 23:48:30 +02:00
if d . DefaultEndpoint == "" {
2020-11-05 19:37:51 +01:00
return false
}
return true
}
2021-02-21 23:23:58 +01:00
func ( d Device ) GetConfig ( ) wgtypes . Config {
2020-11-07 10:31:48 +01:00
var privateKey * wgtypes . Key
if d . PrivateKey != "" {
pKey , _ := wgtypes . ParseKey ( d . PrivateKey )
privateKey = & pKey
}
cfg := wgtypes . Config {
PrivateKey : privateKey ,
ListenPort : & d . ListenPort ,
}
return cfg
}
2021-02-24 21:24:45 +01:00
func ( d Device ) GetConfigFile ( peers [ ] Peer ) ( [ ] byte , error ) {
2021-03-21 12:36:11 +01:00
tpl , err := template . New ( "server" ) . Funcs ( template . FuncMap { "StringsJoin" : strings . Join } ) . Parse ( DeviceCfgTpl )
2020-11-09 20:26:34 +01:00
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to parse server template" )
2020-11-09 20:26:34 +01:00
}
var tplBuff bytes . Buffer
err = tpl . Execute ( & tplBuff , struct {
2021-02-21 23:23:58 +01:00
Clients [ ] Peer
2020-11-09 20:26:34 +01:00
Server Device
} {
2021-02-24 21:24:45 +01:00
Clients : peers ,
2020-11-09 20:26:34 +01:00
Server : d ,
} )
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to execute server template" )
2020-11-09 20:26:34 +01:00
}
return tplBuff . Bytes ( ) , nil
}
2020-11-07 10:31:48 +01:00
//
2021-02-24 21:24:45 +01:00
// PEER-MANAGER --------------------------------------------------------------------------------
2020-11-07 10:31:48 +01:00
//
2021-02-24 21:24:45 +01:00
type PeerManager struct {
2021-03-21 12:36:11 +01:00
db * gorm . DB
wg * Manager
2020-11-05 19:37:51 +01:00
}
2021-03-21 12:36:11 +01:00
func NewPeerManager ( db * gorm . DB , wg * Manager ) ( * PeerManager , error ) {
2021-03-22 13:00:02 +01:00
pm := & PeerManager { db : db , wg : wg }
2020-11-05 19:37:51 +01:00
2021-03-22 13:00:02 +01:00
if err := pm . db . AutoMigrate ( & Peer { } , & Device { } ) ; err != nil {
2021-02-24 21:24:45 +01:00
return nil , errors . WithMessage ( err , "failed to migrate peer database" )
2020-11-05 19:37:51 +01:00
}
2021-03-22 13:00:02 +01: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 ] )
}
}
return pm , nil
2020-11-05 19:37:51 +01:00
}
2021-03-21 12:36:11 +01:00
// InitFromPhysicalInterface read all WireGuard peers from the WireGuard interface configuration. If a peer does not
// exist in the local database, it gets created.
func ( m * PeerManager ) InitFromPhysicalInterface ( ) error {
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 21:54:57 +01:00
}
2021-03-21 12:36:11 +01: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 21:54:57 +01:00
}
2020-11-05 19:37:51 +01:00
2021-03-21 12:36:11 +01:00
// Check if entries already exist in database, if not create them
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 )
}
}
if err := m . validateOrCreateDevice ( * device , ipAddresses , mtu ) ; err != nil {
return errors . WithMessagef ( err , "failed to validate device %s" , device . Name )
2020-11-07 10:31:48 +01:00
}
2020-11-05 19:37:51 +01:00
}
2020-11-07 10:31:48 +01:00
return nil
2020-11-05 19:37:51 +01:00
}
2021-03-21 12:36:11 +01:00
// validateOrCreatePeer checks if the given WireGuard peer already exists in the database, if not, the peer entry will be created
func ( m * PeerManager ) validateOrCreatePeer ( device string , wgPeer wgtypes . Peer ) error {
2021-02-21 23:23:58 +01:00
peer := Peer { }
2021-03-21 12:36:11 +01:00
m . db . Where ( "public_key = ?" , wgPeer . PublicKey . String ( ) ) . FirstOrInit ( & peer )
2020-11-05 19:37:51 +01:00
2021-02-21 23:23:58 +01:00
if peer . PublicKey == "" { // peer not found, create
peer . UID = fmt . Sprintf ( "u%x" , md5 . Sum ( [ ] byte ( wgPeer . PublicKey . String ( ) ) ) )
peer . PublicKey = wgPeer . PublicKey . String ( )
peer . PrivateKey = "" // UNKNOWN
if wgPeer . PresharedKey != ( wgtypes . Key { } ) {
peer . PresharedKey = wgPeer . PresharedKey . String ( )
2020-11-05 19:37:51 +01:00
}
2021-02-21 23:23:58 +01:00
peer . Email = "autodetected@example.com"
peer . Identifier = "Autodetected (" + peer . PublicKey [ 0 : 8 ] + ")"
peer . UpdatedAt = time . Now ( )
peer . CreatedAt = time . Now ( )
peer . AllowedIPs = make ( [ ] string , 0 ) // UNKNOWN
peer . IPs = make ( [ ] string , len ( wgPeer . AllowedIPs ) )
for i , ip := range wgPeer . AllowedIPs {
peer . IPs [ i ] = ip . String ( )
2020-11-05 19:37:51 +01:00
}
2021-02-21 23:23:58 +01:00
peer . AllowedIPsStr = strings . Join ( peer . AllowedIPs , ", " )
peer . IPsStr = strings . Join ( peer . IPs , ", " )
2021-03-21 12:36:11 +01:00
peer . DeviceName = device
2020-11-05 19:37:51 +01:00
2021-03-21 12:36:11 +01:00
res := m . db . Create ( & peer )
2020-11-05 19:37:51 +01:00
if res . Error != nil {
2021-02-24 21:24:45 +01:00
return errors . Wrapf ( res . Error , "failed to create autodetected peer %s" , peer . PublicKey )
2020-11-05 19:37:51 +01:00
}
}
2020-11-07 10:31:48 +01:00
return nil
}
2021-03-21 12:36:11 +01: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 10:31:48 +01:00
device := Device { }
2021-03-21 12:36:11 +01:00
m . db . Where ( "device_name = ?" , dev . Name ) . FirstOrInit ( & device )
2020-11-07 10:31:48 +01:00
if device . PublicKey == "" { // device not found, create
2021-04-02 23:48:30 +02:00
device . Type = DeviceTypeCustom // imported device, we do not (easily) know if it is a client or server
2020-11-07 10:31:48 +01:00
device . PublicKey = dev . PublicKey . String ( )
device . PrivateKey = dev . PrivateKey . String ( )
device . DeviceName = dev . Name
device . ListenPort = dev . ListenPort
device . Mtu = 0
2021-04-02 23:48:30 +02:00
device . DefaultPersistentKeepalive = 16 // Default
2020-12-18 21:54:57 +01:00
device . IPsStr = strings . Join ( ipAddresses , ", " )
2021-03-21 12:36:11 +01:00
if mtu == DefaultMTU {
2020-12-18 22:07:55 +01:00
mtu = 0
}
2020-12-18 21:54:57 +01:00
device . Mtu = mtu
2020-11-07 10:31:48 +01:00
2021-03-21 12:36:11 +01:00
res := m . db . Create ( & device )
2020-11-07 10:31:48 +01:00
if res . Error != nil {
2021-02-24 21:24:45 +01:00
return errors . Wrapf ( res . Error , "failed to create autodetected device" )
2020-11-07 10:31:48 +01:00
}
}
return nil
}
2021-03-21 12:36:11 +01:00
// populatePeerData enriches the peer struct with WireGuard live data like last handshake, ...
func ( m * PeerManager ) populatePeerData ( peer * Peer ) {
2021-02-24 21:24:45 +01:00
peer . AllowedIPs = strings . Split ( peer . AllowedIPsStr , ", " )
peer . IPs = strings . Split ( peer . IPsStr , ", " )
2020-11-07 10:31:48 +01:00
// Set config file
2021-03-21 12:36:11 +01:00
tmpCfg , _ := peer . GetConfigFile ( m . GetDevice ( peer . DeviceName ) )
2021-02-24 21:24:45 +01:00
peer . Config = string ( tmpCfg )
2020-11-06 12:21:47 +01:00
2020-11-07 10:31:48 +01:00
// set data from WireGuard interface
2021-03-21 12:36:11 +01:00
peer . Peer , _ = m . wg . GetPeer ( peer . DeviceName , peer . PublicKey )
2021-02-24 21:24:45 +01: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 20:26:34 +01:00
sinceSeconds := int ( since . Round ( time . Second ) . Seconds ( ) )
2021-03-21 12:36:11 +01:00
sinceMinutes := sinceSeconds / 60
2020-11-09 20:26:34 +01:00
sinceSeconds -= sinceMinutes * 60
if sinceMinutes > 2 * 10080 { // 2 weeks
2021-02-24 21:24:45 +01:00
peer . LastHandshake = "a while ago"
2020-11-09 20:26:34 +01:00
} else if sinceMinutes > 10080 { // 1 week
2021-02-24 21:24:45 +01:00
peer . LastHandshake = "a week ago"
2020-11-09 20:26:34 +01:00
} else {
2021-02-24 21:24:45 +01:00
peer . LastHandshake = fmt . Sprintf ( "%02dm %02ds" , sinceMinutes , sinceSeconds )
2020-11-09 20:26:34 +01:00
}
2021-02-24 21:24:45 +01:00
peer . LastHandshakeTime = peer . Peer . LastHandshakeTime . Format ( time . UnixDate )
2020-11-09 20:26:34 +01:00
}
2021-02-24 21:24:45 +01:00
peer . IsOnline = false
2020-11-07 10:31:48 +01:00
}
2021-03-21 12:36:11 +01:00
// populateDeviceData enriches the device struct with WireGuard live data like interface information
func ( m * PeerManager ) populateDeviceData ( device * Device ) {
2021-04-02 23:48:30 +02:00
device . DefaultAllowedIPs = strings . Split ( device . DefaultAllowedIPsStr , ", " )
2020-11-07 10:31:48 +01:00
device . IPs = strings . Split ( device . IPsStr , ", " )
device . DNS = strings . Split ( device . DNSStr , ", " )
// set data from WireGuard interface
2021-03-21 12:36:11 +01:00
device . Interface , _ = m . wg . GetDeviceInfo ( device . DeviceName )
2020-11-07 10:31:48 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetAllPeers ( device string ) [ ] Peer {
2021-02-21 23:23:58 +01:00
peers := make ( [ ] Peer , 0 )
2021-03-21 12:36:11 +01:00
m . db . Where ( "device_name = ?" , device ) . Find ( & peers )
2020-11-07 10:31:48 +01:00
2021-02-21 23:23:58 +01:00
for i := range peers {
2021-03-21 12:36:11 +01:00
m . populatePeerData ( & peers [ i ] )
2020-11-07 10:31:48 +01:00
}
2021-02-21 23:23:58 +01:00
return peers
2020-11-07 10:31:48 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetActivePeers ( device string ) [ ] Peer {
2021-02-21 23:23:58 +01:00
peers := make ( [ ] Peer , 0 )
2021-03-21 12:36:11 +01:00
m . db . Where ( "device_name = ? AND deactivated_at IS NULL" , device ) . Find ( & peers )
2020-11-09 20:26:34 +01:00
2021-02-21 23:23:58 +01:00
for i := range peers {
2021-03-21 12:36:11 +01:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 20:26:34 +01:00
}
2021-02-21 23:23:58 +01:00
return peers
2020-11-09 20:26:34 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetFilteredAndSortedPeers ( device , sortKey , sortDirection , search string ) [ ] Peer {
2021-02-21 23:23:58 +01:00
peers := make ( [ ] Peer , 0 )
2021-03-21 12:36:11 +01:00
m . db . Where ( "device_name = ?" , device ) . Find ( & peers )
2020-11-09 20:26:34 +01:00
2021-02-21 23:23:58 +01:00
filteredPeers := make ( [ ] Peer , 0 , len ( peers ) )
for i := range peers {
2021-03-21 12:36:11 +01:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 20:26:34 +01:00
if search == "" ||
2021-02-21 23:23:58 +01: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 20:26:34 +01:00
}
}
2021-02-21 23:23:58 +01:00
sort . Slice ( filteredPeers , func ( i , j int ) bool {
2020-11-09 20:26:34 +01:00
var sortValueLeft string
var sortValueRight string
switch sortKey {
case "id" :
2021-02-21 23:23:58 +01:00
sortValueLeft = filteredPeers [ i ] . Identifier
sortValueRight = filteredPeers [ j ] . Identifier
2020-11-09 20:26:34 +01:00
case "pubKey" :
2021-02-21 23:23:58 +01:00
sortValueLeft = filteredPeers [ i ] . PublicKey
sortValueRight = filteredPeers [ j ] . PublicKey
2020-11-09 20:26:34 +01:00
case "mail" :
2021-02-21 23:23:58 +01:00
sortValueLeft = filteredPeers [ i ] . Email
sortValueRight = filteredPeers [ j ] . Email
2020-11-09 20:26:34 +01:00
case "ip" :
2021-02-21 23:23:58 +01:00
sortValueLeft = filteredPeers [ i ] . IPsStr
sortValueRight = filteredPeers [ j ] . IPsStr
2020-11-09 20:26:34 +01:00
case "handshake" :
2021-02-21 23:23:58 +01:00
if filteredPeers [ i ] . Peer == nil {
2020-11-09 20:26:34 +01:00
return false
2021-02-21 23:23:58 +01:00
} else if filteredPeers [ j ] . Peer == nil {
2020-11-09 23:24:14 +01:00
return true
2020-11-09 20:26:34 +01:00
}
2021-02-21 23:23:58 +01:00
sortValueLeft = filteredPeers [ i ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
sortValueRight = filteredPeers [ j ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
2020-11-09 20:26:34 +01:00
}
if sortDirection == "asc" {
return sortValueLeft < sortValueRight
} else {
return sortValueLeft > sortValueRight
}
} )
2021-02-21 23:23:58 +01:00
return filteredPeers
2020-11-09 20:26:34 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetSortedPeersForEmail ( sortKey , sortDirection , email string ) [ ] Peer {
2021-02-21 23:23:58 +01:00
peers := make ( [ ] Peer , 0 )
2021-03-21 12:36:11 +01:00
m . db . Where ( "email = ?" , email ) . Find ( & peers )
2020-11-09 23:24:14 +01:00
2021-02-21 23:23:58 +01:00
for i := range peers {
2021-03-21 12:36:11 +01:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 23:24:14 +01:00
}
2021-02-21 23:23:58 +01:00
sort . Slice ( peers , func ( i , j int ) bool {
2020-11-09 23:24:14 +01:00
var sortValueLeft string
var sortValueRight string
switch sortKey {
case "id" :
2021-02-21 23:23:58 +01:00
sortValueLeft = peers [ i ] . Identifier
sortValueRight = peers [ j ] . Identifier
2020-11-09 23:24:14 +01:00
case "pubKey" :
2021-02-21 23:23:58 +01:00
sortValueLeft = peers [ i ] . PublicKey
sortValueRight = peers [ j ] . PublicKey
2020-11-09 23:24:14 +01:00
case "mail" :
2021-02-21 23:23:58 +01:00
sortValueLeft = peers [ i ] . Email
sortValueRight = peers [ j ] . Email
2020-11-09 23:24:14 +01:00
case "ip" :
2021-02-21 23:23:58 +01:00
sortValueLeft = peers [ i ] . IPsStr
sortValueRight = peers [ j ] . IPsStr
2020-11-09 23:24:14 +01:00
case "handshake" :
2021-02-21 23:23:58 +01:00
if peers [ i ] . Peer == nil {
2020-11-09 23:24:14 +01:00
return true
2021-02-21 23:23:58 +01:00
} else if peers [ j ] . Peer == nil {
2020-11-09 23:24:14 +01:00
return false
}
2021-02-21 23:23:58 +01:00
sortValueLeft = peers [ i ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
sortValueRight = peers [ j ] . Peer . LastHandshakeTime . Format ( time . RFC3339 )
2020-11-09 23:24:14 +01:00
}
if sortDirection == "asc" {
return sortValueLeft < sortValueRight
} else {
return sortValueLeft > sortValueRight
}
} )
2021-02-21 23:23:58 +01:00
return peers
2020-11-09 23:24:14 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetDevice ( device string ) Device {
dev := Device { }
2020-11-07 10:31:48 +01:00
2021-03-21 12:36:11 +01:00
m . db . Where ( "device_name = ?" , device ) . First ( & dev )
m . populateDeviceData ( & dev )
2020-11-07 10:31:48 +01:00
2021-03-21 12:36:11 +01:00
return dev
2020-11-06 12:21:47 +01:00
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetPeerByKey ( publicKey string ) Peer {
2021-02-21 23:23:58 +01:00
peer := Peer { }
2021-03-21 12:36:11 +01:00
m . db . Where ( "public_key = ?" , publicKey ) . FirstOrInit ( & peer )
m . populatePeerData ( & peer )
2021-02-21 23:23:58 +01:00
return peer
2020-11-07 10:31:48 +01:00
}
2020-11-06 12:21:47 +01:00
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetPeersByMail ( mail string ) [ ] Peer {
2021-02-21 23:23:58 +01:00
var peers [ ] Peer
2021-03-21 12:36:11 +01:00
m . db . Where ( "email = ?" , mail ) . Find ( & peers )
2021-02-21 23:23:58 +01:00
for i := range peers {
2021-03-21 12:36:11 +01:00
m . populatePeerData ( & peers [ i ] )
2020-11-09 23:24:14 +01:00
}
2020-11-05 19:37:51 +01:00
2021-02-21 23:23:58 +01:00
return peers
2020-11-05 19:37:51 +01:00
}
2021-03-21 12:36:11 +01:00
// ---- Database helpers -----
func ( m * PeerManager ) CreatePeer ( peer Peer ) error {
2021-02-21 23:23:58 +01:00
peer . UID = fmt . Sprintf ( "u%x" , md5 . Sum ( [ ] byte ( peer . PublicKey ) ) )
peer . UpdatedAt = time . Now ( )
peer . CreatedAt = time . Now ( )
peer . AllowedIPsStr = strings . Join ( peer . AllowedIPs , ", " )
peer . IPsStr = strings . Join ( peer . IPs , ", " )
2020-11-05 19:37:51 +01:00
2021-03-21 12:36:11 +01:00
res := m . db . Create ( & peer )
2020-11-05 19:37:51 +01:00
if res . Error != nil {
2021-02-21 23:23:58 +01:00
logrus . Errorf ( "failed to create peer: %v" , res . Error )
2021-02-26 22:17:04 +01:00
return errors . Wrap ( res . Error , "failed to create peer" )
2020-11-05 19:37:51 +01:00
}
return nil
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) UpdatePeer ( peer Peer ) error {
2021-02-21 23:23:58 +01:00
peer . UpdatedAt = time . Now ( )
peer . AllowedIPsStr = strings . Join ( peer . AllowedIPs , ", " )
peer . IPsStr = strings . Join ( peer . IPs , ", " )
2020-11-05 19:37:51 +01:00
2021-03-21 12:36:11 +01:00
res := m . db . Save ( & peer )
2020-11-05 19:37:51 +01:00
if res . Error != nil {
2021-02-21 23:23:58 +01:00
logrus . Errorf ( "failed to update peer: %v" , res . Error )
2021-02-26 22:17:04 +01:00
return errors . Wrap ( res . Error , "failed to update peer" )
2020-11-05 19:37:51 +01:00
}
return nil
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) DeletePeer ( peer Peer ) error {
res := m . db . Delete ( & peer )
2020-11-09 11:06:02 +01:00
if res . Error != nil {
2021-02-21 23:23:58 +01:00
logrus . Errorf ( "failed to delete peer: %v" , res . Error )
2021-02-26 22:17:04 +01:00
return errors . Wrap ( res . Error , "failed to delete peer" )
2020-11-09 11:06:02 +01:00
}
return nil
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) UpdateDevice ( device Device ) error {
2020-11-07 10:31:48 +01:00
device . UpdatedAt = time . Now ( )
2021-04-02 23:48:30 +02:00
device . DefaultAllowedIPsStr = strings . Join ( device . DefaultAllowedIPs , ", " )
2020-11-07 10:31:48 +01:00
device . IPsStr = strings . Join ( device . IPs , ", " )
device . DNSStr = strings . Join ( device . DNS , ", " )
2021-03-21 12:36:11 +01:00
res := m . db . Save ( & device )
2020-11-07 10:31:48 +01:00
if res . Error != nil {
2021-02-08 22:56:02 +01:00
logrus . Errorf ( "failed to update device: %v" , res . Error )
2021-02-26 22:17:04 +01:00
return errors . Wrap ( res . Error , "failed to update device" )
2020-11-07 10:31:48 +01:00
}
return nil
}
2021-03-21 12:36:11 +01:00
// ---- IP helpers ----
func ( m * PeerManager ) GetAllReservedIps ( device string ) ( [ ] string , error ) {
2020-11-05 19:37:51 +01:00
reservedIps := make ( [ ] string , 0 )
2021-03-21 12:36:11 +01:00
peers := m . GetAllPeers ( device )
2021-02-24 21:24:45 +01:00
for _ , user := range peers {
2020-11-05 19:37:51 +01:00
for _ , cidr := range user . IPs {
2020-11-09 11:06:02 +01:00
if cidr == "" {
continue
}
2020-11-05 19:37:51 +01:00
ip , _ , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 19:37:51 +01:00
}
2020-11-09 11:06:02 +01:00
reservedIps = append ( reservedIps , ip . String ( ) )
2020-11-05 19:37:51 +01:00
}
}
2021-03-21 12:36:11 +01:00
dev := m . GetDevice ( device )
for _ , cidr := range dev . IPs {
2020-11-09 11:06:02 +01:00
if cidr == "" {
continue
}
2020-11-06 12:21:47 +01:00
ip , _ , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 22:17:04 +01:00
return nil , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 19:37:51 +01:00
}
2020-11-09 11:06:02 +01:00
reservedIps = append ( reservedIps , ip . String ( ) )
2020-11-05 19:37:51 +01:00
}
return reservedIps , nil
}
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) IsIPReserved ( device string , cidr string ) bool {
reserved , err := m . GetAllReservedIps ( device )
2020-11-09 11:06:02 +01: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 19:37:51 +01:00
// GetAvailableIp search for an available ip in cidr against a list of reserved ips
2021-03-21 12:36:11 +01:00
func ( m * PeerManager ) GetAvailableIp ( device string , cidr string ) ( string , error ) {
reserved , err := m . GetAllReservedIps ( device )
2020-11-09 11:06:02 +01:00
if err != nil {
2021-03-21 12:36:11 +01:00
return "" , errors . WithMessagef ( err , "failed to get all reserved IP addresses for %s" , device )
2020-11-09 11:06:02 +01:00
}
2020-11-05 19:37:51 +01:00
ip , ipnet , err := net . ParseCIDR ( cidr )
if err != nil {
2021-02-26 22:17:04 +01:00
return "" , errors . Wrap ( err , "failed to parse cidr" )
2020-11-05 19:37:51 +01: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 11:06:02 +01:00
netMask := "/32"
if common . IsIPv6 ( address ) {
netMask = "/128"
}
return address + netMask , nil
2020-11-05 19:37:51 +01:00
}
}
return "" , errors . New ( "no more available address from cidr" )
}