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 }