當我們用Golang寫一個TLS Server的時候,假設給Server端配置了CA證書,那麼Server端一定會校驗客戶端的證書嗎?答案是,即使我們給Server端配置了CA證書,Server端也不一定會校驗客戶端的證書。
看一個TLS Server端的例子:
package main
import (
"log"
"crypto/tls"
"net"
"fmt"
"crypto/x509"
"bufio"
"io/ioutil"
)
func main() {
log.SetFlags(log.Lshortfile)
cer, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Println(err)
return
}
ca, err := ioutil.ReadFile("client-CA.crt")
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(ca)
if !ok {
panic(fmt.Errorf("fail to load ca content"))
}
tlsConfig := &tls.Config{
ClientCAs: pool,
ClientAuth: tls.RequireAnyClientCert,
Certificates: []tls.Certificate{cer},
}
ln, err := tls.Listen("tcp", ":443", tlsConfig)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
println(msg)
n, err := conn.Write([]byte("world\n"))
if err != nil {
log.Println(n, err)
return
}
}
}
我們在tlsConfig中可以看到,配置了驗證客戶端正的CA ClientCAs,還有服務端的證書Certificates。除此之外還有一個很重要的字段——ClientAuth,在Golang中,ClientAuth決定了Server端如何驗證客戶端的證書。
// ClientAuthType declares the policy the server will follow for
// TLS Client Authentication.
type ClientAuthType int
const (
NoClientCert ClientAuthType = iota
RequestClientCert
RequireAnyClientCert
VerifyClientCertIfGiven
RequireAndVerifyClientCert
)
ClientAuth有五種類型,分別是 NoClientCert、RequestClientCert、RequireAnyClientCert、VerifyClientCertIfGiven、RequireAndVerifyClientCert,各自的含義是:
- NoClientCert:忽略任何客戶端證書,即客戶端可以不提供證書。
- RequestClientCert:要求客戶端提供證書,但是如果客戶端沒有提供證書,服務端還是會繼續處理請求。
- RequireAnyClientCert:需要客戶端提供證書,但不用ClientCA來驗證證書的有效性。
- VerifyClientCertIfGiven:如果客戶端提供了證書,則用ClientCA來驗證證書的有效性。 如果客戶端沒提供,則會繼續處理請求。
- RequireAndVerifyClientCert:需要客戶端提供證書,且會用ClientCA來驗證證書的有效性。
在tlsConfig中如果不顯式的指定ClientAuth,則默認值是NoClientCert。即使Server端配置了CA,也不會校驗客戶端證書。
在K8s中我們知道Kube-Apiserver是有多種認證方式的,包括賬號密碼、Token、證書等。其實它的ClientAuth設置的就是RequestClientCert。即在Tls這層是不強校驗客戶端證書的。這樣才能保證Tls能接受多種來自客戶端的憑據。
if s.ClientCA != nil {
// Populate PeerCertificates in requests, but don't reject connections without certificates
// This allows certificates to be validated by authenticators, while still allowing other auth types
tlsConfig.ClientAuth = tls.RequestClientCert
}