18

I have multiple domain (let's say abc.com and xyz.org) with diffrent certificate. Is it possible to use key and certificate based on hostname without going deep low level and net.Listen, etc. Just using simple http.ListenAndServeTLS(...) or similar ? Basically like what nginx does.

Ostad
  • 2,784
  • 2
  • 15
  • 17

1 Answers1

23

BuildNameToCertificate() will sniff the hostname from the cert. If none match the SNI info it serves the [0]. https://golang.org/src/crypto/tls/common.go?s=18204:18245#L947

Update for Go 1.14 - see https://github.com/golang/go/commit/eb93c684d40de4924fc0664d7d9e98a84d5a100b

package main

import (
    "crypto/tls"
    "net/http"
    "time"

    "log"
)

func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("tls"))
}

func main() {
    t := log.Logger{}
    var err error
    tlsConfig := &tls.Config{}
    tlsConfig.Certificates = make([]tls.Certificate, 3)
    // go http server treats the 0'th key as a default fallback key
    tlsConfig.Certificates[0], err = tls.LoadX509KeyPair("test0.pem", "key.pem")
    if err != nil {
        t.Fatal(err)
    }
    tlsConfig.Certificates[1], err = tls.LoadX509KeyPair("test1.pem", "key.pem")
    if err != nil {
        t.Fatal(err)
    }
    tlsConfig.Certificates[2], err = tls.LoadX509KeyPair("test2.pem", "key.pem")
    if err != nil {
        t.Fatal(err)
    }

    // as of go 1.14 this line is no longer needed
    // load the certs as above and skip BuildNameToCertificate()
    tlsConfig.BuildNameToCertificate()

    http.HandleFunc("/", myHandler)
    server := &http.Server{
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
        TLSConfig:      tlsConfig,
    }

    listener, err := tls.Listen("tcp", ":8443", tlsConfig)
    if err != nil {
        t.Fatal(err)
    }
    log.Fatal(server.Serve(listener))
}
foo
  • 701
  • 4
  • 8
  • server doesn't have any StartTLS method! – Ostad Feb 26 '16 at 03:50
  • 2
    Corrected with a working example. This more complete example properly creates the listener and starts the server. – foo Feb 26 '16 at 08:42
  • How should I add my router here? – Saransh Dec 04 '17 at 13:15
  • @foo I am using gorilla mux for routing. I was using only one certificate earlier. So, I used http.ListenAndServe(port,cert,key,router). How can I use same router here? – Saransh Dec 05 '17 at 05:45
  • @Saransh this technique supersedes ListenAndServer(). For gorilla, just replace myHandler with your mux.Router reference – foo Dec 13 '17 at 20:47
  • 1
    Heads up tls.NameToCertificate is deprecated. NameToCertificate only allows associating a single certificate with a given name. Leave that field nil to let the library select the first compatible chain from Certificates. – ecnepsnai Sep 23 '21 at 02:14