mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-07-20 06:18:06 +03:00
Add OpenVPN, TrustTunnel, Sudoku, inbound managers. Fixes
This commit is contained in:
135
common/tls/openvpn_client.go
Normal file
135
common/tls/openvpn_client.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func NewOpenVPNClient(ctx context.Context, logger logger.ContextLogger, options option.OpenVPNTLSOptions) (Config, error) {
|
||||
ca := options.CA
|
||||
if ca == "" && options.CAPath != "" {
|
||||
data, err := os.ReadFile(options.CAPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read ca_path")
|
||||
}
|
||||
ca = string(data)
|
||||
}
|
||||
certificate := options.Certificate
|
||||
if certificate == "" && options.CertificatePath != "" {
|
||||
data, err := os.ReadFile(options.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read certificate_path")
|
||||
}
|
||||
certificate = string(data)
|
||||
}
|
||||
key := options.Key
|
||||
if key == "" && options.KeyPath != "" {
|
||||
data, err := os.ReadFile(options.KeyPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read key_path")
|
||||
}
|
||||
key = string(data)
|
||||
}
|
||||
if strings.TrimSpace(ca) == "" {
|
||||
return nil, E.New("openvpn: missing ca certificate")
|
||||
}
|
||||
if block, _ := pem.Decode([]byte(ca)); block == nil {
|
||||
return nil, E.New("openvpn: ca is not valid PEM")
|
||||
}
|
||||
hasCert := strings.TrimSpace(certificate) != "" || strings.TrimSpace(key) != ""
|
||||
if hasCert {
|
||||
if strings.TrimSpace(certificate) == "" || strings.TrimSpace(key) == "" {
|
||||
return nil, E.New("openvpn: certificate and key must both be set")
|
||||
}
|
||||
if block, _ := pem.Decode([]byte(certificate)); block == nil {
|
||||
return nil, E.New("openvpn: certificate is not valid PEM")
|
||||
}
|
||||
if block, _ := pem.Decode([]byte(key)); block == nil {
|
||||
return nil, E.New("openvpn: key is not valid PEM")
|
||||
}
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM([]byte(ca)) {
|
||||
return nil, E.New("openvpn: failed to parse ca certificate")
|
||||
}
|
||||
var tlsConfig tls.Config
|
||||
tlsConfig.RootCAs = roots
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
if options.CipherSuites != nil {
|
||||
find:
|
||||
for _, cipherSuite := range options.CipherSuites {
|
||||
for _, tlsCipherSuite := range tls.CipherSuites() {
|
||||
if cipherSuite == tlsCipherSuite.Name {
|
||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
|
||||
continue find
|
||||
}
|
||||
}
|
||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||
}
|
||||
}
|
||||
tlsConfig.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||
if len(cs.PeerCertificates) == 0 {
|
||||
return E.New("openvpn: server did not provide certificate")
|
||||
}
|
||||
cert := cs.PeerCertificates[0]
|
||||
intermediates := x509.NewCertPool()
|
||||
for _, intermediate := range cs.PeerCertificates[1:] {
|
||||
intermediates.AddCert(intermediate)
|
||||
}
|
||||
_, err := cert.Verify(x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
Intermediates: intermediates,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if options.VerifyX509Name != "" {
|
||||
cn := cert.Subject.CommonName
|
||||
switch options.VerifyX509NameMode {
|
||||
case "name-prefix":
|
||||
if !strings.HasPrefix(cn, options.VerifyX509Name) {
|
||||
return E.New("openvpn: server CN ", cn, " does not match prefix ", options.VerifyX509Name)
|
||||
}
|
||||
case "name-suffix":
|
||||
if !strings.HasSuffix(cn, options.VerifyX509Name) {
|
||||
return E.New("openvpn: server CN ", cn, " does not match suffix ", options.VerifyX509Name)
|
||||
}
|
||||
default:
|
||||
if cn != options.VerifyX509Name {
|
||||
return E.New("openvpn: server CN ", cn, " does not match ", options.VerifyX509Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if hasCert {
|
||||
cert, err := tls.X509KeyPair([]byte(certificate), []byte(key))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "openvpn: parse client certificate/key")
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
var config Config = &STDClientConfig{ctx, &tlsConfig, false, 0, false}
|
||||
if options.KernelRx || options.KernelTx {
|
||||
if !C.IsLinux {
|
||||
return nil, E.New("kTLS is only supported on Linux")
|
||||
}
|
||||
config = &KTLSClientConfig{
|
||||
Config: config,
|
||||
logger: logger,
|
||||
kernelTx: options.KernelTx,
|
||||
kernelRx: options.KernelRx,
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
Reference in New Issue
Block a user