Skip to content

Commit

Permalink
security: support init tls config from cert/key/ca bytes (#458)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ehco1996 authored Jul 27, 2021
1 parent ea3f7c2 commit 915b22e
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 24 deletions.
4 changes: 0 additions & 4 deletions go.sum1
Original file line number Diff line number Diff line change
Expand Up @@ -805,10 +805,7 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v2.19.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v2.20.3+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v2.20.6+incompatible h1:P37G9YH8M4vqkKcwBosp+URN5O8Tay67D2MbR361ioY=
github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v2.20.9+incompatible h1:msXs2frUV+O/JLva9EDLpuJ84PrFsdCTCQex8PUdtkQ=
github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil v3.21.4+incompatible h1:fuHcTm5mX+wzo542cmYcV9RTGQLbnHLI5SyQ5ryTVck=
github.com/shirou/gopsutil v3.21.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
Expand Down Expand Up @@ -1178,7 +1175,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200819171115-d785dc25833f h1:KJuwZVtZBVzDmEDtB2zro9CXkD9O0dpCv4o2LHbQIAw=
golang.org/x/sys v0.0.0-20200819171115-d785dc25833f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa h1:ZYxPR6aca/uhfRJyaOAtflSHjJYiktO7QnJC5ut7iY4=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
67 changes: 48 additions & 19 deletions pkg/utils/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ func ToTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) {
return ToTLSConfigWithVerify(caPath, certPath, keyPath, nil)
}

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

// ToTLSConfigWithVerify constructs a `*tls.Config` from the CA, certification and key
// paths, and add verify for CN.
//
Expand Down Expand Up @@ -77,29 +100,35 @@ func ToTLSConfigWithVerify(caPath, certPath, keyPath string, verifyCN []string)
NextProtos: []string{"h2", "http/1.1"}, // specify `h2` to let Go use HTTP/2.
}

if len(verifyCN) != 0 {
checkCN := make(map[string]struct{})
for _, cn := range verifyCN {
cn = strings.TrimSpace(cn)
checkCN[cn] = struct{}{}
}
addVerifyPeerCertificate(tlsCfg, verifyCN)
return tlsCfg, nil
}

tlsCfg.ClientAuth = tls.RequireAndVerifyClientCert
// ToTLSConfigWithVerifyByRawbytes constructs a `*tls.Config` from the CA, certification and key bytes
// and add verify for CN.
func ToTLSConfigWithVerifyByRawbytes(caData, certData, keyData []byte, verifyCN []string) (*tls.Config, error) {
// Generate a key pair from your pem-encoded cert and key ([]byte).
cert, err := tls.X509KeyPair(certData, keyData)
if err != nil {
return nil, errors.New("failed to generate cert")
}
certificates := []tls.Certificate{cert}

tlsCfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
cns := make([]string, 0, len(verifiedChains))
for _, chains := range verifiedChains {
for _, chain := range chains {
cns = append(cns, chain.Subject.CommonName)
if _, match := checkCN[chain.Subject.CommonName]; match {
return nil
}
}
}
return errors.Errorf("client certificate authentication failed. The Common Name from the client certificate %v was not found in the configuration cluster-verify-cn with value: %s", cns, verifyCN)
}
// Create a certificate pool from CA
certPool := x509.NewCertPool()

// Append the certificates from the CA
if !certPool.AppendCertsFromPEM(caData) {
return nil, errors.New("failed to append ca certs")
}

tlsCfg := &tls.Config{
Certificates: certificates,
RootCAs: certPool,
ClientCAs: certPool,
NextProtos: []string{"h2", "http/1.1"}, // specify `h2` to let Go use HTTP/2.
}
addVerifyPeerCertificate(tlsCfg, verifyCN)
return tlsCfg, nil
}

Expand Down
17 changes: 16 additions & 1 deletion pkg/utils/security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ func (s *securitySuite) TestCheckCN(c *C) {
c.Assert(err, IsNil)

caPath1, certPath1, keyPath1 := getTestCertFile(dir, "client1")
clientTLS1, err := ToTLSConfigWithVerify(caPath1, certPath1, keyPath1, nil)
caData, certData, keyData := loadTLSContent(c, caPath1, certPath1, keyPath1)
clientTLS1, err := ToTLSConfigWithVerifyByRawbytes(caData, certData, keyData, []string{})
c.Assert(err, IsNil)

caPath2, certPath2, keyPath2 := getTestCertFile(dir, "client2")
Expand Down Expand Up @@ -160,3 +161,17 @@ func runServer(ctx context.Context, tlsCfg *tls.Config, port int, c *C) *http.Se
func getTestCertFile(dir, role string) (string, string, string) {
return path.Join(dir, "ca.pem"), path.Join(dir, fmt.Sprintf("%s.pem", role)), path.Join(dir, fmt.Sprintf("%s.key", role))
}

func loadTLSContent(c *C, caPath, certPath, keyPath string) (caData, certData, keyData []byte) {
// NOTE we make sure the file exists,so we don't need to check the error
var err error
caData, err = ioutil.ReadFile(caPath)
c.Assert(err, IsNil)

certData, err = ioutil.ReadFile(certPath)
c.Assert(err, IsNil)

keyData, err = ioutil.ReadFile(keyPath)
c.Assert(err, IsNil)
return
}

0 comments on commit 915b22e

Please sign in to comment.