2023-08-04 11:34:18 +00:00
package domain
import (
"fmt"
"net"
"regexp"
"strings"
"time"
2024-10-15 13:44:47 +00:00
"github.com/h44z/wg-portal/internal"
2023-08-04 11:34:18 +00:00
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type PeerIdentifier string
func ( i PeerIdentifier ) IsPublicKey ( ) bool {
_ , err := wgtypes . ParseKey ( string ( i ) )
if err != nil {
return false
}
return true
}
func ( i PeerIdentifier ) ToPublicKey ( ) wgtypes . Key {
publicKey , _ := wgtypes . ParseKey ( string ( i ) )
return publicKey
}
type Peer struct {
BaseModel
// WireGuard specific (for the [peer] section of the config file)
2024-10-15 13:44:47 +00:00
Endpoint ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:endpoint_" ` // the endpoint address
EndpointPublicKey ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:endpoint_pubkey_" ` // the endpoint public key
AllowedIPsStr ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:allowed_ips_str_" ` // all allowed ip subnets, comma seperated
ExtraAllowedIPsStr string // all allowed ip subnets on the server side, comma seperated
PresharedKey PreSharedKey // the pre-shared Key of the peer
PersistentKeepalive ConfigOption [ int ] ` gorm:"embedded;embeddedPrefix:persistent_keep_alive_" ` // the persistent keep-alive interval
2023-08-04 11:34:18 +00:00
// WG Portal specific
2024-04-02 20:29:10 +00:00
DisplayName string // a nice display name/ description for the peer
Identifier PeerIdentifier ` gorm:"primaryKey;column:identifier" ` // peer unique identifier
UserIdentifier UserIdentifier ` gorm:"index;column:user_identifier" ` // the owner
InterfaceIdentifier InterfaceIdentifier ` gorm:"index;column:interface_identifier" ` // the interface id
Disabled * time . Time ` gorm:"column:disabled" ` // if this field is set, the peer is disabled
DisabledReason string // the reason why the peer has been disabled
ExpiresAt * time . Time ` gorm:"column:expires_at" ` // expiry dates for peers
Notes string ` form:"notes" binding:"omitempty" ` // a note field for peers
AutomaticallyCreated bool ` gorm:"column:auto_created" ` // specifies if the peer was automatically created
2023-08-04 11:34:18 +00:00
// Interface settings for the peer, used to generate the [interface] section in the peer config file
Interface PeerInterfaceConfig ` gorm:"embedded" `
}
func ( p * Peer ) IsDisabled ( ) bool {
return p . Disabled != nil
}
func ( p * Peer ) IsExpired ( ) bool {
if p . ExpiresAt == nil {
return false
}
if p . ExpiresAt . Before ( time . Now ( ) ) {
return true
}
return false
}
func ( p * Peer ) CheckAliveAddress ( ) string {
if p . Interface . CheckAliveAddress != "" {
return p . Interface . CheckAliveAddress
}
if len ( p . Interface . Addresses ) > 0 {
return p . Interface . Addresses [ 0 ] . Addr // take the first peer address
}
return ""
}
func ( p * Peer ) CopyCalculatedAttributes ( src * Peer ) {
p . BaseModel = src . BaseModel
}
func ( p * Peer ) GetConfigFileName ( ) string {
filename := ""
reg := regexp . MustCompile ( "[^a-zA-Z0-9-_]+" )
if p . DisplayName != "" {
filename = p . DisplayName
filename = strings . ReplaceAll ( filename , " " , "_" )
filename = reg . ReplaceAllString ( filename , "" )
filename = internal . TruncateString ( filename , 16 )
filename += ".conf"
} else {
filename = fmt . Sprintf ( "wg_%s" , internal . TruncateString ( string ( p . Identifier ) , 8 ) )
filename = reg . ReplaceAllString ( filename , "" )
filename += ".conf"
}
return filename
}
func ( p * Peer ) ApplyInterfaceDefaults ( in * Interface ) {
p . Endpoint . TrySetValue ( in . PeerDefEndpoint )
p . EndpointPublicKey . TrySetValue ( in . PublicKey )
p . AllowedIPsStr . TrySetValue ( in . PeerDefAllowedIPsStr )
p . PersistentKeepalive . TrySetValue ( in . PeerDefPersistentKeepalive )
p . Interface . DnsStr . TrySetValue ( in . PeerDefDnsStr )
p . Interface . DnsSearchStr . TrySetValue ( in . PeerDefDnsSearchStr )
p . Interface . Mtu . TrySetValue ( in . PeerDefMtu )
p . Interface . FirewallMark . TrySetValue ( in . PeerDefFirewallMark )
p . Interface . RoutingTable . TrySetValue ( in . PeerDefRoutingTable )
p . Interface . PreUp . TrySetValue ( in . PeerDefPreUp )
p . Interface . PostUp . TrySetValue ( in . PeerDefPostUp )
p . Interface . PreDown . TrySetValue ( in . PeerDefPreDown )
p . Interface . PostDown . TrySetValue ( in . PeerDefPostDown )
}
2025-01-11 17:44:55 +00:00
func ( p * Peer ) GenerateDisplayName ( prefix string ) {
if prefix != "" {
prefix = fmt . Sprintf ( "%s " , strings . TrimSpace ( prefix ) ) // add a space after the prefix
}
p . DisplayName = fmt . Sprintf ( "%sPeer %s" , prefix , internal . TruncateString ( string ( p . Identifier ) , 8 ) )
}
2023-08-04 11:34:18 +00:00
type PeerInterfaceConfig struct {
KeyPair // private/public Key of the peer
Type InterfaceType ` gorm:"column:iface_type" ` // the interface type (server, client, any)
2024-10-15 13:44:47 +00:00
Addresses [ ] Cidr ` gorm:"many2many:peer_addresses;" ` // the interface ip addresses
CheckAliveAddress string ` gorm:"column:check_alive_address" ` // optional ip address or DNS name that is used for ping checks
DnsStr ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_dns_str_" ` // the dns server that should be set if the interface is up, comma separated
DnsSearchStr ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_dns_search_str_" ` // the dns search option string that should be set if the interface is up, will be appended to DnsStr
Mtu ConfigOption [ int ] ` gorm:"embedded;embeddedPrefix:iface_mtu_" ` // the device MTU
FirewallMark ConfigOption [ uint32 ] ` gorm:"embedded;embeddedPrefix:iface_firewall_mark_" ` // a firewall mark
RoutingTable ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_routing_table_" ` // the routing table
2023-08-04 11:34:18 +00:00
2024-10-15 13:44:47 +00:00
PreUp ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_pre_up_" ` // action that is executed before the device is up
PostUp ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_post_up_" ` // action that is executed after the device is up
PreDown ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_pre_down_" ` // action that is executed before the device is down
PostDown ConfigOption [ string ] ` gorm:"embedded;embeddedPrefix:iface_post_down_" ` // action that is executed after the device is down
2023-08-04 11:34:18 +00:00
}
func ( p * PeerInterfaceConfig ) AddressStr ( ) string {
return CidrsToString ( p . Addresses )
}
type PhysicalPeer struct {
Identifier PeerIdentifier // peer unique identifier
Endpoint string // the endpoint address
AllowedIPs [ ] Cidr // all allowed ip subnets
KeyPair // private/public Key of the peer, for imports it only contains the public key as the private key is not known to the server
PresharedKey PreSharedKey // the pre-shared Key of the peer
PersistentKeepalive int // the persistent keep-alive interval
LastHandshake time . Time
ProtocolVersion int
BytesUpload uint64 // upload bytes are the number of bytes that the remote peer has sent to the server
BytesDownload uint64 // upload bytes are the number of bytes that the remote peer has received from the server
}
func ( p PhysicalPeer ) GetPresharedKey ( ) * wgtypes . Key {
if p . PresharedKey == "" {
return nil
}
key , err := wgtypes . ParseKey ( string ( p . PresharedKey ) )
if err != nil {
return nil
}
return & key
}
func ( p PhysicalPeer ) GetEndpointAddress ( ) * net . UDPAddr {
if p . Endpoint == "" {
return nil
}
addr , err := net . ResolveUDPAddr ( "udp" , p . Endpoint )
if err != nil {
return nil
}
return addr
}
func ( p PhysicalPeer ) GetPersistentKeepaliveTime ( ) * time . Duration {
if p . PersistentKeepalive == 0 {
return nil
}
keepAliveDuration := time . Duration ( p . PersistentKeepalive ) * time . Second
return & keepAliveDuration
}
func ( p PhysicalPeer ) GetAllowedIPs ( ) [ ] net . IPNet {
allowedIPs := make ( [ ] net . IPNet , len ( p . AllowedIPs ) )
for i , ip := range p . AllowedIPs {
allowedIPs [ i ] = * ip . IpNet ( )
}
return allowedIPs
}
func ConvertPhysicalPeer ( pp * PhysicalPeer ) * Peer {
peer := & Peer {
2024-10-15 13:44:47 +00:00
Endpoint : NewConfigOption ( pp . Endpoint , true ) ,
EndpointPublicKey : NewConfigOption ( "" , true ) ,
AllowedIPsStr : NewConfigOption ( "" , true ) ,
2023-08-04 11:34:18 +00:00
ExtraAllowedIPsStr : "" ,
PresharedKey : pp . PresharedKey ,
2024-10-15 13:44:47 +00:00
PersistentKeepalive : NewConfigOption ( pp . PersistentKeepalive , true ) ,
2023-08-04 11:34:18 +00:00
DisplayName : string ( pp . Identifier ) ,
Identifier : pp . Identifier ,
UserIdentifier : "" ,
InterfaceIdentifier : "" ,
Disabled : nil ,
Interface : PeerInterfaceConfig {
KeyPair : pp . KeyPair ,
} ,
}
return peer
}
func MergeToPhysicalPeer ( pp * PhysicalPeer , p * Peer ) {
pp . Identifier = p . Identifier
pp . Endpoint = p . Endpoint . GetValue ( )
if p . Interface . Type == InterfaceTypeServer {
allowedIPs , _ := CidrsFromString ( p . AllowedIPsStr . GetValue ( ) )
extraAllowedIPs , _ := CidrsFromString ( p . ExtraAllowedIPsStr )
pp . AllowedIPs = append ( allowedIPs , extraAllowedIPs ... )
} else {
2023-10-19 20:53:51 +00:00
allowedIPs := make ( [ ] Cidr , len ( p . Interface . Addresses ) )
for i , ip := range p . Interface . Addresses {
allowedIPs [ i ] = ip . HostAddr ( )
}
2023-08-04 11:34:18 +00:00
extraAllowedIPs , _ := CidrsFromString ( p . ExtraAllowedIPsStr )
pp . AllowedIPs = append ( allowedIPs , extraAllowedIPs ... )
}
pp . PresharedKey = p . PresharedKey
pp . PublicKey = p . Interface . PublicKey
pp . PersistentKeepalive = p . PersistentKeepalive . GetValue ( )
}
type PeerCreationRequest struct {
UserIdentifiers [ ] string
Suffix string
}