validate client certificate of frpc for tcpmux

This commit is contained in:
Moritz Habegger 2023-11-13 13:53:11 +01:00
parent 184223cb2f
commit 2b91ac4d18
No known key found for this signature in database
GPG Key ID: 1627E2B493D230F3
4 changed files with 46 additions and 0 deletions

View File

@ -649,6 +649,7 @@ transport.tls.force = true
transport.tls.certFile = "certificate.crt"
transport.tls.keyFile = "certificate.key"
transport.tls.trustedCaFile = "ca.crt"
transport.tls.clientCertSubjectRegex = "CN=client.com(.+)"
```
You will need **a root CA cert** and **at least one SSL/TLS certificate**. It **can** be self-signed or regular (such as Let's Encrypt or another SSL/TLS certificate provider).

View File

@ -80,6 +80,9 @@ type TLSConfig struct {
// ServerName specifies the custom server name of tls certificate. By
// default, server name if same to ServerAddr.
ServerName string `json:"serverName,omitempty"`
// ClientCertificateSubjectRegex specifies the regex that is used to validate the client certificate subject.
// If it's not set, the validation of the subject is disabled.
ClientCertificateSubjectRegex string `json:"clientCertSubjectRegex,omitempty"`
}
type LogConfig struct {

View File

@ -17,7 +17,10 @@ package net
import (
"crypto/tls"
"fmt"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/util/log"
"net"
"regexp"
"time"
libnet "github.com/fatedier/golib/net"
@ -55,3 +58,35 @@ func CheckAndEnableTLSServerConnWithTimeout(
}
return
}
func IsClientCertificateSubjectValid(c net.Conn, tlsConfig v1.TLSServerConfig) bool {
subjectRegex := tlsConfig.ClientCertificateSubjectRegex
regex, err := regexp.Compile(subjectRegex)
if err != nil {
log.Trace("Client certificate subject validation is disabled")
return true
}
tlsConn, ok := c.(*tls.Conn)
if !ok {
log.Warn("Skip client certificate subject validation because its not a tls connection")
return true
}
state := tlsConn.ConnectionState()
log.Trace("Validating client certificate subject using regex: %v", subjectRegex)
if len(state.PeerCertificates) == 0 {
log.Warn("No client certificates found in TLS connection, the verification was probably called to early.")
return false
}
for _, v := range state.PeerCertificates {
subject := fmt.Sprintf("%v", v.Subject)
if !regex.MatchString(subject) {
log.Warn("Client certificate subject %v doesn't match regex %v", v.Subject, subjectRegex)
return false
}
log.Trace("Client certificate subject is valid")
}
return true
}

View File

@ -484,6 +484,13 @@ func (svr *Service) HandleListener(l net.Listener) {
session.Close()
return
}
// Has to be called after session.AcceptStream() so that the client certificates are available
if !utilnet.IsClientCertificateSubjectValid(c, svr.cfg.Transport.TLS) {
session.Close()
return
}
go svr.handleConnection(ctx, stream)
}
} else {