github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/openvpn/config_validator.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  	"bufio"
    22  	"bytes"
    23  	"crypto/x509"
    24  	"encoding/pem"
    25  	"fmt"
    26  	"net"
    27  	"strings"
    28  
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  // ValidateConfig is function which takes VPNConfig as argument, checks it and returns error if validation fails
    33  type ValidateConfig func(config VPNConfig) error
    34  
    35  // ConfigValidator represents structure which contains list of validating functions
    36  type ConfigValidator struct {
    37  	validators []ValidateConfig
    38  }
    39  
    40  // NewDefaultValidator returns ConfigValidator with predefined list of validating functions
    41  func NewDefaultValidator() *ConfigValidator {
    42  	return &ConfigValidator{
    43  		validators: []ValidateConfig{
    44  			validProtocol,
    45  			validIPFormat,
    46  			validTLSPresharedKey,
    47  			validCACertificate,
    48  		},
    49  	}
    50  }
    51  
    52  // IsValid function checks if provided config is valid against given config validator and returns first encountered error
    53  func (v *ConfigValidator) IsValid(config VPNConfig) error {
    54  	for _, validator := range v.validators {
    55  		if err := validator(config); err != nil {
    56  			return err
    57  		}
    58  	}
    59  	return nil
    60  }
    61  
    62  func validProtocol(config VPNConfig) error {
    63  	switch config.RemoteProtocol {
    64  	case
    65  		"udp",
    66  		"tcp":
    67  		return nil
    68  	}
    69  	return errors.New("invalid protocol: " + config.RemoteProtocol)
    70  }
    71  
    72  func validIPFormat(config VPNConfig) error {
    73  	parsed := net.ParseIP(config.RemoteIP)
    74  	if parsed == nil {
    75  		return errors.New("unable to parse ip address " + config.RemoteIP)
    76  	}
    77  	if parsed.To4() == nil {
    78  		return errors.New("IPv4 address is expected")
    79  	}
    80  	return nil
    81  }
    82  
    83  func validTLSPresharedKey(config VPNConfig) error {
    84  	_, err := FormatTLSPresharedKey(config)
    85  	return err
    86  }
    87  
    88  // FormatTLSPresharedKey formats preshared key (PEM blocks with data encoded to hex) are taken from
    89  // openvpn --genkey --secret static.key, which is openvpn specific.
    90  // it reformats key from single line to multiline fixed length strings.
    91  func FormatTLSPresharedKey(config VPNConfig) (VPNConfig, error) {
    92  	contentScanner := bufio.NewScanner(bytes.NewBufferString(config.TLSPresharedKey))
    93  	for contentScanner.Scan() {
    94  		line := contentScanner.Text()
    95  		//skip empty lines or comments
    96  		if len(line) > 0 || strings.HasPrefix(line, "#") {
    97  			break
    98  		}
    99  	}
   100  	if err := contentScanner.Err(); err != nil {
   101  		return VPNConfig{}, contentScanner.Err()
   102  	}
   103  	header := contentScanner.Text()
   104  	if header != "-----BEGIN OpenVPN Static key V1-----" {
   105  		return VPNConfig{}, errors.New("Invalid key header: " + header)
   106  	}
   107  
   108  	var key string
   109  	for contentScanner.Scan() {
   110  		line := contentScanner.Text()
   111  		if line == "-----END OpenVPN Static key V1-----" {
   112  			break
   113  		} else {
   114  			key = key + line
   115  		}
   116  	}
   117  	if err := contentScanner.Err(); err != nil {
   118  		return VPNConfig{}, err
   119  	}
   120  	// 256 bytes key is 512 bytes if encoded to hex
   121  	if len(key) != 512 {
   122  		return VPNConfig{}, errors.New("invalid key length")
   123  	}
   124  
   125  	var buff = &bytes.Buffer{}
   126  	fmt.Fprintln(buff, "-----BEGIN OpenVPN Static key V1-----")
   127  	left := key
   128  	for ; len(left) > 64; left = left[64:] {
   129  		fmt.Fprintln(buff, left[0:64])
   130  	}
   131  	fmt.Fprintln(buff, left)
   132  	fmt.Fprintln(buff, "-----END OpenVPN Static key V1-----")
   133  	config.TLSPresharedKey = buff.String()
   134  
   135  	return config, nil
   136  }
   137  
   138  func validCACertificate(config VPNConfig) error {
   139  	pemBlock, _ := pem.Decode([]byte(config.CACertificate))
   140  	if pemBlock.Type != "CERTIFICATE" {
   141  		return errors.New("invalid CA certificate. Certificate block expected")
   142  	}
   143  	//if we parse it correctly - at least structure is right
   144  	_, err := x509.ParseCertificate(pemBlock.Bytes)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	return nil
   149  }