Skip to content

Commit 92f6f4e

Browse files
authored
server: support check the "CommanName" of tls-cert for status-port(http/grpc) (pingcap#15137)
1 parent 0ddfe07 commit 92f6f4e

12 files changed

+176
-7
lines changed

config/config.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,14 @@ func (l *Log) getDisableErrorStack() bool {
242242

243243
// Security is the security section of the config.
244244
type Security struct {
245-
SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"`
246-
SSLCA string `toml:"ssl-ca" json:"ssl-ca"`
247-
SSLCert string `toml:"ssl-cert" json:"ssl-cert"`
248-
SSLKey string `toml:"ssl-key" json:"ssl-key"`
249-
ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"`
250-
ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"`
251-
ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"`
245+
SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"`
246+
SSLCA string `toml:"ssl-ca" json:"ssl-ca"`
247+
SSLCert string `toml:"ssl-cert" json:"ssl-cert"`
248+
SSLKey string `toml:"ssl-key" json:"ssl-key"`
249+
ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"`
250+
ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"`
251+
ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"`
252+
ClusterVerifyCN []string `toml:"cluster-verify-cn" json:"cluster-verify-cn"`
252253
}
253254

254255
// The ErrConfigValidationFailed error is used so that external callers can do a type assertion

server/http_handler_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ package server
1515

1616
import (
1717
"bytes"
18+
"crypto/tls"
19+
"crypto/x509"
20+
"crypto/x509/pkix"
1821
"database/sql"
1922
"encoding/base64"
2023
"encoding/json"
@@ -1059,6 +1062,19 @@ func (ts *HTTPHandlerTestSuite) TestDebugZip(c *C) {
10591062
c.Assert(resp.Body.Close(), IsNil)
10601063
}
10611064

1065+
func (ts *HTTPHandlerTestSuite) TestCheckCN(c *C) {
1066+
s := &Server{cfg: &config.Config{Security: config.Security{ClusterVerifyCN: []string{"a ", "b", "c"}}}}
1067+
tlsConfig := &tls.Config{}
1068+
s.setCNChecker(tlsConfig)
1069+
c.Assert(tlsConfig.VerifyPeerCertificate, NotNil)
1070+
err := tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "a"}}}})
1071+
c.Assert(err, IsNil)
1072+
err = tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "b"}}}})
1073+
c.Assert(err, IsNil)
1074+
err = tlsConfig.VerifyPeerCertificate(nil, [][]*x509.Certificate{{{Subject: pkix.Name{CommonName: "d"}}}})
1075+
c.Assert(err, NotNil)
1076+
}
1077+
10621078
func (ts *HTTPHandlerTestSuite) TestZipInfoForSQL(c *C) {
10631079
ts.startServer(c)
10641080
defer ts.stopServer(c)

server/http_status.go

+24
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bytes"
1919
"context"
2020
"crypto/tls"
21+
"crypto/x509"
2122
"encoding/json"
2223
"fmt"
2324
"net"
@@ -273,6 +274,7 @@ func (s *Server) setupStatusServerAndRPCServer(addr string, serverMux *http.Serv
273274
logutil.BgLogger().Error("invalid TLS config", zap.Error(err))
274275
return
275276
}
277+
tlsConfig = s.setCNChecker(tlsConfig)
276278

277279
var l net.Listener
278280
if tlsConfig != nil {
@@ -310,6 +312,28 @@ func (s *Server) setupStatusServerAndRPCServer(addr string, serverMux *http.Serv
310312
}
311313
}
312314

315+
func (s *Server) setCNChecker(tlsConfig *tls.Config) *tls.Config {
316+
if tlsConfig != nil && len(s.cfg.Security.ClusterVerifyCN) != 0 {
317+
checkCN := make(map[string]struct{})
318+
for _, cn := range s.cfg.Security.ClusterVerifyCN {
319+
cn = strings.TrimSpace(cn)
320+
checkCN[cn] = struct{}{}
321+
}
322+
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
323+
for _, chain := range verifiedChains {
324+
if len(chain) != 0 {
325+
if _, match := checkCN[chain[0].Subject.CommonName]; match {
326+
return nil
327+
}
328+
}
329+
}
330+
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)
331+
}
332+
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
333+
}
334+
return tlsConfig
335+
}
336+
313337
// status of TiDB.
314338
type status struct {
315339
Connections int `json:"connections"`

server/tidb_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import (
2424
"encoding/pem"
2525
"io/ioutil"
2626
"math/big"
27+
"net/http"
2728
"os"
29+
"path/filepath"
2830
"time"
2931

3032
"github.com/go-sql-driver/mysql"
@@ -215,6 +217,56 @@ func (ts *tidbTestSuite) TestStatusAPIWithTLS(c *C) {
215217
server.Close()
216218
}
217219

220+
func (ts *tidbTestSuite) TestStatusAPIWithTLSCNCheck(c *C) {
221+
c.Skip("need add ca-tidb-test-1.crt to OS")
222+
root := filepath.Join(os.Getenv("GOPATH"), "/src/github.com/pingcap/tidb")
223+
ca := filepath.Join(root, "/tests/cncheckcert/ca-tidb-test-1.crt")
224+
225+
cli := newTestServerClient()
226+
cli.statusScheme = "https"
227+
cfg := config.NewConfig()
228+
cfg.Port = cli.port
229+
cfg.Status.StatusPort = cli.statusPort
230+
cfg.Security.ClusterSSLCA = ca
231+
cfg.Security.ClusterSSLCert = filepath.Join(root, "/tests/cncheckcert/server-cert.pem")
232+
cfg.Security.ClusterSSLKey = filepath.Join(root, "/tests/cncheckcert/server-key.pem")
233+
cfg.Security.ClusterVerifyCN = []string{"tidb-client-2"}
234+
server, err := NewServer(cfg, ts.tidbdrv)
235+
c.Assert(err, IsNil)
236+
go server.Run()
237+
time.Sleep(time.Millisecond * 100)
238+
239+
hc := newTLSHttpClient(c, ca,
240+
filepath.Join(root, "/tests/cncheckcert/client-cert-1.pem"),
241+
filepath.Join(root, "/tests/cncheckcert/client-key-1.pem"),
242+
)
243+
_, err = hc.Get(cli.statusURL("/status"))
244+
c.Assert(err, NotNil)
245+
246+
hc = newTLSHttpClient(c, ca,
247+
filepath.Join(root, "/tests/cncheckcert/client-cert-2.pem"),
248+
filepath.Join(root, "/tests/cncheckcert/client-key-2.pem"),
249+
)
250+
_, err = hc.Get(cli.statusURL("/status"))
251+
c.Assert(err, IsNil)
252+
}
253+
254+
func newTLSHttpClient(c *C, caFile, certFile, keyFile string) *http.Client {
255+
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
256+
c.Assert(err, IsNil)
257+
caCert, err := ioutil.ReadFile(caFile)
258+
c.Assert(err, IsNil)
259+
caCertPool := x509.NewCertPool()
260+
caCertPool.AppendCertsFromPEM(caCert)
261+
tlsConfig := &tls.Config{
262+
Certificates: []tls.Certificate{cert},
263+
RootCAs: caCertPool,
264+
InsecureSkipVerify: true,
265+
}
266+
tlsConfig.BuildNameToCertificate()
267+
return &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
268+
}
269+
218270
func (ts *tidbTestSuite) TestMultiStatements(c *C) {
219271
c.Parallel()
220272
ts.runTestMultiStatements(c)

tests/cncheckcert/ca-key.pem

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIBQwIBAAJDAL6zaGJBgNTBNhtGTfSXnbzoZa1LlfCpFlIwtXc1YBBtl9IIzRI5
3+
j2Cyd/DTYjO0vniHGIHv7H64336szhSGOcHV5QIDAQABAkIpDH9MnyL3KPvXlSOU
4+
oco/bprsWZfV7N+0I238Ug3ym1QyCK3ue8m/bJveL9AXCwJWMLdvHQoiyCFnaQ6f
5+
Ay2hGYUCIgD4YcAP4aIJL1H9vo0vmXQzFZJzklyOUcmRm00Ulvie5dsCIgDEjMGk
6+
QZoiR9ammPSc1IKF/c6THtd1sA0rW9Vh0sCBXz8CIgCMkq4jjtyo/BoYVRcM4HmO
7+
S+A1/pjZh1pgSRfH1mXhcE8CITRT5Rn9/TMzPQqNnlJCoZ1avSyeAW7ruBXbFSw+
8+
F9JZsQIhMvQjbm0ygrSwOlvRhWOzAtUKSxcs7JKfxgt9/5/XIV4C
9+
-----END RSA PRIVATE KEY-----

tests/cncheckcert/ca-tidb-test-1.crt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBXzCCAQegAwIBAgIBADANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
3+
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAUMRIwEAYDVQQD
4+
EwlUaURCIENBIDUwXjANBgkqhkiG9w0BAQEFAANNADBKAkMAvrNoYkGA1ME2G0ZN
5+
9JedvOhlrUuV8KkWUjC1dzVgEG2X0gjNEjmPYLJ38NNiM7S+eIcYge/sfrjffqzO
6+
FIY5wdXlAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwICpDAdBgNVHSUEFjAUBggrBgEF
7+
BQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAND
8+
AKfu05xLZQ6PVMyYPjpo/RI/WchaLFRbBZKpdu+/QMaxreH7/xAqDfnslTVblWLo
9+
uGxIrIOBL/1jR8CoAhBB1VAoJw==
10+
-----END CERTIFICATE-----

tests/cncheckcert/client-cert-1.pem

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
3+
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
4+
Ew10aWRiLWNsaWVudC0xMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDAMMfWMiDqdQE
5+
xy0kE/W+V+OmYZGit7iGk+BzMe6G0w9fFmJt8ajwmHp5dMT5AmJpNF/i089Wej3K
6+
SdEkWhWa96BBOwIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
7+
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
8+
QwAnXNy43kWtlyelP1M5s0h4KNkmYBe6McaGV5nvhD/dwFfviY1dMRQ4ChgBgc5y
9+
vv2r0fnRd2XF+81EmNGQk79Xg9A=
10+
-----END CERTIFICATE-----

tests/cncheckcert/client-cert-2.pem

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
3+
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
4+
Ew10aWRiLWNsaWVudC0yMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDANuIi1RlJKHF
5+
KJg5D/fblj8FfRaay9CLJOhDZkabJTuMHerlpY0vf8wsY15khaJU/dRiJeAT+lvg
6+
V4CluJLSSLuIEQIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
7+
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
8+
QwABsC2+tggurK8iUDmuaEKOqGnQdHrMMohDnSjmuS+X9xYcLlix7Haf53fBhs8B
9+
kvUiPFk8QEiOi7xttaMHKeDLnRE=
10+
-----END CERTIFICATE-----

tests/cncheckcert/client-key-1.pem

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIBRQIBAAJDAMMfWMiDqdQExy0kE/W+V+OmYZGit7iGk+BzMe6G0w9fFmJt8ajw
3+
mHp5dMT5AmJpNF/i089Wej3KSdEkWhWa96BBOwIDAQABAkJ4ps1zT1aX70xpsUFW
4+
Vxhpf9wc/Yy04SJXS2O4pk2j15seJ9chhaolp26mVm1w/jyV/k6TwuKo0pM+F4pI
5+
ePJNQQECIgDLWTCV6E1KGmprVnm/WRqUTewcNzfsXFHUlPSuIhTxUZsCIgD1pOfU
6+
DpzwOoZb7XUogqkLKleaWWm22ffHuvuT3+ozGOECIgDDDS9Ea8pPTV1MzmsDtyV+
7+
oevb+L9ksf0wKx00Nq7d9wcCIgCX/++4B0bTW9OSBLCvXZKOpyfICbXhgKTTQX+0
8+
9CRuc+ECIgCqTejjhRnhz3rYD0wWqRRsXT/144RmiIpBslRp+HJ+5dg=
9+
-----END RSA PRIVATE KEY-----

tests/cncheckcert/client-key-2.pem

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIBQgIBAAJDANuIi1RlJKHFKJg5D/fblj8FfRaay9CLJOhDZkabJTuMHerlpY0v
3+
f8wsY15khaJU/dRiJeAT+lvgV4CluJLSSLuIEQIDAQABAkJw/adQqbof9Pz+1CfO
4+
11tOVoHaV5PdYzB8xuvmHUYdjvCG4WJcfFFkVipqE8VhJsS2Onzb7BFJ7gOqxCV6
5+
9+FoYgECIgD1IP2jXxqOJgI3p78YaCJnBlRUMIu/+g+MmGPcLqg8taECIgDlRPYh
6+
m0kOwtmzTtTI5sOLDgDPew16p8jyBvBPxsk53HECIW6W/rc5DeL5tOBlFqqtOHAg
7+
g+Ujrbjj2SYGDm9kwVP6YQIhPd/xqTo2alRt2nWA+cNFrMaXs2cbSSn1ElSLEIyu
8+
i/4RAiFcI5yrNMt2XwyKo/A+hzFvJeuQ6xTs9I1KMYbsngXJ58k=
9+
-----END RSA PRIVATE KEY-----

tests/cncheckcert/server-cert.pem

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBYDCCAQigAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUaURC
3+
IENBIDUwHhcNMjAwMzA0MTI0MzIxWhcNMjAwMzA0MTM0MzIxWjAYMRYwFAYDVQQD
4+
Ew10aWRiLXNlcnZlci0zMF4wDQYJKoZIhvcNAQEBBQADTQAwSgJDAM+ez7yZLKp9
5+
spGiUqqInrH8iTZ8JPa3C8K8bYw/Fc/W028sVvf6+VLC1ZP0cJX4Lx3iyyyIfzjG
6+
KV4+MVcBOlfn4wIDAQABoz8wPTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
7+
KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
8+
QwCUAO+eDYvVXBIqpNWr/h513TTSzZa+ELNOrYNGr2MgQ6j8TW0+J+J4JGgj0Wm7
9+
1haBMbGShAHlXYK81ZShtnbPpFI=
10+
-----END CERTIFICATE-----

tests/cncheckcert/server-key.pem

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIBRAIBAAJDAM+ez7yZLKp9spGiUqqInrH8iTZ8JPa3C8K8bYw/Fc/W028sVvf6
3+
+VLC1ZP0cJX4Lx3iyyyIfzjGKV4+MVcBOlfn4wIDAQABAkJ5hiNh6OZUBK74v2JT
4+
nxQEaiSGV7PrFMk1esVESciilsKsRJLcyzxbpANooQQefOswHzRR/YyMmy2HiW++
5+
H08ENMECIgDR6EfM7CKKJGj2jz3b+2bUoBpdpuPtD9kPo4u6ubVnNz8CIgD9Nfhq
6+
Viiwl2IentOXOWvIasGQdQXz1fWHGLP5nA2Xql0CIgCXcSGUXG2TAy/ja3cy5k/L
7+
afN7y/O3zm5JlTIzttaFMFsCIgC7Io8Eb8bEtCzk+nbgVaStyxBhJcuPaPp7rKse
8+
d9Gn3FUCITGo0wLxVqHmjk2Pe2n9IJK1ooupjGY1unWQ2rM8RDkqfA==
9+
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)