github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/openvpn/client.go (about)

     1  /*
     2   * Copyright (C) 2017 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 openvpn
    19  
    20  import (
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/pkg/errors"
    28  	"github.com/rs/zerolog/log"
    29  
    30  	"github.com/mysteriumnetwork/go-openvpn/openvpn"
    31  	"github.com/mysteriumnetwork/go-openvpn/openvpn/management"
    32  	"github.com/mysteriumnetwork/go-openvpn/openvpn/middlewares/client/auth"
    33  	openvpn_bytescount "github.com/mysteriumnetwork/go-openvpn/openvpn/middlewares/client/bytescount"
    34  	"github.com/mysteriumnetwork/go-openvpn/openvpn/middlewares/state"
    35  	"github.com/mysteriumnetwork/node/config"
    36  	"github.com/mysteriumnetwork/node/core/connection"
    37  	"github.com/mysteriumnetwork/node/core/connection/connectionstate"
    38  	"github.com/mysteriumnetwork/node/core/ip"
    39  	"github.com/mysteriumnetwork/node/firewall"
    40  	"github.com/mysteriumnetwork/node/identity"
    41  	"github.com/mysteriumnetwork/node/session"
    42  )
    43  
    44  // ErrProcessNotStarted represents the error we return when the process is not started yet
    45  var ErrProcessNotStarted = errors.New("process not started yet")
    46  
    47  // processFactory creates a new openvpn process
    48  type processFactory func(options connection.ConnectOptions, sessionConfig VPNConfig) (openvpn.Process, *ClientConfig, error)
    49  
    50  // NewClient creates a new openvpn connection
    51  func NewClient(openvpnBinary, scriptDir, runtimeDir string,
    52  	signerFactory identity.SignerFactory,
    53  	ipResolver ip.Resolver,
    54  ) (connection.Connection, error) {
    55  	stateCh := make(chan connectionstate.State, 100)
    56  	client := &Client{
    57  		scriptDir:           scriptDir,
    58  		runtimeDir:          runtimeDir,
    59  		signerFactory:       signerFactory,
    60  		stateCh:             stateCh,
    61  		ipResolver:          ipResolver,
    62  		removeAllowedIPRule: func() {},
    63  	}
    64  
    65  	procFactory := func(options connection.ConnectOptions, sessionConfig VPNConfig) (openvpn.Process, *ClientConfig, error) {
    66  		vpnClientConfig, err := NewClientConfigFromSession(sessionConfig, scriptDir, runtimeDir, options)
    67  		if err != nil {
    68  			return nil, nil, err
    69  		}
    70  
    71  		signer := signerFactory(options.ConsumerID)
    72  
    73  		stateMiddleware := newStateMiddleware(stateCh)
    74  		authMiddleware := newAuthMiddleware(options.SessionID, signer)
    75  		byteCountMiddleware := openvpn_bytescount.NewMiddleware(client.OnStats, config.GetDuration(config.FlagStatsReportInterval))
    76  		proc := openvpn.CreateNewProcess(openvpnBinary, vpnClientConfig.GenericConfig, stateMiddleware, byteCountMiddleware, authMiddleware)
    77  		return proc, vpnClientConfig, nil
    78  	}
    79  
    80  	client.processFactory = procFactory
    81  	return client, nil
    82  }
    83  
    84  // Client takes in the openvpn process and works with it
    85  type Client struct {
    86  	scriptDir           string
    87  	runtimeDir          string
    88  	signerFactory       identity.SignerFactory
    89  	stateCh             chan connectionstate.State
    90  	stats               connectionstate.Statistics
    91  	statsMu             sync.RWMutex
    92  	process             openvpn.Process
    93  	processFactory      processFactory
    94  	ipResolver          ip.Resolver
    95  	removeAllowedIPRule func()
    96  	stopOnce            sync.Once
    97  }
    98  
    99  var _ connection.Connection = &Client{}
   100  
   101  // State returns connection state channel.
   102  func (c *Client) State() <-chan connectionstate.State {
   103  	return c.stateCh
   104  }
   105  
   106  // Statistics returns connection statistics channel.
   107  func (c *Client) Statistics() (connectionstate.Statistics, error) {
   108  	c.statsMu.RLock()
   109  	defer c.statsMu.RUnlock()
   110  	return c.stats, nil
   111  }
   112  
   113  // Reconnect restarts a connection with a new options.
   114  func (c *Client) Reconnect(ctx context.Context, options connection.ConnectOptions) error {
   115  	return fmt.Errorf("not supported")
   116  }
   117  
   118  // Start starts the connection
   119  func (c *Client) Start(ctx context.Context, options connection.ConnectOptions) error {
   120  	log.Info().Msg("Starting connection")
   121  
   122  	sessionConfig := VPNConfig{}
   123  	err := json.Unmarshal(options.SessionConfig, &sessionConfig)
   124  	if err != nil {
   125  		return errors.Wrap(err, "failed to unmarshal session config")
   126  	}
   127  
   128  	c.removeAllowedIPRule, err = firewall.AllowIPAccess(sessionConfig.RemoteIP)
   129  	if err != nil {
   130  		return errors.Wrap(err, "failed to add allowed IP address")
   131  	}
   132  
   133  	proc, clientConfig, err := c.processFactory(options, sessionConfig)
   134  	if err != nil {
   135  		log.Info().Err(err).Msg("Client config factory error")
   136  		return errors.Wrap(err, "client config factory error")
   137  	}
   138  	c.process = proc
   139  	log.Info().Interface("data", clientConfig).Msgf("Openvpn client configuration")
   140  
   141  	err = c.process.Start()
   142  	if err != nil {
   143  		c.removeAllowedIPRule()
   144  	}
   145  	return errors.Wrap(err, "failed to start client process")
   146  }
   147  
   148  // Stop stops the connection
   149  func (c *Client) Stop() {
   150  	c.stopOnce.Do(func() {
   151  		if c.process != nil {
   152  			c.process.Stop()
   153  		}
   154  		c.removeAllowedIPRule()
   155  	})
   156  }
   157  
   158  // OnStats updates connection statistics.
   159  func (c *Client) OnStats(cnt openvpn_bytescount.Bytecount) error {
   160  	c.statsMu.Lock()
   161  	defer c.statsMu.Unlock()
   162  	c.stats.At = time.Now()
   163  	c.stats.BytesReceived = cnt.BytesIn
   164  	c.stats.BytesSent = cnt.BytesOut
   165  	return nil
   166  }
   167  
   168  // GetConfig returns the consumer-side configuration.
   169  func (c *Client) GetConfig() (connection.ConsumerConfig, error) {
   170  	return &ConsumerConfig{}, nil
   171  }
   172  
   173  // VPNConfig structure represents VPN configuration options for given session
   174  type VPNConfig struct {
   175  	DNSIPs          string `json:"dns_ips"`
   176  	RemoteIP        string `json:"remote"`
   177  	RemotePort      int    `json:"port"`
   178  	LocalPort       int    `json:"lport"`
   179  	Ports           []int  `json:"ports"`
   180  	RemoteProtocol  string `json:"protocol"`
   181  	TLSPresharedKey string `json:"TLSPresharedKey"`
   182  	CACertificate   string `json:"CACertificate"`
   183  }
   184  
   185  func newAuthMiddleware(sessionID session.ID, signer identity.Signer) management.Middleware {
   186  	credentialsProvider := SignatureCredentialsProvider(sessionID, signer)
   187  	return auth.NewMiddleware(credentialsProvider)
   188  }
   189  
   190  func newStateMiddleware(stateChannel connection.StateChannel) management.Middleware {
   191  	stateCallback := getStateCallback(stateChannel)
   192  	return state.NewMiddleware(stateCallback)
   193  }
   194  
   195  // getStateCallback returns the callback for working with openvpn state
   196  func getStateCallback(stateChannel connection.StateChannel) func(openvpnState openvpn.State) {
   197  	return func(openvpnState openvpn.State) {
   198  		connectionState := openVpnStateCallbackToConnectionState(openvpnState)
   199  		if connectionState != connectionstate.Unknown {
   200  			stateChannel <- connectionState
   201  		}
   202  
   203  		// this is the last state - close channel (according to best practices of go - channel writer controls channel)
   204  		if openvpnState == openvpn.ProcessExited {
   205  			close(stateChannel)
   206  		}
   207  	}
   208  }
   209  
   210  // openvpnStateMap maps openvpn states to connection state
   211  var openvpnStateMap = map[openvpn.State]connectionstate.State{
   212  	openvpn.ConnectedState:    connectionstate.Connected,
   213  	openvpn.ExitingState:      connectionstate.Disconnecting,
   214  	openvpn.ReconnectingState: connectionstate.Reconnecting,
   215  }
   216  
   217  // openVpnStateCallbackToConnectionState maps openvpn.State to connection.State. Returns a pointer to connection.state, or nil
   218  func openVpnStateCallbackToConnectionState(input openvpn.State) connectionstate.State {
   219  	if val, ok := openvpnStateMap[input]; ok {
   220  		return val
   221  	}
   222  	return connectionstate.Unknown
   223  }