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 }