github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/wgcfg/device_config.go (about)

     1  /*
     2   * Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package wgcfg
    19  
    20  import (
    21  	"encoding/base64"
    22  	"encoding/hex"
    23  	"encoding/json"
    24  	"fmt"
    25  	"net"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/rs/zerolog/log"
    30  )
    31  
    32  // Stats represents wireguard peer statistics information.
    33  type Stats struct {
    34  	BytesSent     uint64    `json:"bytes_sent"`
    35  	BytesReceived uint64    `json:"bytes_received"`
    36  	LastHandshake time.Time `json:"last_handshake"`
    37  }
    38  
    39  // DeviceConfig describes wireguard device configuration.
    40  type DeviceConfig struct {
    41  	IfaceName  string    `json:"iface_name"`
    42  	MTU        int       `json:"mtu"`
    43  	Subnet     net.IPNet `json:"subnet"`
    44  	PrivateKey string    `json:"private_key"`
    45  	ListenPort int       `json:"listen_port"`
    46  	DNSPort    int       `json:"dns_port,omitempty"`
    47  	DNS        []string  `json:"dns"`
    48  	// Used only for unix.
    49  	DNSScriptDir string `json:"dns_script_dir"`
    50  
    51  	Peer         Peer `json:"peer"`
    52  	ReplacePeers bool `json:"replace_peers,omitempty"`
    53  
    54  	ProxyPort int `json:"proxy_port,omitempty"`
    55  }
    56  
    57  // MarshalJSON implements json.Marshaler interface to provide human readable configuration.
    58  func (dc DeviceConfig) MarshalJSON() ([]byte, error) {
    59  	type peer struct {
    60  		PublicKey              string   `json:"public_key"`
    61  		Endpoint               string   `json:"endpoint"`
    62  		AllowedIPs             []string `json:"allowed_i_ps"`
    63  		KeepAlivePeriodSeconds int      `json:"keep_alive_period_seconds"`
    64  	}
    65  
    66  	type deviceConfig struct {
    67  		IfaceName    string   `json:"iface_name"`
    68  		Subnet       string   `json:"subnet"`
    69  		PrivateKey   string   `json:"private_key"`
    70  		ListenPort   int      `json:"listen_port"`
    71  		DNS          []string `json:"dns"`
    72  		DNSScriptDir string   `json:"dns_script_dir"`
    73  		Peer         peer     `json:"peer"`
    74  		ReplacePeers bool     `json:"replace_peers,omitempty"`
    75  		ProxyPort    int      `json:"proxy_port,omitempty"`
    76  	}
    77  
    78  	var peerEndpoint string
    79  	if dc.Peer.Endpoint != nil {
    80  		peerEndpoint = dc.Peer.Endpoint.String()
    81  	}
    82  
    83  	return json.Marshal(&deviceConfig{
    84  		IfaceName:    dc.IfaceName,
    85  		Subnet:       dc.Subnet.String(),
    86  		PrivateKey:   dc.PrivateKey,
    87  		ListenPort:   dc.ListenPort,
    88  		DNS:          dc.DNS,
    89  		DNSScriptDir: dc.DNSScriptDir,
    90  		Peer: peer{
    91  			PublicKey:              dc.Peer.PublicKey,
    92  			Endpoint:               peerEndpoint,
    93  			AllowedIPs:             dc.Peer.AllowedIPs,
    94  			KeepAlivePeriodSeconds: dc.Peer.KeepAlivePeriodSeconds,
    95  		},
    96  		ReplacePeers: dc.ReplacePeers,
    97  		ProxyPort:    dc.ProxyPort,
    98  	})
    99  }
   100  
   101  // UnmarshalJSON implements json.Unmarshaler interface to receive human readable configuration.
   102  func (dc *DeviceConfig) UnmarshalJSON(data []byte) error {
   103  	type peer struct {
   104  		PublicKey              string   `json:"public_key"`
   105  		Endpoint               string   `json:"endpoint"`
   106  		AllowedIPs             []string `json:"allowed_i_ps"`
   107  		KeepAlivePeriodSeconds int      `json:"keep_alive_period_seconds"`
   108  	}
   109  
   110  	type deviceConfig struct {
   111  		IfaceName    string   `json:"iface_name"`
   112  		Subnet       string   `json:"subnet"`
   113  		PrivateKey   string   `json:"private_key"`
   114  		ListenPort   int      `json:"listen_port"`
   115  		DNS          []string `json:"dns"`
   116  		DNSScriptDir string   `json:"dns_script_dir"`
   117  		Peer         peer     `json:"peer"`
   118  		ReplacePeers bool     `json:"replace_peers,omitempty"`
   119  		ProxyPort    int      `json:"proxy_port"`
   120  	}
   121  
   122  	cfg := deviceConfig{}
   123  
   124  	if err := json.Unmarshal(data, &cfg); err != nil {
   125  		return fmt.Errorf("could not unmarshal device config: %w", err)
   126  	}
   127  
   128  	ip, ipnet, err := net.ParseCIDR(cfg.Subnet)
   129  	if err != nil {
   130  		return fmt.Errorf("could not parse subnet: %w", err)
   131  	}
   132  
   133  	var peerEndpoint *net.UDPAddr
   134  	if cfg.Peer.Endpoint != "" {
   135  		peerEndpoint, err = net.ResolveUDPAddr("udp", cfg.Peer.Endpoint)
   136  		if err != nil {
   137  			return fmt.Errorf("could not resolve peer endpoint: %w", err)
   138  		}
   139  	}
   140  
   141  	dc.IfaceName = cfg.IfaceName
   142  	dc.Subnet = *ipnet
   143  	dc.Subnet.IP = ip
   144  	dc.PrivateKey = cfg.PrivateKey
   145  	dc.ListenPort = cfg.ListenPort
   146  	dc.DNS = cfg.DNS
   147  	dc.DNSScriptDir = cfg.DNSScriptDir
   148  	dc.Peer = Peer{
   149  		PublicKey:              cfg.Peer.PublicKey,
   150  		Endpoint:               peerEndpoint,
   151  		AllowedIPs:             cfg.Peer.AllowedIPs,
   152  		KeepAlivePeriodSeconds: cfg.Peer.KeepAlivePeriodSeconds,
   153  	}
   154  	dc.ReplacePeers = cfg.ReplacePeers
   155  	dc.ProxyPort = cfg.ProxyPort
   156  
   157  	return nil
   158  }
   159  
   160  // Encode encodes device config into string representation which is used for
   161  // userspace and kernel space wireguard configuration.
   162  func (dc *DeviceConfig) Encode() string {
   163  	var res strings.Builder
   164  	keyBytes, err := base64.StdEncoding.DecodeString(dc.PrivateKey)
   165  	if err != nil {
   166  		log.Err(err).Msg("Could not decode device private key. Will use empty config.")
   167  		return ""
   168  	}
   169  	hexKey := hex.EncodeToString(keyBytes)
   170  
   171  	res.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
   172  	res.WriteString(fmt.Sprintf("listen_port=%d\n", dc.ListenPort))
   173  
   174  	if dc.ReplacePeers {
   175  		res.WriteString(fmt.Sprintf("replace_peers=%t\n", dc.ReplacePeers))
   176  	}
   177  
   178  	res.WriteString(dc.Peer.Encode())
   179  	return res.String()
   180  }
   181  
   182  // Peer represents wireguard peer.
   183  type Peer struct {
   184  	PublicKey              string       `json:"public_key"`
   185  	Endpoint               *net.UDPAddr `json:"endpoint"`
   186  	AllowedIPs             []string     `json:"allowed_i_ps"`
   187  	KeepAlivePeriodSeconds int          `json:"keep_alive_period_seconds"`
   188  }
   189  
   190  // Encode encodes device peer config into string representation which is used for
   191  // userspace and kernel space wireguard configuration.
   192  func (p *Peer) Encode() string {
   193  	var res strings.Builder
   194  
   195  	keyBytes, err := base64.StdEncoding.DecodeString(p.PublicKey)
   196  	if err != nil {
   197  		log.Err(err).Msg("Could not decode device public key. Will use empty config.")
   198  		return ""
   199  	}
   200  	hexKey := hex.EncodeToString(keyBytes)
   201  	res.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
   202  	res.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", p.KeepAlivePeriodSeconds))
   203  	if p.Endpoint != nil {
   204  		res.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
   205  	}
   206  	for _, ip := range p.AllowedIPs {
   207  		res.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip))
   208  	}
   209  	return res.String()
   210  }