github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/endpoint/remoteclient/client.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 remoteclient
    19  
    20  import (
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"fmt"
    24  	"os/user"
    25  	"sync"
    26  
    27  	"github.com/rs/zerolog/log"
    28  
    29  	"github.com/mysteriumnetwork/node/services/wireguard/wgcfg"
    30  	supervisorclient "github.com/mysteriumnetwork/node/supervisor/client"
    31  	"github.com/mysteriumnetwork/node/utils"
    32  )
    33  
    34  type client struct {
    35  	mu    sync.Mutex
    36  	iface string
    37  }
    38  
    39  // New create new remote WireGuard client which communicates with supervisor.
    40  func New() (*client, error) {
    41  	log.Debug().Msg("Creating remote wg client")
    42  	return &client{}, nil
    43  }
    44  
    45  func (c *client) ReConfigureDevice(config wgcfg.DeviceConfig) error {
    46  	return c.ConfigureDevice(config)
    47  }
    48  
    49  func (c *client) ConfigureDevice(config wgcfg.DeviceConfig) error {
    50  	c.mu.Lock()
    51  	defer c.mu.Unlock()
    52  	c.iface = config.IfaceName
    53  	currentUser, err := user.Current()
    54  	if err != nil {
    55  		return fmt.Errorf("could not get current OS user: %w", err)
    56  	}
    57  
    58  	jsonCfg, err := json.Marshal(config)
    59  	if err != nil {
    60  		return fmt.Errorf("could not marshal device config to JSON: %w", err)
    61  	}
    62  
    63  	// Convert config to base64 to prevent nasty parsing issues on supervisor.
    64  	jsonb64 := base64.StdEncoding.EncodeToString(jsonCfg)
    65  
    66  	actualIface, err := supervisorclient.Command("wg-up", "-uid", currentUser.Uid, "-config", jsonb64)
    67  	if err != nil {
    68  		return fmt.Errorf("failed to create wg interface: %w", err)
    69  	}
    70  	log.Debug().Msgf("Tunnel interface created: %s", actualIface)
    71  	return nil
    72  }
    73  
    74  func (c *client) DestroyDevice(iface string) error {
    75  	_, err := supervisorclient.Command("wg-down", "-iface", iface)
    76  	if err != nil {
    77  		return fmt.Errorf("failed to destroy wg interface: %w", err)
    78  	}
    79  	return nil
    80  }
    81  
    82  func (c *client) PeerStats(iface string) (wgcfg.Stats, error) {
    83  	statsJSON, err := supervisorclient.Command("wg-stats", "-iface", iface)
    84  	if err != nil {
    85  		return wgcfg.Stats{}, fmt.Errorf("failed to get wg stats: %w", err)
    86  	}
    87  
    88  	stats := wgcfg.Stats{}
    89  	if err := json.Unmarshal([]byte(statsJSON), &stats); err != nil {
    90  		return wgcfg.Stats{}, fmt.Errorf("could not unmarshal stats: %w", err)
    91  	}
    92  	return stats, nil
    93  }
    94  
    95  func (c *client) Close() (err error) {
    96  	c.mu.Lock()
    97  	defer c.mu.Unlock()
    98  
    99  	errs := utils.ErrorCollection{}
   100  	if err := c.DestroyDevice(c.iface); err != nil {
   101  		errs.Add(err)
   102  	}
   103  	if err := errs.Error(); err != nil {
   104  		return fmt.Errorf("could not close client: %w", err)
   105  	}
   106  	return nil
   107  }