Add curve preferences, pinned public key SHA256 and mTLS for TLS options
This commit is contained in:
@@ -68,7 +68,10 @@ func NewRealityServer(ctx context.Context, logger log.ContextLogger, options opt
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
if len(options.CurvePreferences) > 0 {
|
||||||
|
return nil, E.New("curve preferences is unavailable in reality")
|
||||||
|
}
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" || len(options.ClientCertificatePublicKeySHA256) > 0 {
|
||||||
return nil, E.New("certificate is unavailable in reality")
|
return nil, E.New("certificate is unavailable in reality")
|
||||||
}
|
}
|
||||||
if len(options.Key) > 0 || options.KeyPath != "" {
|
if len(options.Key) > 0 || options.KeyPath != "" {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -108,6 +111,15 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.CertificatePublicKeySHA256) > 0 {
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
|
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
||||||
|
}
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
@@ -137,6 +149,9 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, curve := range options.CurvePreferences {
|
||||||
|
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
|
||||||
|
}
|
||||||
var certificate []byte
|
var certificate []byte
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@@ -175,3 +190,22 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyPublicKeySHA256(knownHashValues [][]byte, rawCerts [][]byte, timeFunc func() time.Time) error {
|
||||||
|
leafCertificate, err := x509.ParseCertificate(rawCerts[0])
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "failed to parse leaf certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyBytes, err := x509.MarshalPKIXPublicKey(leafCertificate.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "failed to marshal public key")
|
||||||
|
}
|
||||||
|
hashValue := sha256.Sum256(pubKeyBytes)
|
||||||
|
for _, value := range knownHashValues {
|
||||||
|
if bytes.Equal(value, hashValue[:]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return E.New("unrecognized remote public key: ", base64.StdEncoding.EncodeToString(hashValue[:]))
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -22,16 +23,17 @@ import (
|
|||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
|
|
||||||
type STDServerConfig struct {
|
type STDServerConfig struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
acmeService adapter.SimpleLifecycle
|
acmeService adapter.SimpleLifecycle
|
||||||
certificate []byte
|
certificate []byte
|
||||||
key []byte
|
key []byte
|
||||||
certificatePath string
|
certificatePath string
|
||||||
keyPath string
|
keyPath string
|
||||||
echKeyPath string
|
clientCertificatePath []string
|
||||||
watcher *fswatch.Watcher
|
echKeyPath string
|
||||||
|
watcher *fswatch.Watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) ServerName() string {
|
func (c *STDServerConfig) ServerName() string {
|
||||||
@@ -111,6 +113,9 @@ func (c *STDServerConfig) startWatcher() error {
|
|||||||
if c.echKeyPath != "" {
|
if c.echKeyPath != "" {
|
||||||
watchPath = append(watchPath, c.echKeyPath)
|
watchPath = append(watchPath, c.echKeyPath)
|
||||||
}
|
}
|
||||||
|
if len(c.clientCertificatePath) > 0 {
|
||||||
|
watchPath = append(watchPath, c.clientCertificatePath...)
|
||||||
|
}
|
||||||
if len(watchPath) == 0 {
|
if len(watchPath) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -159,6 +164,30 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
c.config = config
|
c.config = config
|
||||||
c.access.Unlock()
|
c.access.Unlock()
|
||||||
c.logger.Info("reloaded TLS certificate")
|
c.logger.Info("reloaded TLS certificate")
|
||||||
|
} else if common.Contains(c.clientCertificatePath, path) {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
var reloaded bool
|
||||||
|
for _, certPath := range c.clientCertificatePath {
|
||||||
|
content, err := os.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error(E.Cause(err, "reload certificate from ", c.clientCertificatePath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
||||||
|
c.logger.Error(E.New("invalid client certificate file: ", certPath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reloaded = true
|
||||||
|
}
|
||||||
|
if !reloaded {
|
||||||
|
return E.New("client certificates is empty")
|
||||||
|
}
|
||||||
|
c.access.Lock()
|
||||||
|
config := c.config.Clone()
|
||||||
|
config.ClientCAs = clientCertificateCA
|
||||||
|
c.config = config
|
||||||
|
c.access.Unlock()
|
||||||
|
c.logger.Info("reloaded client certificates")
|
||||||
} else if path == c.echKeyPath {
|
} else if path == c.echKeyPath {
|
||||||
echKey, err := os.ReadFile(c.echKeyPath)
|
echKey, err := os.ReadFile(c.echKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -235,8 +264,14 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var certificate []byte
|
for _, curveID := range options.CurvePreferences {
|
||||||
var key []byte
|
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curveID))
|
||||||
|
}
|
||||||
|
tlsConfig.ClientAuth = tls.ClientAuthType(options.ClientAuthentication)
|
||||||
|
var (
|
||||||
|
certificate []byte
|
||||||
|
key []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"))
|
||||||
@@ -278,6 +313,43 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.ClientCertificate) > 0 || len(options.ClientCertificatePath) > 0 {
|
||||||
|
if tlsConfig.ClientAuth == tls.NoClientCert {
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
||||||
|
if len(options.ClientCertificate) > 0 {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM([]byte(strings.Join(options.ClientCertificate, "\n"))) {
|
||||||
|
return nil, E.New("invalid client certificate strings")
|
||||||
|
}
|
||||||
|
tlsConfig.ClientCAs = clientCertificateCA
|
||||||
|
} else if len(options.ClientCertificatePath) > 0 {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
for _, path := range options.ClientCertificatePath {
|
||||||
|
content, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read client certificate from ", path)
|
||||||
|
}
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
||||||
|
return nil, E.New("invalid client certificate file: ", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlsConfig.ClientCAs = clientCertificateCA
|
||||||
|
} else if len(options.ClientCertificatePublicKeySHA256) > 0 {
|
||||||
|
if tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAnyClientCert
|
||||||
|
} else if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven {
|
||||||
|
tlsConfig.ClientAuth = tls.RequestClientCert
|
||||||
|
}
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.ClientCertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, E.New("missing client_certificate, client_certificate_path or client_certificate_public_key_sha256 for client authentication")
|
||||||
|
}
|
||||||
|
}
|
||||||
var echKeyPath string
|
var echKeyPath string
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
||||||
@@ -286,14 +358,15 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
serverConfig := &STDServerConfig{
|
serverConfig := &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
acmeService: acmeService,
|
acmeService: acmeService,
|
||||||
certificate: certificate,
|
certificate: certificate,
|
||||||
key: key,
|
key: key,
|
||||||
certificatePath: options.CertificatePath,
|
certificatePath: options.CertificatePath,
|
||||||
keyPath: options.KeyPath,
|
clientCertificatePath: options.ClientCertificatePath,
|
||||||
echKeyPath: echKeyPath,
|
keyPath: options.KeyPath,
|
||||||
|
echKeyPath: echKeyPath,
|
||||||
}
|
}
|
||||||
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||||
serverConfig.access.Lock()
|
serverConfig.access.Lock()
|
||||||
|
|||||||
@@ -167,6 +167,15 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
}
|
}
|
||||||
tlsConfig.InsecureServerNameToVerify = serverName
|
tlsConfig.InsecureServerNameToVerify = serverName
|
||||||
}
|
}
|
||||||
|
if len(options.CertificatePublicKeySHA256) > 0 {
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
|
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
||||||
|
}
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ icon: material/new-box
|
|||||||
!!! quote "Changes in sing-box 1.13.0"
|
!!! quote "Changes in sing-box 1.13.0"
|
||||||
|
|
||||||
:material-plus: [kernel_tx](#kernel_tx)
|
:material-plus: [kernel_tx](#kernel_tx)
|
||||||
:material-plus: [kernel_rx](#kernel_rx)
|
:material-plus: [kernel_rx](#kernel_rx)
|
||||||
|
:material-plus: [curve_preferences](#curve_preferences)
|
||||||
|
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
||||||
|
:material-plus: [client_authentication](#client_authentication)
|
||||||
|
:material-plus: [client_certificate](#client_certificate)
|
||||||
|
:material-plus: [client_certificate_path](#client_certificate_path)
|
||||||
|
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
@@ -29,8 +35,13 @@ icon: material/new-box
|
|||||||
"min_version": "",
|
"min_version": "",
|
||||||
"max_version": "",
|
"max_version": "",
|
||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
|
"curve_preferences": [],
|
||||||
"certificate": [],
|
"certificate": [],
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
|
"client_authentication": "",
|
||||||
|
"client_certificate": [],
|
||||||
|
"client_certificate_path": [],
|
||||||
|
"client_certificate_public_key_sha256": [],
|
||||||
"key": [],
|
"key": [],
|
||||||
"key_path": "",
|
"key_path": "",
|
||||||
"kernel_tx": false,
|
"kernel_tx": false,
|
||||||
@@ -92,6 +103,7 @@ icon: material/new-box
|
|||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
"certificate": "",
|
"certificate": "",
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
|
"certificate_public_key_sha256": [],
|
||||||
"fragment": false,
|
"fragment": false,
|
||||||
"fragment_fallback_delay": "",
|
"fragment_fallback_delay": "",
|
||||||
"record_fragment": false,
|
"record_fragment": false,
|
||||||
@@ -195,14 +207,29 @@ By default, the maximum version is currently TLS 1.3.
|
|||||||
|
|
||||||
#### cipher_suites
|
#### cipher_suites
|
||||||
|
|
||||||
A list of enabled TLS 1.0–1.2 cipher suites. The order of the list is ignored.
|
List of enabled TLS 1.0–1.2 cipher suites. The order of the list is ignored.
|
||||||
Note that TLS 1.3 cipher suites are not configurable.
|
Note that TLS 1.3 cipher suites are not configurable.
|
||||||
|
|
||||||
If empty, a safe default list is used. The default cipher suites might change over time.
|
If empty, a safe default list is used. The default cipher suites might change over time.
|
||||||
|
|
||||||
|
#### curve_preferences
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
Set of supported key exchange mechanisms. The order of the list is ignored, and key exchange mechanisms are chosen
|
||||||
|
from this list using an internal preference order by Golang.
|
||||||
|
|
||||||
|
Available values, also the default list:
|
||||||
|
|
||||||
|
* `P256`
|
||||||
|
* `P384`
|
||||||
|
* `P521`
|
||||||
|
* `X25519`
|
||||||
|
* `X25519MLKEM768`
|
||||||
|
|
||||||
#### certificate
|
#### certificate
|
||||||
|
|
||||||
The server certificate line array, in PEM format.
|
Server certificates chain line array, in PEM format.
|
||||||
|
|
||||||
#### certificate_path
|
#### certificate_path
|
||||||
|
|
||||||
@@ -210,7 +237,26 @@ The server certificate line array, in PEM format.
|
|||||||
|
|
||||||
Will be automatically reloaded if file modified.
|
Will be automatically reloaded if file modified.
|
||||||
|
|
||||||
The path to the server certificate, in PEM format.
|
The path to server certificate chain, in PEM format.
|
||||||
|
|
||||||
|
|
||||||
|
#### certificate_public_key_sha256
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
==Client only==
|
||||||
|
|
||||||
|
List of SHA-256 hashes of server certificate public keys, in base64 format.
|
||||||
|
|
||||||
|
To generate the SHA-256 hash for a certificate's public key, use the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For a certificate file
|
||||||
|
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
|
||||||
|
# For a certificate from a remote server
|
||||||
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
```
|
||||||
|
|
||||||
#### key
|
#### key
|
||||||
|
|
||||||
@@ -228,6 +274,63 @@ The server private key line array, in PEM format.
|
|||||||
|
|
||||||
The path to the server private key, in PEM format.
|
The path to the server private key, in PEM format.
|
||||||
|
|
||||||
|
#### client_authentication
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
==Server only==
|
||||||
|
|
||||||
|
The type of client authentication to use.
|
||||||
|
|
||||||
|
Available values:
|
||||||
|
|
||||||
|
* `no` (default)
|
||||||
|
* `request`
|
||||||
|
* `require-any`
|
||||||
|
* `verify-if-given`
|
||||||
|
* `require-and-verify`
|
||||||
|
|
||||||
|
One of `client_certificate`, `client_certificate_path`, or `client_certificate_public_key_sha256` is required
|
||||||
|
if this option is set to `verify-if-given`, or `require-and-verify`.
|
||||||
|
|
||||||
|
#### client_certificate
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
==Server only==
|
||||||
|
|
||||||
|
Client certificate chain line array, in PEM format.
|
||||||
|
|
||||||
|
#### client_certificate_path
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
==Server only==
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
Will be automatically reloaded if file modified.
|
||||||
|
|
||||||
|
List of path to client certificate chain, in PEM format.
|
||||||
|
|
||||||
|
#### client_certificate_public_key_sha256
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
==Server only==
|
||||||
|
|
||||||
|
List of SHA-256 hashes of client certificate public keys, in base64 format.
|
||||||
|
|
||||||
|
To generate the SHA-256 hash for a certificate's public key, use the following commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For a certificate file
|
||||||
|
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
|
||||||
|
# For a certificate from a remote server
|
||||||
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
```
|
||||||
|
|
||||||
#### kernel_tx
|
#### kernel_tx
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
---
|
---
|
||||||
icon: material/alert-decagram
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "sing-box 1.13.0 中的更改"
|
!!! quote "sing-box 1.13.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [kernel_tx](#kernel_tx)
|
:material-plus: [kernel_tx](#kernel_tx)
|
||||||
:material-plus: [kernel_rx](#kernel_rx)
|
:material-plus: [kernel_rx](#kernel_rx)
|
||||||
|
:material-plus: [curve_preferences](#curve_preferences)
|
||||||
|
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
||||||
|
:material-plus: [client_authentication](#client_authentication)
|
||||||
|
:material-plus: [client_certificate](#client_certificate)
|
||||||
|
:material-plus: [client_certificate_path](#client_certificate_path)
|
||||||
|
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [tls_fragment](#tls_fragment)
|
:material-plus: [fragment](#fragment)
|
||||||
:material-plus: [tls_fragment_fallback_delay](#tls_fragment_fallback_delay)
|
:material-plus: [fragment_fallback_delay](#fragment_fallback_delay)
|
||||||
:material-plus: [tls_record_fragment](#tls_record_fragment)
|
:material-plus: [record_fragment](#record_fragment)
|
||||||
:material-delete-clock: [ech.pq_signature_schemes_enabled](#pq_signature_schemes_enabled)
|
:material-delete-clock: [ech.pq_signature_schemes_enabled](#pq_signature_schemes_enabled)
|
||||||
:material-delete-clock: [ech.dynamic_record_sizing_disabled](#dynamic_record_sizing_disabled)
|
:material-delete-clock: [ech.dynamic_record_sizing_disabled](#dynamic_record_sizing_disabled)
|
||||||
|
|
||||||
!!! quote "sing-box 1.10.0 中的更改"
|
!!! quote "sing-box 1.10.0 中的更改"
|
||||||
@@ -29,8 +35,13 @@ icon: material/alert-decagram
|
|||||||
"min_version": "",
|
"min_version": "",
|
||||||
"max_version": "",
|
"max_version": "",
|
||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
|
"curve_preferences": [],
|
||||||
"certificate": [],
|
"certificate": [],
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
|
"client_authentication": "",
|
||||||
|
"client_certificate": [],
|
||||||
|
"client_certificate_path": [],
|
||||||
|
"client_certificate_public_key_sha256": [],
|
||||||
"key": [],
|
"key": [],
|
||||||
"key_path": "",
|
"key_path": "",
|
||||||
"kernel_tx": false,
|
"kernel_tx": false,
|
||||||
@@ -90,17 +101,20 @@ icon: material/alert-decagram
|
|||||||
"min_version": "",
|
"min_version": "",
|
||||||
"max_version": "",
|
"max_version": "",
|
||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
"certificate": [],
|
"certificate": "",
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
|
"certificate_public_key_sha256": [],
|
||||||
"fragment": false,
|
"fragment": false,
|
||||||
"fragment_fallback_delay": "",
|
"fragment_fallback_delay": "",
|
||||||
"record_fragment": false,
|
"record_fragment": false,
|
||||||
"ech": {
|
"ech": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"pq_signature_schemes_enabled": false,
|
|
||||||
"dynamic_record_sizing_disabled": false,
|
|
||||||
"config": [],
|
"config": [],
|
||||||
"config_path": ""
|
"config_path": "",
|
||||||
|
|
||||||
|
// 废弃的
|
||||||
|
"pq_signature_schemes_enabled": false,
|
||||||
|
"dynamic_record_sizing_disabled": false
|
||||||
},
|
},
|
||||||
"utls": {
|
"utls": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
@@ -191,13 +205,27 @@ TLS 版本值:
|
|||||||
|
|
||||||
#### cipher_suites
|
#### cipher_suites
|
||||||
|
|
||||||
启用的 TLS 1.0-1.2密码套件的列表。列表的顺序被忽略。请注意,TLS 1.3 的密码套件是不可配置的。
|
启用的 TLS 1.0–1.2 密码套件列表。列表的顺序被忽略。请注意,TLS 1.3 的密码套件是不可配置的。
|
||||||
|
|
||||||
如果为空,则使用安全的默认列表。默认密码套件可能会随着时间的推移而改变。
|
如果为空,则使用安全的默认列表。默认密码套件可能会随着时间的推移而改变。
|
||||||
|
|
||||||
|
#### curve_preferences
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
支持的密钥交换机制集合。列表的顺序被忽略,密钥交换机制通过 Golang 的内部偏好顺序从此列表中选择。
|
||||||
|
|
||||||
|
可用值,同时也是默认列表:
|
||||||
|
|
||||||
|
* `P256`
|
||||||
|
* `P384`
|
||||||
|
* `P521`
|
||||||
|
* `X25519`
|
||||||
|
* `X25519MLKEM768`
|
||||||
|
|
||||||
#### certificate
|
#### certificate
|
||||||
|
|
||||||
服务器 PEM 证书行数组。
|
服务器证书链行数组,PEM 格式。
|
||||||
|
|
||||||
#### certificate_path
|
#### certificate_path
|
||||||
|
|
||||||
@@ -205,7 +233,25 @@ TLS 版本值:
|
|||||||
|
|
||||||
文件更改时将自动重新加载。
|
文件更改时将自动重新加载。
|
||||||
|
|
||||||
服务器 PEM 证书路径。
|
服务器证书链路径,PEM 格式。
|
||||||
|
|
||||||
|
#### certificate_public_key_sha256
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
==仅客户端==
|
||||||
|
|
||||||
|
服务器证书公钥的 SHA-256 哈希列表,base64 格式。
|
||||||
|
|
||||||
|
要生成证书公钥的 SHA-256 哈希,请使用以下命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 对于证书文件
|
||||||
|
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
|
||||||
|
# 对于远程服务器的证书
|
||||||
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
```
|
||||||
|
|
||||||
#### key
|
#### key
|
||||||
|
|
||||||
@@ -221,7 +267,68 @@ TLS 版本值:
|
|||||||
|
|
||||||
==仅服务器==
|
==仅服务器==
|
||||||
|
|
||||||
服务器 PEM 私钥路径。
|
!!! note ""
|
||||||
|
|
||||||
|
文件更改时将自动重新加载。
|
||||||
|
|
||||||
|
服务器私钥路径,PEM 格式。
|
||||||
|
|
||||||
|
#### client_authentication
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
要使用的客户端身份验证类型。
|
||||||
|
|
||||||
|
可用值:
|
||||||
|
|
||||||
|
* `no`(默认)
|
||||||
|
* `request`
|
||||||
|
* `require-any`
|
||||||
|
* `verify-if-given`
|
||||||
|
* `require-and-verify`
|
||||||
|
|
||||||
|
如果此选项设置为 `verify-if-given` 或 `require-and-verify`,
|
||||||
|
则需要 `client_certificate`、`client_certificate_path` 或 `client_certificate_public_key_sha256` 中的一个。
|
||||||
|
|
||||||
|
#### client_certificate
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
客户端证书链行数组,PEM 格式。
|
||||||
|
|
||||||
|
#### client_certificate_path
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
文件更改时将自动重新加载。
|
||||||
|
|
||||||
|
客户端证书链路径列表,PEM 格式。
|
||||||
|
|
||||||
|
#### client_certificate_public_key_sha256
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
客户端证书公钥的 SHA-256 哈希列表,base64 格式。
|
||||||
|
|
||||||
|
要生成证书公钥的 SHA-256 哈希,请使用以下命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 对于证书文件
|
||||||
|
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
|
||||||
|
# 对于远程服务器的证书
|
||||||
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
|
```
|
||||||
|
|
||||||
#### kernel_tx
|
#### kernel_tx
|
||||||
|
|
||||||
@@ -307,44 +414,11 @@ uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻
|
|||||||
|
|
||||||
默认使用 chrome 指纹。
|
默认使用 chrome 指纹。
|
||||||
|
|
||||||
## ECH 字段
|
### ECH 字段
|
||||||
|
|
||||||
ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其 ClientHello 的第一部分
|
ECH (Encrypted Client Hello) 是一个 TLS 扩展,它允许客户端加密其 ClientHello 的第一部分信息。
|
||||||
信息。
|
|
||||||
|
|
||||||
ECH 配置和密钥可以通过 `sing-box generate ech-keypair [--pq-signature-schemes-enabled]` 生成。
|
ECH 密钥和配置可以通过 `sing-box generate ech-keypair` 生成。
|
||||||
|
|
||||||
#### key
|
|
||||||
|
|
||||||
==仅服务器==
|
|
||||||
|
|
||||||
ECH PEM 密钥行数组
|
|
||||||
|
|
||||||
#### key_path
|
|
||||||
|
|
||||||
==仅服务器==
|
|
||||||
|
|
||||||
!!! note ""
|
|
||||||
|
|
||||||
文件更改时将自动重新加载。
|
|
||||||
|
|
||||||
ECH PEM 密钥路径
|
|
||||||
|
|
||||||
#### config
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
ECH PEM 配置行数组
|
|
||||||
|
|
||||||
如果为空,将尝试从 DNS 加载。
|
|
||||||
|
|
||||||
#### config_path
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
ECH PEM 配置路径
|
|
||||||
|
|
||||||
如果为空,将尝试从 DNS 加载。
|
|
||||||
|
|
||||||
#### pq_signature_schemes_enabled
|
#### pq_signature_schemes_enabled
|
||||||
|
|
||||||
@@ -354,8 +428,6 @@ ECH PEM 配置路径
|
|||||||
|
|
||||||
启用对后量子对等证书签名方案的支持。
|
启用对后量子对等证书签名方案的支持。
|
||||||
|
|
||||||
建议匹配 `sing-box generate ech-keypair` 的参数。
|
|
||||||
|
|
||||||
#### dynamic_record_sizing_disabled
|
#### dynamic_record_sizing_disabled
|
||||||
|
|
||||||
!!! failure "已在 sing-box 1.12.0 废弃"
|
!!! failure "已在 sing-box 1.12.0 废弃"
|
||||||
@@ -364,57 +436,91 @@ ECH PEM 配置路径
|
|||||||
|
|
||||||
禁用 TLS 记录的自适应大小调整。
|
禁用 TLS 记录的自适应大小调整。
|
||||||
|
|
||||||
如果为 true,则始终使用最大可能的 TLS 记录大小。
|
当为 true 时,总是使用最大可能的 TLS 记录大小。
|
||||||
如果为 false,则可能会调整 TLS 记录的大小以尝试改善延迟。
|
当为 false 时,可能会调整 TLS 记录的大小以尝试改善延迟。
|
||||||
|
|
||||||
#### tls_fragment
|
#### key
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
ECH 密钥行数组,PEM 格式。
|
||||||
|
|
||||||
|
#### key_path
|
||||||
|
|
||||||
|
==仅服务器==
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
文件更改时将自动重新加载。
|
||||||
|
|
||||||
|
ECH 密钥路径,PEM 格式。
|
||||||
|
|
||||||
|
#### config
|
||||||
|
|
||||||
|
==仅客户端==
|
||||||
|
|
||||||
|
ECH 配置行数组,PEM 格式。
|
||||||
|
|
||||||
|
如果为空,将尝试从 DNS 加载。
|
||||||
|
|
||||||
|
#### config_path
|
||||||
|
|
||||||
|
==仅客户端==
|
||||||
|
|
||||||
|
ECH 配置路径,PEM 格式。
|
||||||
|
|
||||||
|
如果为空,将尝试从 DNS 加载。
|
||||||
|
|
||||||
|
#### fragment
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
==仅客户端==
|
==仅客户端==
|
||||||
|
|
||||||
通过分段 TLS 握手数据包来绕过防火墙检测。
|
通过分段 TLS 握手数据包来绕过防火墙。
|
||||||
|
|
||||||
此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真的审查。
|
此功能旨在规避基于**明文数据包匹配**的简单防火墙,不应该用于规避真正的审查。
|
||||||
|
|
||||||
由于性能不佳,请首先尝试 `tls_record_fragment`,且仅应用于已知被阻止的服务器名称。
|
由于性能不佳,请首先尝试 `record_fragment`,且仅应用于已知被阻止的服务器名称。
|
||||||
|
|
||||||
在 Linux、Apple 平台和需要管理员权限的 Windows 系统上,可自动检测等待时间。
|
在 Linux、Apple 平台和(需要管理员权限的)Windows 系统上,
|
||||||
若无法自动检测,将回退使用 `tls_fragment_fallback_delay` 指定的固定等待时间。
|
可以自动检测等待时间。否则,将回退到
|
||||||
|
等待 `fragment_fallback_delay` 指定的固定时间。
|
||||||
|
|
||||||
此外,若实际等待时间小于 20 毫秒,同样会回退至固定等待时间模式,因为此时判定目标处于本地或透明代理之后。
|
此外,如果实际等待时间少于 20ms,也会回退到等待固定时间,
|
||||||
|
因为目标被认为是本地的或在透明代理后面。
|
||||||
|
|
||||||
#### tls_fragment_fallback_delay
|
#### fragment_fallback_delay
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
==仅客户端==
|
==仅客户端==
|
||||||
|
|
||||||
当 TLS 分片功能无法自动判定等待时间时使用的回退值。
|
当 TLS 分段无法自动确定等待时间时使用的回退值。
|
||||||
|
|
||||||
默认使用 `500ms`。
|
默认使用 `500ms`。
|
||||||
|
|
||||||
#### tls_record_fragment
|
#### record_fragment
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
通过分段 TLS 握手数据包到多个 TLS 记录来绕过防火墙检测。
|
==仅客户端==
|
||||||
|
|
||||||
|
将 TLS 握手分段为多个 TLS 记录以绕过防火墙。
|
||||||
|
|
||||||
### ACME 字段
|
### ACME 字段
|
||||||
|
|
||||||
#### domain
|
#### domain
|
||||||
|
|
||||||
一组域名。
|
域名列表。
|
||||||
|
|
||||||
默认禁用 ACME。
|
如果为空则禁用 ACME。
|
||||||
|
|
||||||
#### data_directory
|
#### data_directory
|
||||||
|
|
||||||
ACME 数据目录。
|
ACME 数据存储目录。
|
||||||
|
|
||||||
默认使用 `$XDG_DATA_HOME/certmagic|$HOME/.local/share/certmagic`。
|
如果为空则使用 `$XDG_DATA_HOME/certmagic|$HOME/.local/share/certmagic`。
|
||||||
|
|
||||||
#### default_server_name
|
#### default_server_name
|
||||||
|
|
||||||
@@ -452,12 +558,11 @@ ACME 数据目录。
|
|||||||
|
|
||||||
#### external_account
|
#### external_account
|
||||||
|
|
||||||
EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到其他已知帐户所需的信息由 CA。
|
EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到 CA 已知的其他帐户所需的信息。
|
||||||
|
|
||||||
外部帐户绑定“用于将 ACME 帐户与非 ACME 系统中的现有帐户相关联,例如 CA 客户数据库。
|
外部帐户绑定"用于将 ACME 帐户与非 ACME 系统中的现有帐户相关联,例如 CA 客户数据库。
|
||||||
|
|
||||||
为了启用 ACME 帐户绑定,运行 ACME 服务器的 CA 需要向 ACME 客户端提供 MAC 密钥和密钥标识符,使用 ACME 之外的一些机制。
|
为了启用 ACME 帐户绑定,运行 ACME 服务器的 CA 需要使用 ACME 之外的某种机制向 ACME 客户端提供 MAC 密钥和密钥标识符。§7.3.4
|
||||||
§7.3.4
|
|
||||||
|
|
||||||
#### external_account.key_id
|
#### external_account.key_id
|
||||||
|
|
||||||
@@ -507,6 +612,8 @@ ACME DNS01 验证字段。如果配置,将禁用其他验证方法。
|
|||||||
|
|
||||||
#### max_time_difference
|
#### max_time_difference
|
||||||
|
|
||||||
服务器与和客户端之间允许的最大时间差。
|
==仅服务器==
|
||||||
|
|
||||||
默认禁用检查。
|
服务器和客户端之间的最大时间差。
|
||||||
|
|
||||||
|
如果为空则禁用检查。
|
||||||
|
|||||||
180
option/tls.go
180
option/tls.go
@@ -1,24 +1,80 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
import "github.com/sagernet/sing/common/json/badoption"
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
)
|
||||||
|
|
||||||
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 badoption.Listable[string] `json:"alpn,omitempty"`
|
ALPN badoption.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 badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
||||||
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
CurvePreferences badoption.Listable[CurvePreference] `json:"curve_preferences,omitempty"`
|
||||||
CertificatePath string `json:"certificate_path,omitempty"`
|
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
||||||
Key badoption.Listable[string] `json:"key,omitempty"`
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
KeyPath string `json:"key_path,omitempty"`
|
ClientAuthentication ClientAuthType `json:"client_authentication,omitempty"`
|
||||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
ClientCertificate badoption.Listable[string] `json:"client_certificate,omitempty"`
|
||||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
ClientCertificatePath badoption.Listable[string] `json:"client_certificate_path,omitempty"`
|
||||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
ClientCertificatePublicKeySHA256 badoption.Listable[[]byte] `json:"client_certificate_public_key_sha256,omitempty"`
|
||||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
Key badoption.Listable[string] `json:"key,omitempty"`
|
||||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
KeyPath string `json:"key_path,omitempty"`
|
||||||
|
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||||
|
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||||
|
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||||
|
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||||
|
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientAuthType tls.ClientAuthType
|
||||||
|
|
||||||
|
func (t ClientAuthType) MarshalJSON() ([]byte, error) {
|
||||||
|
var stringValue string
|
||||||
|
switch t {
|
||||||
|
case ClientAuthType(tls.NoClientCert):
|
||||||
|
stringValue = "no"
|
||||||
|
case ClientAuthType(tls.RequestClientCert):
|
||||||
|
stringValue = "request"
|
||||||
|
case ClientAuthType(tls.RequireAnyClientCert):
|
||||||
|
stringValue = "require-any"
|
||||||
|
case ClientAuthType(tls.VerifyClientCertIfGiven):
|
||||||
|
stringValue = "verify-if-given"
|
||||||
|
case ClientAuthType(tls.RequireAndVerifyClientCert):
|
||||||
|
stringValue = "require-and-verify"
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown client authentication type: ", int(t))
|
||||||
|
}
|
||||||
|
return json.Marshal(stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ClientAuthType) UnmarshalJSON(data []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(data, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch stringValue {
|
||||||
|
case "no":
|
||||||
|
*t = ClientAuthType(tls.NoClientCert)
|
||||||
|
case "request":
|
||||||
|
*t = ClientAuthType(tls.RequestClientCert)
|
||||||
|
case "require-any":
|
||||||
|
*t = ClientAuthType(tls.RequireAnyClientCert)
|
||||||
|
case "verify-if-given":
|
||||||
|
*t = ClientAuthType(tls.VerifyClientCertIfGiven)
|
||||||
|
case "require-and-verify":
|
||||||
|
*t = ClientAuthType(tls.RequireAndVerifyClientCert)
|
||||||
|
default:
|
||||||
|
return E.New("unknown client authentication type: ", stringValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type InboundTLSOptionsContainer struct {
|
type InboundTLSOptionsContainer struct {
|
||||||
@@ -39,24 +95,26 @@ func (o *InboundTLSOptionsContainer) ReplaceInboundTLSOptions(options *InboundTL
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 badoption.Listable[string] `json:"alpn,omitempty"`
|
ALPN badoption.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 badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
|
||||||
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
CurvePreferences badoption.Listable[CurvePreference] `json:"curve_preferences,omitempty"`
|
||||||
CertificatePath string `json:"certificate_path,omitempty"`
|
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
|
||||||
Fragment bool `json:"fragment,omitempty"`
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
|
CertificatePublicKeySHA256 badoption.Listable[[]byte] `json:"certificate_public_key_sha256,omitempty"`
|
||||||
RecordFragment bool `json:"record_fragment,omitempty"`
|
Fragment bool `json:"fragment,omitempty"`
|
||||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
|
||||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
RecordFragment bool `json:"record_fragment,omitempty"`
|
||||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
KernelTx bool `json:"kernel_tx,omitempty"`
|
||||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
KernelRx bool `json:"kernel_rx,omitempty"`
|
||||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||||
|
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||||
|
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OutboundTLSOptionsContainer struct {
|
type OutboundTLSOptionsContainer struct {
|
||||||
@@ -76,6 +134,58 @@ func (o *OutboundTLSOptionsContainer) ReplaceOutboundTLSOptions(options *Outboun
|
|||||||
o.TLS = options
|
o.TLS = options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CurvePreference tls.CurveID
|
||||||
|
|
||||||
|
const (
|
||||||
|
CurveP256 = 23
|
||||||
|
CurveP384 = 24
|
||||||
|
CurveP521 = 25
|
||||||
|
X25519 = 29
|
||||||
|
X25519MLKEM768 = 4588
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c CurvePreference) MarshalJSON() ([]byte, error) {
|
||||||
|
var stringValue string
|
||||||
|
switch c {
|
||||||
|
case CurvePreference(CurveP256):
|
||||||
|
stringValue = "P256"
|
||||||
|
case CurvePreference(CurveP384):
|
||||||
|
stringValue = "P384"
|
||||||
|
case CurvePreference(CurveP521):
|
||||||
|
stringValue = "P521"
|
||||||
|
case CurvePreference(X25519):
|
||||||
|
stringValue = "X25519"
|
||||||
|
case CurvePreference(X25519MLKEM768):
|
||||||
|
stringValue = "X25519MLKEM768"
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown curve id: ", int(c))
|
||||||
|
}
|
||||||
|
return json.Marshal(stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurvePreference) UnmarshalJSON(data []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(data, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch strings.ToUpper(stringValue) {
|
||||||
|
case "P256":
|
||||||
|
*c = CurvePreference(CurveP256)
|
||||||
|
case "P384":
|
||||||
|
*c = CurvePreference(CurveP384)
|
||||||
|
case "P521":
|
||||||
|
*c = CurvePreference(CurveP521)
|
||||||
|
case "X25519":
|
||||||
|
*c = CurvePreference(X25519)
|
||||||
|
case "X25519MLKEM768":
|
||||||
|
*c = CurvePreference(X25519MLKEM768)
|
||||||
|
default:
|
||||||
|
return E.New("unknown curve name: ", stringValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type InboundRealityOptions struct {
|
type InboundRealityOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Handshake InboundRealityHandshakeOptions `json:"handshake,omitempty"`
|
Handshake InboundRealityHandshakeOptions `json:"handshake,omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user