Practical embedding in GoLang

Eric Urban has written a phenomenal blog post about embedding in Golang:

http://www.hydrogen18.com/blog/golang-embedding.html

Embedding in Golang is an important concept to  grasp, especially when coming from an object-oriented paradigm, since as Eric describes, embedding in Golang is superficially similar to OO’s inheritance concept. I came across a practical need for doing this when dealing with Golang’s ListenAndServeTLS [1]

The problem in my case is that by default ListenAndServeTLS only accepts external files representing the TLS server certificate and private key [2]. However, this Golang TLS server needed to be self-contained, as it was going to be installed in a semi-hostile environment where I did not want the casual user to be able to read the TLS certificate and key. So ideally we modify the original ListenAndServeTLS method to accept normal variable/constant strings as certificate and key, which can then be compiled straight into an executable. Modifying this default ListenAndServeTLS is where Golang embedding comes into play.

Below is my implementation of this:

Aside: the tls_common package is simply a file containing the necessary certificate and key, as follows:

We can see in practice that embedding is quite simple. We start by first defining a custom struct which we call “embeddedServer”, defined as follows:

type embeddedServer struct {
   http.Server
   webserverCertificate string
   webserverKey string
}

Note that we embedded the default “http.Server” that is included in Go HTTP package, therefore we can expect to have access to all http.Server’s methods and attributes. We are only really interested in “overriding” one of them, which is in fact ListenAndServeTLS. If we look at the source code for ListenAndServeTLS [3] we see that the certificate and key are loaded into the program by means of tls.LoadX509KeyPair [4], which expects files. We need to modify ListenAndServeTLS to instead use “X509KeyPair” which accepts byte arrays.  

So we define a method which receives our custom struct of embeddedServer, with the same name of ListenAndServeTLS and modify it to use X509KeyPair:

func (srv *embeddedServer) ListenAndServeTLS(addr string) error {

   config := &tls.Config{
      MinVersion: tls.VersionTLS10,
   }
   if srv.TLSConfig != nil {
      *config = *srv.TLSConfig
   }
   if config.NextProtos == nil {
      config.NextProtos = []string{"http/1.1"}
   }

   var err error
   config.Certificates = make([]tls.Certificate, 1)
   config.Certificates[0], err = tls.X509KeyPair([]byte(srv.webserverCertificate), []byte(srv.webserverKey))
   if err != nil {
      return err
   }

   conn, err := net.Listen("tcp", addr)
   if err != nil {
      return err
   }

   tlsListener := tls.NewListener(conn, config)
   return srv.Serve(tlsListener)
}

The most important change is in bold above. This now allows us to pass variables into the struct (webServerCertificate and webserverKey) which are then used by our modified ListenAndServeTLS.

 

References

 

[1] Golang, ‘Package http’, [online] <Available from: http://golang.org/pkg/net/http/#ListenAndServeTLS>

[2] Kaihag, ‘External Assets, Working Directories, and Go’ [online] <Available from: https://www.kaihag.com/external-assets-working-directories-and-go/>

[3] Github, ‘zenazn/goji’ [online] <Available from: https://github.com/zenazn/goji/blob/master/graceful/server.go#L33>

[4] Golang, ‘Package tls’, [online] <Available from: https://golang.org/pkg/crypto/tls/#LoadX509KeyPair>