Reputation: 3789
I have a go service that makes REST requests to an HTTP server that I don't control. A customer asked my to "confirm" that my service is connecting via TLS 1.2. Is that something that I can do in code?
Current code looks something like this:
request, _ := http.NewRequest("PUT",
"https://example.com/path/to/endpoint",
bytes.NewReader(json))
client := &http.Client{}
response, _ := client.Do(request)
defer response.Body.Close()
str, err := ioutil.ReadAll(response.Body)
Based on a quick read of the docs I believe I need to use a Transport
and build my client using that transport. Something like this:
tr := &http.Transport{
... some options here ...
}
client := &http.Client{Transport: tr}
But I'm not sure what options I should set.
Upvotes: 5
Views: 13911
Reputation: 31701
At the time of writing, Go will speak TLS 1.2 automatically if the server supports it.
tls.ConnectionState reports various negotiated TLS parameters of a connection, including the protocol version.
To get the underlying TLS connection for an HTTP client it is easiest to set the DialTLS
field of the Transport
to a function that establishes and remembers the connection. Once the response arrived (but before you close the response body!), call tls.Conn.ConnectionState:
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
)
func main() {
var (
conn *tls.Conn
err error
)
tlsConfig := http.DefaultTransport.(*http.Transport).TLSClientConfig
c := &http.Client{
Transport: &http.Transport{
DialTLS: func(network, addr string) (net.Conn, error) {
conn, err = tls.Dial(network, addr, tlsConfig)
return conn, err
},
},
}
res, err := c.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
versions := map[uint16]string{
tls.VersionSSL30: "SSL",
tls.VersionTLS10: "TLS 1.0",
tls.VersionTLS11: "TLS 1.1",
tls.VersionTLS12: "TLS 1.2",
}
fmt.Println(res.Request.URL)
fmt.Println(res.Status)
v := conn.ConnectionState().Version
fmt.Println(versions[v])
res.Body.Close()
}
// Output:
// https://example.com
// 200 OK
// TLS 1.2
Upvotes: 8
Reputation: 4461
From the docs
Package tls partially implements TLS 1.2, as specified in RFC 5246.
That beeing said I keep this function as a snippet to create the necessary configuration:
func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string) (*tls.Config, error) {
tlsConfig := tls.Config{}
// Load client cert
cert, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
if err != nil {
return &tlsConfig, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
// Load CA cert
caCert, err := ioutil.ReadFile(caCertFile)
if err != nil {
return &tlsConfig, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.RootCAs = caCertPool
tlsConfig.BuildNameToCertificate()
return &tlsConfig, err
}
After that you just need to initialize the transport:
transport := &http.Transport{TLSClientConfig: tlsConfig}
client := &http.Client{Transport: transport}
Upvotes: 3