mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
feat: add TLS mutual authentication
Introduce a feature to require and verify client certificate to provide mutual authentication in TLS.
This commit is contained in:
parent
0f643bf414
commit
737f47858a
@ -128,5 +128,40 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
if options.ClientAuth {
|
||||||
|
// explicitly call mutual verification
|
||||||
|
var cert []byte
|
||||||
|
var key []byte
|
||||||
|
|
||||||
|
if len(options.ClientCertificate) > 0 {
|
||||||
|
cert = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||||
|
} else if options.ClientCertificatePath != "" {
|
||||||
|
content, err := os.ReadFile(options.ClientCertificatePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read client certificate")
|
||||||
|
}
|
||||||
|
cert = content
|
||||||
|
}
|
||||||
|
if len(options.ClientKey) > 0 {
|
||||||
|
key = []byte(strings.Join(options.ClientKey, "\n"))
|
||||||
|
} else if options.ClientKeyPath != "" {
|
||||||
|
content, err := os.ReadFile(options.ClientKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read client key")
|
||||||
|
}
|
||||||
|
key = content
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return nil, E.New("missing client certificate")
|
||||||
|
} else if key == nil {
|
||||||
|
return nil, E.New("missing client key")
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPair, err := tls.X509KeyPair(cert, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.New(err, "parse x509 key pair")
|
||||||
|
}
|
||||||
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
|
}
|
||||||
return &STDClientConfig{&tlsConfig}, nil
|
return &STDClientConfig{&tlsConfig}, nil
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -212,6 +213,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
var certificate []byte
|
var certificate []byte
|
||||||
var key []byte
|
var key []byte
|
||||||
|
var clientca []byte
|
||||||
if acmeService == nil {
|
if acmeService == nil {
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@ -249,6 +251,29 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.ClientAuth {
|
||||||
|
// require client authentication
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
if len(options.ClientCA) > 0 {
|
||||||
|
clientca = []byte(strings.Join(options.ClientCA, "\n"))
|
||||||
|
} else if options.ClientCAPath != "" {
|
||||||
|
content, err := os.ReadFile(options.ClientCAPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read client CA")
|
||||||
|
}
|
||||||
|
clientca = content
|
||||||
|
}
|
||||||
|
// add client ca certpool
|
||||||
|
if len(clientca) > 0 {
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
if !certPool.AppendCertsFromPEM(clientca) {
|
||||||
|
return nil, E.New("failed to parse client CA:\n\n", clientca)
|
||||||
|
}
|
||||||
|
tlsConfig.ClientCAs = certPool
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tlsConfig.ClientAuth = tls.NoClientCert
|
||||||
|
}
|
||||||
return &STDServerConfig{
|
return &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -257,5 +282,6 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
key: key,
|
key: key,
|
||||||
certificatePath: options.CertificatePath,
|
certificatePath: options.CertificatePath,
|
||||||
keyPath: options.KeyPath,
|
keyPath: options.KeyPath,
|
||||||
|
watcher: &fsnotify.Watcher{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,45 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
type InboundTLSOptions struct {
|
type InboundTLSOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
ServerName string `json:"server_name,omitempty"`
|
ServerName string `json:"server_name,omitempty"`
|
||||||
Insecure bool `json:"insecure,omitempty"`
|
Insecure bool `json:"insecure,omitempty"`
|
||||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||||
MinVersion string `json:"min_version,omitempty"`
|
MinVersion string `json:"min_version,omitempty"`
|
||||||
MaxVersion string `json:"max_version,omitempty"`
|
MaxVersion string `json:"max_version,omitempty"`
|
||||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||||
Certificate Listable[string] `json:"certificate,omitempty"`
|
Certificate Listable[string] `json:"certificate,omitempty"`
|
||||||
CertificatePath string `json:"certificate_path,omitempty"`
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
Key Listable[string] `json:"key,omitempty"`
|
Key Listable[string] `json:"key,omitempty"`
|
||||||
KeyPath string `json:"key_path,omitempty"`
|
KeyPath string `json:"key_path,omitempty"`
|
||||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
ClientAuth bool `json:"clientAuth,omitempty"`
|
||||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
// , Requireandverify
|
||||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
ClientCA Listable[string] `json:"clientCA,omitempty"`
|
||||||
|
ClientCAPath string `json:"clientCA_path,omitempty"`
|
||||||
|
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||||
|
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||||
|
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OutboundTLSOptions struct {
|
type OutboundTLSOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
DisableSNI bool `json:"disable_sni,omitempty"`
|
DisableSNI bool `json:"disable_sni,omitempty"`
|
||||||
ServerName string `json:"server_name,omitempty"`
|
ServerName string `json:"server_name,omitempty"`
|
||||||
Insecure bool `json:"insecure,omitempty"`
|
Insecure bool `json:"insecure,omitempty"`
|
||||||
ALPN Listable[string] `json:"alpn,omitempty"`
|
ALPN Listable[string] `json:"alpn,omitempty"`
|
||||||
MinVersion string `json:"min_version,omitempty"`
|
MinVersion string `json:"min_version,omitempty"`
|
||||||
MaxVersion string `json:"max_version,omitempty"`
|
MaxVersion string `json:"max_version,omitempty"`
|
||||||
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
CipherSuites Listable[string] `json:"cipher_suites,omitempty"`
|
||||||
Certificate Listable[string] `json:"certificate,omitempty"`
|
Certificate Listable[string] `json:"certificate,omitempty"`
|
||||||
CertificatePath string `json:"certificate_path,omitempty"`
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
ClientAuth bool `json:"clientAuth,omitempty"`
|
||||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
ClientKey Listable[string] `json:"client_key,omitempty"`
|
||||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
ClientKeyPath string `json:"client_key_path,omitempty"`
|
||||||
|
ClientCertificate Listable[string] `json:"client_certificate,omitempty"`
|
||||||
|
ClientCertificatePath string `json:"client_certificate_path,omitempty"`
|
||||||
|
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||||
|
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||||
|
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InboundRealityOptions struct {
|
type InboundRealityOptions struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user