mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-03 02:08:45 +00:00
Feat: Add mTLS
This commit is contained in:
@@ -603,6 +603,10 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
certificate.Usage = tls.Certificate_AUTHORITY_VERIFY
|
||||
case "issue":
|
||||
certificate.Usage = tls.Certificate_AUTHORITY_ISSUE
|
||||
case "client-cert":
|
||||
certificate.Usage = tls.Certificate_MTLS_CLIENT_CERT
|
||||
case "client-ca":
|
||||
certificate.Usage = tls.Certificate_MTLS_CLIENT_CA
|
||||
default:
|
||||
certificate.Usage = tls.Certificate_ENCIPHERMENT
|
||||
}
|
||||
@@ -653,6 +657,7 @@ type TLSConfig struct {
|
||||
ECHServerKeys string `json:"echServerKeys"`
|
||||
ECHConfigList string `json:"echConfigList"`
|
||||
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||
ClientAuth string `json:"clientAuth"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -741,6 +746,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
config.EchSocketSettings = ss
|
||||
}
|
||||
|
||||
config.ClientAuth = c.ClientAuth
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,27 @@ func (c *Config) getCustomCA() []*Certificate {
|
||||
return certs
|
||||
}
|
||||
|
||||
func (c *Config) getClientCert() []*Certificate {
|
||||
certs := make([]*Certificate, 0, len(c.Certificate))
|
||||
for _, certificate := range c.Certificate {
|
||||
if certificate.Usage == Certificate_MTLS_CLIENT_CERT {
|
||||
certs = append(certs, certificate)
|
||||
setupHotReload(certificate)
|
||||
}
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
func (c *Config) getClientCA() []*Certificate {
|
||||
certs := make([]*Certificate, 0, len(c.Certificate))
|
||||
for _, certificate := range c.Certificate {
|
||||
if certificate.Usage == Certificate_MTLS_CLIENT_CA {
|
||||
certs = append(certs, certificate)
|
||||
}
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
var access sync.RWMutex
|
||||
|
||||
@@ -234,6 +255,19 @@ func (c *Config) getCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, e
|
||||
return defaultCert, nil
|
||||
}
|
||||
|
||||
func (c *Config) getClientCertificate(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
for _, cert := range c.Certificate {
|
||||
parsed := cert.getX509KeyPair()
|
||||
if cert.Usage != Certificate_MTLS_CLIENT_CERT || parsed == nil {
|
||||
continue
|
||||
}
|
||||
if err := cri.SupportsCertificate(parsed); err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
}
|
||||
return nil, errNoCertificates
|
||||
}
|
||||
|
||||
func (c *Config) parseServerName() string {
|
||||
if IsFromMitm(c.ServerName) {
|
||||
return ""
|
||||
@@ -376,6 +410,18 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
}
|
||||
config.GetCertificate = c.getCertificate
|
||||
}
|
||||
if len(c.getClientCert()) > 0 {
|
||||
config.GetClientCertificate = c.getClientCertificate
|
||||
}
|
||||
if clientCA := c.getClientCA(); len(clientCA) > 0 {
|
||||
clientCAPool := x509.NewCertPool()
|
||||
for _, cert := range clientCA {
|
||||
if !clientCAPool.AppendCertsFromPEM(cert.Certificate) {
|
||||
errors.LogError(context.Background(), errors.New("failed to append client CA certificate"))
|
||||
}
|
||||
}
|
||||
config.ClientCAs = clientCAPool
|
||||
}
|
||||
|
||||
if sn := c.parseServerName(); len(sn) > 0 {
|
||||
config.ServerName = sn
|
||||
@@ -438,6 +484,11 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||
}
|
||||
}
|
||||
|
||||
config.ClientAuth = ParseClientAuth(c.ClientAuth)
|
||||
if config.ClientAuth >= tls.VerifyClientCertIfGiven && config.ClientCAs == nil {
|
||||
errors.LogWarning(context.Background(), "clientAuth is set to ", c.ClientAuth, " but no client CA is provided")
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -483,17 +534,17 @@ func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func ParseCurveName(curveNames []string) []tls.CurveID {
|
||||
curveMap := map[string]tls.CurveID{
|
||||
"curvep256": tls.CurveP256,
|
||||
"curvep384": tls.CurveP384,
|
||||
"curvep521": tls.CurveP521,
|
||||
"x25519": tls.X25519,
|
||||
"x25519mlkem768": tls.X25519MLKEM768,
|
||||
"secp256r1mlkem768": tls.SecP256r1MLKEM768,
|
||||
"secp384r1mlkem1024": tls.SecP384r1MLKEM1024,
|
||||
}
|
||||
var curveMap = map[string]tls.CurveID{
|
||||
"curvep256": tls.CurveP256,
|
||||
"curvep384": tls.CurveP384,
|
||||
"curvep521": tls.CurveP521,
|
||||
"x25519": tls.X25519,
|
||||
"x25519mlkem768": tls.X25519MLKEM768,
|
||||
"secp256r1mlkem768": tls.SecP256r1MLKEM768,
|
||||
"secp384r1mlkem1024": tls.SecP384r1MLKEM1024,
|
||||
}
|
||||
|
||||
func ParseCurveName(curveNames []string) []tls.CurveID {
|
||||
var curveIDs []tls.CurveID
|
||||
for _, name := range curveNames {
|
||||
if curveID, ok := curveMap[strings.ToLower(name)]; ok {
|
||||
@@ -505,6 +556,25 @@ func ParseCurveName(curveNames []string) []tls.CurveID {
|
||||
return curveIDs
|
||||
}
|
||||
|
||||
var clientAuthMap = map[string]tls.ClientAuthType{
|
||||
"noclientcert": tls.NoClientCert,
|
||||
"requestclientcert": tls.RequestClientCert,
|
||||
"requireanyclientcert": tls.RequireAnyClientCert,
|
||||
"verifyclientcertifgiven": tls.VerifyClientCertIfGiven,
|
||||
"requireandverifyclientcert": tls.RequireAndVerifyClientCert,
|
||||
}
|
||||
|
||||
func ParseClientAuth(clientAuth string) tls.ClientAuthType {
|
||||
if clientAuth == "" {
|
||||
return tls.NoClientCert
|
||||
}
|
||||
if clientAuthType, ok := clientAuthMap[strings.ToLower(clientAuth)]; ok {
|
||||
return clientAuthType
|
||||
}
|
||||
errors.LogWarning(context.Background(), "unsupported clientAuth: "+clientAuth)
|
||||
return tls.NoClientCert
|
||||
}
|
||||
|
||||
func IsFromMitm(str string) bool {
|
||||
return strings.ToLower(str) == "frommitm"
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ const (
|
||||
Certificate_ENCIPHERMENT Certificate_Usage = 0
|
||||
Certificate_AUTHORITY_VERIFY Certificate_Usage = 1
|
||||
Certificate_AUTHORITY_ISSUE Certificate_Usage = 2
|
||||
Certificate_MTLS_CLIENT_CERT Certificate_Usage = 3
|
||||
Certificate_MTLS_CLIENT_CA Certificate_Usage = 4
|
||||
)
|
||||
|
||||
// Enum value maps for Certificate_Usage.
|
||||
@@ -36,11 +38,15 @@ var (
|
||||
0: "ENCIPHERMENT",
|
||||
1: "AUTHORITY_VERIFY",
|
||||
2: "AUTHORITY_ISSUE",
|
||||
3: "MTLS_CLIENT_CERT",
|
||||
4: "MTLS_CLIENT_CA",
|
||||
}
|
||||
Certificate_Usage_value = map[string]int32{
|
||||
"ENCIPHERMENT": 0,
|
||||
"AUTHORITY_VERIFY": 1,
|
||||
"AUTHORITY_ISSUE": 2,
|
||||
"MTLS_CLIENT_CERT": 3,
|
||||
"MTLS_CLIENT_CA": 4,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -231,6 +237,7 @@ type Config struct {
|
||||
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||
EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"`
|
||||
PinnedPeerCertSha256 [][]byte `protobuf:"bytes,22,rep,name=pinned_peer_cert_sha256,json=pinnedPeerCertSha256,proto3" json:"pinned_peer_cert_sha256,omitempty"`
|
||||
ClientAuth string `protobuf:"bytes,23,opt,name=client_auth,json=clientAuth,proto3" json:"client_auth,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -384,11 +391,18 @@ func (x *Config) GetPinnedPeerCertSha256() [][]byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetClientAuth() string {
|
||||
if x != nil {
|
||||
return x.ClientAuth
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_transport_internet_tls_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"#transport/internet/tls/config.proto\x12\x1bxray.transport.internet.tls\x1a\x1ftransport/internet/config.proto\"\xe4\x03\n" +
|
||||
"#transport/internet/tls/config.proto\x12\x1bxray.transport.internet.tls\x1a\x1ftransport/internet/config.proto\"\x8e\x04\n" +
|
||||
"\vCertificate\x12 \n" +
|
||||
"\vcertificate\x18\x01 \x01(\fR\vcertificate\x12\x10\n" +
|
||||
"\x03key\x18\x02 \x01(\fR\x03key\x12D\n" +
|
||||
@@ -403,11 +417,13 @@ const file_transport_internet_tls_config_proto_rawDesc = "" +
|
||||
"\tocsp_data\x18\n" +
|
||||
" \x01(\fR\bocspData\x12\x1f\n" +
|
||||
"\vlast_reload\x18\v \x01(\x03R\n" +
|
||||
"lastReload\"D\n" +
|
||||
"lastReload\"n\n" +
|
||||
"\x05Usage\x12\x10\n" +
|
||||
"\fENCIPHERMENT\x10\x00\x12\x14\n" +
|
||||
"\x10AUTHORITY_VERIFY\x10\x01\x12\x13\n" +
|
||||
"\x0fAUTHORITY_ISSUE\x10\x02\"\xa6\x06\n" +
|
||||
"\x0fAUTHORITY_ISSUE\x10\x02\x12\x14\n" +
|
||||
"\x10MTLS_CLIENT_CERT\x10\x03\x12\x12\n" +
|
||||
"\x0eMTLS_CLIENT_CA\x10\x04\"\xc7\x06\n" +
|
||||
"\x06Config\x12J\n" +
|
||||
"\vcertificate\x18\x02 \x03(\v2(.xray.transport.internet.tls.CertificateR\vcertificate\x12\x1f\n" +
|
||||
"\vserver_name\x18\x03 \x01(\tR\n" +
|
||||
@@ -428,7 +444,9 @@ const file_transport_internet_tls_config_proto_rawDesc = "" +
|
||||
"\x0fech_server_keys\x18\x12 \x01(\fR\rechServerKeys\x12&\n" +
|
||||
"\x0fech_config_list\x18\x13 \x01(\tR\rechConfigList\x12U\n" +
|
||||
"\x13ech_socket_settings\x18\x15 \x01(\v2%.xray.transport.internet.SocketConfigR\x11echSocketSettings\x125\n" +
|
||||
"\x17pinned_peer_cert_sha256\x18\x16 \x03(\fR\x14pinnedPeerCertSha256Bs\n" +
|
||||
"\x17pinned_peer_cert_sha256\x18\x16 \x03(\fR\x14pinnedPeerCertSha256\x12\x1f\n" +
|
||||
"\vclient_auth\x18\x17 \x01(\tR\n" +
|
||||
"clientAuthBs\n" +
|
||||
"\x1fcom.xray.transport.internet.tlsP\x01Z0github.com/xtls/xray-core/transport/internet/tls\xaa\x02\x1bXray.Transport.Internet.Tlsb\x06proto3"
|
||||
|
||||
var (
|
||||
|
||||
@@ -19,6 +19,8 @@ message Certificate {
|
||||
ENCIPHERMENT = 0;
|
||||
AUTHORITY_VERIFY = 1;
|
||||
AUTHORITY_ISSUE = 2;
|
||||
MTLS_CLIENT_CERT = 3;
|
||||
MTLS_CLIENT_CA = 4;
|
||||
}
|
||||
|
||||
Usage usage = 3;
|
||||
@@ -91,4 +93,6 @@ message Config {
|
||||
SocketConfig ech_socket_settings = 21;
|
||||
|
||||
repeated bytes pinned_peer_cert_sha256 = 22;
|
||||
|
||||
string client_auth = 23;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user