Go

How do you configure TLS correctly in Go (crypto/tls MinVersion, certificate reloading, mTLS)?

Question 573HardGo 1.22 to 1.25

crypto/tls has safe defaults. Since Go 1.22 the default minimum version is TLS 1.2 for both clients and servers, and RSA key-exchange cipher suites are no longer offered by default. Since Go 1.24 the hybrid post-quantum key exchange X25519MLKEM768 is on by default. Usually the best configuration is a minimal one: set MinVersion explicitly for auditors and leave cipher suites alone. TLS 1.3 suites cannot be configured anyway.

For certificate rotation without a restart, use GetCertificate, which is called on each handshake. For mTLS, the server sets ClientAuth: tls.RequireAndVerifyClientCert and ClientCAs, and the client presents Certificates and trusts the server CA via RootCAs.

type certReloader struct{ cert atomic.Pointer[tls.Certificate] }

func (r *certReloader) reload(certFile, keyFile string) error {
	c, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err // keep serving the old certificate
	}
	r.cert.Store(&c)
	return nil
}

func (r *certReloader) get(*tls.ClientHelloInfo) (*tls.Certificate, error) {
	return r.cert.Load(), nil
}

func newServer(r *certReloader, caPEM []byte, h http.Handler) (*http.Server, error) {
	pool := x509.NewCertPool()
	if !pool.AppendCertsFromPEM(caPEM) {
		return nil, errors.New("no CA certs parsed")
	}
	return &http.Server{
		Addr:              ":8443",
		Handler:           h,
		ReadHeaderTimeout: 5 * time.Second,
		TLSConfig: &tls.Config{
			MinVersion:     tls.VersionTLS12,
			GetCertificate: r.get,
			ClientAuth:     tls.RequireAndVerifyClientCert, // mTLS
			ClientCAs:      pool,
		},
	}, nil
}
// srv.ListenAndServeTLS("", "") because the certificate comes from GetCertificate

Gotchas:

  • Never ship InsecureSkipVerify: true. If you need custom verification, use VerifyConnection.
  • A client's ServerName must match the certificate's SAN. Go stopped accepting the CommonName field for hostname checks in Go 1.15.
  • For client-certificate rotation, use GetClientCertificate.
  • Authorize on the verified peer identity (r.TLS.VerifiedChains or the SPIFFE URI SAN). A valid certificate alone does not mean the caller is allowed.
  • Go 1.24 added FIPS 140-3 mode (GOFIPS140).

More on Observability, Debugging & Production Operations

All 14 Observability, Debugging & Production Operations questions