Skip to content

Commit

Permalink
server: support check the "CommanName" of tls-cert for status-port(ht…
Browse files Browse the repository at this point in the history
…tp/grpc) (pingcap#15137)
  • Loading branch information
lysu committed Mar 5, 2020
1 parent 819603f commit fa8f260
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 11 deletions.
15 changes: 8 additions & 7 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,14 @@ type Log struct {

// Security is the security section of the config.
type Security struct {
SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"`
SSLCA string `toml:"ssl-ca" json:"ssl-ca"`
SSLCert string `toml:"ssl-cert" json:"ssl-cert"`
SSLKey string `toml:"ssl-key" json:"ssl-key"`
ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"`
ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"`
ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"`
SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"`
SSLCA string `toml:"ssl-ca" json:"ssl-ca"`
SSLCert string `toml:"ssl-cert" json:"ssl-cert"`
SSLKey string `toml:"ssl-key" json:"ssl-key"`
ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"`
ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"`
ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"`
ClusterVerifyCN []string `toml:"cluster-verify-cn" json:"cluster-verify-cn"`
}

// The ErrConfigValidationFailed error is used so that external callers can do a type assertion
Expand Down
16 changes: 16 additions & 0 deletions server/http_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ package server

import (
"bytes"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"database/sql"
"encoding/base64"
"encoding/hex"
Expand Down Expand Up @@ -879,3 +882,16 @@ func (ts *HTTPHandlerTestSuite) TestAllServerInfo(c *C) {
c.Assert(serverInfo.GitHash, Equals, printer.TiDBGitHash)
c.Assert(serverInfo.ID, Equals, ddl.GetID())
}

func (ts *HTTPHandlerTestSuite) TestCheckCN(c *C) {
s := &Server{cfg: &config.Config{Security: config.Security{ClusterVerifyCN: []string{"a ", "b", "c"}}}}
tlsConfig := &tls.Config{}
s.setCNChecker(tlsConfig)
c.Assert(tlsConfig.VerifyPeerCertificate, NotNil)
err := tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "a"}}}})
c.Assert(err, IsNil)
err = tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "b"}}}})
c.Assert(err, IsNil)
err = tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "d"}}}})
c.Assert(err, NotNil)
}
43 changes: 39 additions & 4 deletions server/http_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"archive/zip"
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"net"
Expand Down Expand Up @@ -253,15 +255,48 @@ func (s *Server) startHTTPServer() {
logutil.Logger(context.Background()).Info("for status and metrics report", zap.String("listening on addr", addr))
s.statusServer = &http.Server{Addr: addr, Handler: CorsHandler{handler: serverMux, cfg: s.cfg}}

ln, err := net.Listen("tcp", addr)
if err != nil {
logutil.Logger(context.Background()).Info("listen failed", zap.Error(err))
return
}

if len(s.cfg.Security.ClusterSSLCA) != 0 {
err = s.statusServer.ListenAndServeTLS(s.cfg.Security.ClusterSSLCert, s.cfg.Security.ClusterSSLKey)
} else {
err = s.statusServer.ListenAndServe()
tlsConfig, err := s.cfg.Security.ToTLSConfig()
if err != nil {
logutil.Logger(context.Background()).Error("invalid TLS config", zap.Error(err))
return
}
tlsConfig = s.setCNChecker(tlsConfig)
ln = tls.NewListener(ln, tlsConfig)
}

err = s.statusServer.Serve(ln)
if err != nil {
logutil.Logger(context.Background()).Info("listen failed", zap.Error(err))
logutil.Logger(context.Background()).Info("serve status port failed", zap.Error(err))
}
}

func (s *Server) setCNChecker(tlsConfig *tls.Config) *tls.Config {
if tlsConfig != nil && len(s.cfg.Security.ClusterVerifyCN) != 0 {
checkCN := make(map[string]struct{})
for _, cn := range s.cfg.Security.ClusterVerifyCN {
cn = strings.TrimSpace(cn)
checkCN[cn] = struct{}{}
}
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, chain := range verifiedChains {
if len(chain) != 0 {
if _, match := checkCN[chain[0].Subject.CommonName]; match {
return nil
}
}
}
return errors.Errorf("client certificate authentication failed. The Common Name from the client certificate was not found in the configuration cluster-verify-cn with value: %s", s.cfg.Security.ClusterVerifyCN)
}
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
return tlsConfig
}

// status of TiDB.
Expand Down
51 changes: 51 additions & 0 deletions server/tidb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net/http"
"os"
"path/filepath"
"time"

"github.com/go-sql-driver/mysql"
Expand Down Expand Up @@ -157,6 +160,54 @@ func (ts *TidbTestSuite) TestStatusAPI(c *C) {
runTestStatusAPI(c)
}

func (ts *TidbTestSuite) TestStatusAPIWithTLSCNCheck(c *C) {
c.Skip("need add ca-tidb-test-1.crt to OS")
root := filepath.Join(os.Getenv("GOPATH"), "/src/github.com/pingcap/tidb")
ca := filepath.Join(root, "/tests/cncheckcert/ca-tidb-test-1.crt")
statusURL := fmt.Sprintf("%s://localhost:%d%s", "https", 4100, "/status")

cfg := config.NewConfig()
cfg.Status.StatusPort = 4100
cfg.Security.ClusterSSLCA = ca
cfg.Security.ClusterSSLCert = filepath.Join(root, "/tests/cncheckcert/server-cert.pem")
cfg.Security.ClusterSSLKey = filepath.Join(root, "/tests/cncheckcert/server-key.pem")
cfg.Security.ClusterVerifyCN = []string{"tidb-client-2"}
server, err := NewServer(cfg, ts.tidbdrv)
c.Assert(err, IsNil)
go server.Run()
time.Sleep(time.Millisecond * 100)

hc := newTLSHttpClient(c, ca,
filepath.Join(root, "/tests/cncheckcert/client-cert-1.pem"),
filepath.Join(root, "/tests/cncheckcert/client-key-1.pem"),
)
_, err = hc.Get(statusURL)
c.Assert(err, NotNil)

hc = newTLSHttpClient(c, ca,
filepath.Join(root, "/tests/cncheckcert/client-cert-2.pem"),
filepath.Join(root, "/tests/cncheckcert/client-key-2.pem"),
)
_, err = hc.Get(statusURL)
c.Assert(err, IsNil)
}

func newTLSHttpClient(c *C, caFile, certFile, keyFile string) *http.Client {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
c.Assert(err, IsNil)
caCert, err := ioutil.ReadFile(caFile)
c.Assert(err, IsNil)
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
InsecureSkipVerify: true,
}
tlsConfig.BuildNameToCertificate()
return &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
}

func (ts *TidbTestSuite) TestMultiStatements(c *C) {
c.Parallel()
runTestMultiStatements(c)
Expand Down
9 changes: 9 additions & 0 deletions tests/cncheckcert/ca-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBQwIBAAJDAL6zaGJBgNTBNhtGTfSXnbzoZa1LlfCpFlIwtXc1YBBtl9IIzRI5
j2Cyd/DTYjO0vniHGIHv7H64336szhSGOcHV5QIDAQABAkIpDH9MnyL3KPvXlSOU
oco/bprsWZfV7N+0I238Ug3ym1QyCK3ue8m/bJveL9AXCwJWMLdvHQoiyCFnaQ6f
Ay2hGYUCIgD4YcAP4aIJL1H9vo0vmXQzFZJzklyOUcmRm00Ulvie5dsCIgDEjMGk
QZoiR9ammPSc1IKF/c6THtd1sA0rW9Vh0sCBXz8CIgCMkq4jjtyo/BoYVRcM4HmO
S+A1/pjZh1pgSRfH1mXhcE8CITRT5Rn9/TMzPQqNnlJCoZ1avSyeAW7ruBXbFSw+
F9JZsQIhMvQjbm0ygrSwOlvRhWOzAtUKSxcs7JKfxgt9/5/XIV4C
-----END RSA PRIVATE KEY-----
10 changes: 10 additions & 0 deletions tests/cncheckcert/ca-tidb-test-1.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBXzCCAQegAwIBAgIBADANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAUMRIwEAYDVQQD
EwlUaURCIENBIDUwXjANBgkqhkiG9w0BAQEFAANNADBKAkMAvrNoYkGA1ME2G0ZN
9JedvOhlrUuV8KkWUjC1dzVgEG2X0gjNEjmPYLJ38NNiM7S+eIcYge/sfrjffqzO
FIY5wdXlAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwICpDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAND
AKfu05xLZQ6PVMyYPjpo/RI/WchaLFRbBZKpdu+/QMaxreH7/xAqDfnslTVblWLo
uGxIrIOBL/1jR8CoAhBB1VAoJw==
-----END CERTIFICATE-----
10 changes: 10 additions & 0 deletions tests/cncheckcert/client-cert-1.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
Ew10aWRiLWNsaWVudC0xMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDAMMfWMiDqdQE
xy0kE/W+V+OmYZGit7iGk+BzMe6G0w9fFmJt8ajwmHp5dMT5AmJpNF/i089Wej3K
SdEkWhWa96BBOwIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
QwAnXNy43kWtlyelP1M5s0h4KNkmYBe6McaGV5nvhD/dwFfviY1dMRQ4ChgBgc5y
vv2r0fnRd2XF+81EmNGQk79Xg9A=
-----END CERTIFICATE-----
10 changes: 10 additions & 0 deletions tests/cncheckcert/client-cert-2.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
Ew10aWRiLWNsaWVudC0yMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDANuIi1RlJKHF
KJg5D/fblj8FfRaay9CLJOhDZkabJTuMHerlpY0vf8wsY15khaJU/dRiJeAT+lvg
V4CluJLSSLuIEQIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
QwABsC2+tggurK8iUDmuaEKOqGnQdHrMMohDnSjmuS+X9xYcLlix7Haf53fBhs8B
kvUiPFk8QEiOi7xttaMHKeDLnRE=
-----END CERTIFICATE-----
9 changes: 9 additions & 0 deletions tests/cncheckcert/client-key-1.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBRQIBAAJDAMMfWMiDqdQExy0kE/W+V+OmYZGit7iGk+BzMe6G0w9fFmJt8ajw
mHp5dMT5AmJpNF/i089Wej3KSdEkWhWa96BBOwIDAQABAkJ4ps1zT1aX70xpsUFW
Vxhpf9wc/Yy04SJXS2O4pk2j15seJ9chhaolp26mVm1w/jyV/k6TwuKo0pM+F4pI
ePJNQQECIgDLWTCV6E1KGmprVnm/WRqUTewcNzfsXFHUlPSuIhTxUZsCIgD1pOfU
DpzwOoZb7XUogqkLKleaWWm22ffHuvuT3+ozGOECIgDDDS9Ea8pPTV1MzmsDtyV+
oevb+L9ksf0wKx00Nq7d9wcCIgCX/++4B0bTW9OSBLCvXZKOpyfICbXhgKTTQX+0
9CRuc+ECIgCqTejjhRnhz3rYD0wWqRRsXT/144RmiIpBslRp+HJ+5dg=
-----END RSA PRIVATE KEY-----
9 changes: 9 additions & 0 deletions tests/cncheckcert/client-key-2.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBQgIBAAJDANuIi1RlJKHFKJg5D/fblj8FfRaay9CLJOhDZkabJTuMHerlpY0v
f8wsY15khaJU/dRiJeAT+lvgV4CluJLSSLuIEQIDAQABAkJw/adQqbof9Pz+1CfO
11tOVoHaV5PdYzB8xuvmHUYdjvCG4WJcfFFkVipqE8VhJsS2Onzb7BFJ7gOqxCV6
9+FoYgECIgD1IP2jXxqOJgI3p78YaCJnBlRUMIu/+g+MmGPcLqg8taECIgDlRPYh
m0kOwtmzTtTI5sOLDgDPew16p8jyBvBPxsk53HECIW6W/rc5DeL5tOBlFqqtOHAg
g+Ujrbjj2SYGDm9kwVP6YQIhPd/xqTo2alRt2nWA+cNFrMaXs2cbSSn1ElSLEIyu
i/4RAiFcI5yrNMt2XwyKo/A+hzFvJeuQ6xTs9I1KMYbsngXJ58k=
-----END RSA PRIVATE KEY-----
10 changes: 10 additions & 0 deletions tests/cncheckcert/server-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
Ew10aWRiLXNlcnZlci0zMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDAM+ez7yZLKp9
spGiUqqInrH8iTZ8JPa3C8K8bYw/Fc/W028sVvf6+VLC1ZP0cJX4Lx3iyyyIfzjG
KV4+MVcBOlfn4wIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
QwCUAO+eDYvVXBIqpNWr/h513TTSzZa+ELNOrYNGr2MgQ6j8TW0+J+J4JGgj0Wm7
1haBMbGShAHlXYK81ZShtnbPpFI=
-----END CERTIFICATE-----
9 changes: 9 additions & 0 deletions tests/cncheckcert/server-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBRAIBAAJDAM+ez7yZLKp9spGiUqqInrH8iTZ8JPa3C8K8bYw/Fc/W028sVvf6
+VLC1ZP0cJX4Lx3iyyyIfzjGKV4+MVcBOlfn4wIDAQABAkJ5hiNh6OZUBK74v2JT
nxQEaiSGV7PrFMk1esVESciilsKsRJLcyzxbpANooQQefOswHzRR/YyMmy2HiW++
H08ENMECIgDR6EfM7CKKJGj2jz3b+2bUoBpdpuPtD9kPo4u6ubVnNz8CIgD9Nfhq
Viiwl2IentOXOWvIasGQdQXz1fWHGLP5nA2Xql0CIgCXcSGUXG2TAy/ja3cy5k/L
afN7y/O3zm5JlTIzttaFMFsCIgC7Io8Eb8bEtCzk+nbgVaStyxBhJcuPaPp7rKse
d9Gn3FUCITGo0wLxVqHmjk2Pe2n9IJK1ooupjGY1unWQ2rM8RDkqfA==
-----END RSA PRIVATE KEY-----

0 comments on commit fa8f260

Please sign in to comment.