Search code examples
goreverse-proxytls1.2go-gin

Trouble getting reverse proxy working with TLS (to proxied server) in Golang with Gin


I've been tasked with creating a reverse proxy that is required to make a TLS connection to the proxied service. The certificates I have are unique per request, and in-memory.

I haven't had much luck getting it right, and I've tried a number of things. Here's where I'm at now, hopefully someone can help:

func proxy(c *gin.Context) {
   /* snip: magic here to get the x509 cert strings and remoteUrl */

   proxy := httputil.NewSingleHostReverseProxy(remoteUrl)

   cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte(signedCertString)})
   key:= pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: []byte(privateKeyString)})

   certificate, err := tls.X509KeyPair(cert, key)
   if err != nil {
      c.JSON(400, gin.H{"message": "Invalid certificate"})
      return
   }

   proxy.Transport = &http.Transport{
     TLSClientConfig: &tls.Config{
       Certificates: []tls.Certificate{certificate},
       InsecureSkipVerify: true,
     }
   }

   c.Request.Host = remote.Host
   c.Request.URL.Scheme = remote.Scheme
   c.Request.URL.Host = remote.Host
   c.Request.URL.Path = remote.Path

   proxy.ServeHTTP(c.Writer, c.Request)
}

I've also tried setting RootCAs (figuring maybe I'm just twisted about now TLS needs to work in this scenario):


func proxy(c *gin.Context) {
   /* snip: magic here to get the x509 cert strings and remoteUrl */

   proxy := httputil.NewSingleHostReverseProxy(remoteUrl)

   cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte(signedCertString)})
  
   certPool := x509.NewCertPool()
   certPool.AppendCertsFromPEM(cert)

   proxy.Transport = &http.Transport{
     TLSClientConfig: &tls.Config{
       RootCAs: certPool,
       InsecureSkipVerify: true,
     }
   }

   c.Request.Host = remote.Host
   c.Request.URL.Scheme = remote.Scheme
   c.Request.URL.Host = remote.Host
   c.Request.URL.Path = remote.Path

   proxy.ServeHTTP(c.Writer, c.Request)
}

In any case, the server I'm targeting doesn't seem to pick up that the proxied request was TLS, and I'm not really sure where to proceed with this.


Solution

  • I ended up switching to use ReverseProxy directly. But the big problem is I was using RootCAs and not ClientCAs. Here is what I ended up with:

    
    clientCAs := x509.NewCertPool()
    clientCAs.AppendCertsFromPEM(signedCert)
    certificate, err := tls.X509KeyPair(signedCert, privateKey)
    
    proxy := &httputil.ReverseProxy({
      Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
          Certificates: []tls.Certificate{certificate},
          ClientCAs: clientCAs
        },
        Director: func (req *http.Request) {
          // Alter request here
        }
      },
    })
    
    proxy.ServeHTTP(w, r)
    

    After this, everything is working swimmingly. Thanks, all!