Hi,
I've a legacy application at hand that has a nginx as TLS offloader in front of it. Besides a simple frontend the application offers an API including a PKI infrastructure (CSRs are pushed to server, signed CRTs are returned). The nginx is configured to optionally request a client certificate ("ssl_verify_client optional") from the requesting party (browsers, IoT devices, ...).
This approach is working for most browsers except OS X's Safari. nginx offers a list acceptable client certificate CA names but Safari tends to ignore this list and prompts the user to chose a certificate (including iCloud certificates and stuff).
Browsers are not necessarily required to provide a client certificate. IoT devices are configured to always provide a client certificate.
Differing from nginx's and Apache's SSL configuration, the TLS implementation of golang seems to offer another client authentication mechanism: VerifyClientCertIfGiven (see
https://golang.org/pkg/crypto/tls/#ClientAuthType). So I decided to give golang a try (as a potential TLS offloader replacing nginx) and came up with the following simple TLS secured HTTP server:
package main
import (
"log"
"net/http"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"fmt"
)
func handler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("Test.\n"))
}
func main() {
http.HandleFunc("/", handler)
pemByte, _ := ioutil.ReadFile("ssl/ca.pem")
block, pemByte := pem.Decode(pemByte)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
fmt.Println(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(cert)
server := &http.Server{
TLSConfig: &tls.Config{
ClientAuth: tls.VerifyClientCertIfGiven,
ClientCAs: certPool,
},
Addr: "0.0.0.0:10443",
}
server.TLSConfig.BuildNameToCertificate()
err = server.ListenAndServeTLS("ssl/server.crt", "ssl/server.key")
if err != nil {
log.Fatal(err)
}
}
My initial thought when reading the const "VerifyClientCertIfGiven" was that a list of acceptable client certificate CA names will NOT be sent to the connecting client, but certificates that are provided will be validated against the configured ClientCAs.
I'm new to golang and my knowledge of the TLS spec isn't good either but I'm wondering if this conditional would be better changed to "c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert || c.config.ClientAuth == RequestClientCert"? From my point of view "VerifyClientCertIfGiven" has no special meaning and acts exactly as RequireAndVerifyClientCert.
Am I missing something?
BR, Sven