SNI (Server Name Indication) support for ListenAndServeTLS

5,421 views
Skip to first unread message

Paul van Brouwershaven

unread,
Jun 4, 2013, 5:06:46 AM6/4/13
to golan...@googlegroups.com
Has Go implemented SNI (Server Name Indication) for TLS connections?

I haven't found a way to configure multiple SSL Certificates on a single listener in Go.

Thanks,

Paul

StalkR

unread,
Jun 4, 2013, 5:40:15 AM6/4/13
to Paul van Brouwershaven, golan...@googlegroups.com
Hi Paul,

Yes, on the client side, SNI is implemented in tls.Config with
ServerName http://godoc.org/crypto/tls#Config
On the server side, you can specify multiple Certificates and map
NameToCertificate (you can use helper
http://godoc.org/crypto/tls#Config.BuildNameToCertificate).

I agree ListenAndServeTLS does not allow you to set multiple certificates.
I suggest to create your own based on
https://code.google.com/p/go/source/browse/src/pkg/net/http/server.go?name=release#1642

I haven't tried, I hope this helps and let us know if you're successful.

Cheers,
StalkR
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Paul van Brouwershaven

unread,
Jun 4, 2013, 7:53:12 AM6/4/13
to golan...@googlegroups.com, sta...@stalkr.net
Hi StalkR,

Thanks for you tip, I have been able to run multiple certificates on the same socket by using the build-in "BuildNameToCertificate" for SNI support. See below.

I don't think a second ListenAndServeTLS function for SSL should be necessary as the default function could simply accept an array of certificates instead of only a single certificate and key.

What do you think?

Packge SNI in sni/sni.go
package sni

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

type Certificates struct {
CertFile string
KeyFile string
}

func ListenAndServeTLSSNI(srv *http.Server, certs []Certificates) error {
        addr := srv.Addr
        if addr == "" {
                addr = ":https"
        }
        config := &tls.Config{}
        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, len(certs))
for i, v := range certs {
config.Certificates[i], err = tls.LoadX509KeyPair(v.CertFile, v.KeyFile)
if err != nil {
return err
}
}
config.BuildNameToCertificate()
        conn, err := net.Listen("tcp", addr)
        if err != nil {
                return err
        }

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

Application that starts the webserver

package main

import (
"net/http"
"fmt"
"./sni"
)

func main() {
httpsServer := &http.Server{
Addr:      ":8080",
}

var certs []sni.Certificates
certs = append(certs, sni.Certificates{
CertFile: "../etc/site1.pem",
KeyFile: "../etc/site1.key",
})
certs = append(certs, sni.Certificates{
CertFile: "../etc/site2.pem",
KeyFile: "../etc/site2.key",
})

fmt.Println("Listening on port 8080...")
err = sni.ListenAndServeTLSSNI(httpsServer, certs)
if err != nil {
fmt.Printf("err: %s", err)
}
}

StalkR

unread,
Jun 4, 2013, 8:41:43 AM6/4/13
to Paul van Brouwershaven, golan...@googlegroups.com, a...@golang.org
On Tue, Jun 4, 2013 at 1:53 PM, Paul van Brouwershaven <pa...@vanbrouwershaven.com> wrote:
> Hi StalkR,
>
> Thanks for you tip, I have been able to run multiple certificates on the
> same socket by using the build-in "BuildNameToCertificate" for SNI support.
> See below.
Looks good! Glad it worked.


> I don't think a second ListenAndServeTLS function for SSL should be
> necessary as the default function could simply accept an array of
> certificates instead of only a single certificate and key.
The default one is useful if you don't want SNI and changing it would be an API change.
I don't know if it's worth it, since we have all the pieces to do it (as you just did).

Anyway, were you thinking of something like:
ListenAndServeTLS(addr string, certKeyFiles [][2]string, handler Handler) error
with certKeyFiles a slice of [2]string{certFile, keyFile string}?

Adding Adam for comment.

Paul van Brouwershaven

unread,
Jun 4, 2013, 9:07:13 AM6/4/13
to golan...@googlegroups.com, Paul van Brouwershaven, a...@golang.org, sta...@stalkr.net
On Tuesday, 4 June 2013 14:41:43 UTC+2, StalkR wrote:
On Tue, Jun 4, 2013 at 1:53 PM, Paul van Brouwershaven <pa...@vanbrouwershaven.com> wrote:
> I don't think a second ListenAndServeTLS function for SSL should be
> necessary as the default function could simply accept an array of
> certificates instead of only a single certificate and key.
The default one is useful if you don't want SNI and changing it would be an API change.
I don't know if it's worth it, since we have all the pieces to do it (as you just did).

It's not a really hard job to do it yourself, but we have to live with IPv4 for a while and limited availability of IPv4 the use of SNI would likely increase (potentially in combination with a fallback multi domain certificate) I think it's a fair question as it only makes the function more compatible, you could run SNI or non SNI with this function like you do on most web servers.

Anyway, were you thinking of something like:
ListenAndServeTLS(addr string, certKeyFiles [][2]string, handler Handler) error
with certKeyFiles a slice of [2]string{certFile, keyFile string}?

Yes, that make sense.

agl

unread,
Jun 4, 2013, 11:11:14 AM6/4/13
to golan...@googlegroups.com, Paul van Brouwershaven, a...@golang.org, sta...@stalkr.net
On Tuesday, June 4, 2013 8:41:43 AM UTC-4, StalkR wrote:
On Tue, Jun 4, 2013 at 1:53 PM, Paul van Brouwershaven <pa...@vanbrouwershaven.com> wrote:
> Hi StalkR,
>
> Thanks for you tip, I have been able to run multiple certificates on the
> same socket by using the build-in "BuildNameToCertificate" for SNI support.
> See below.
Looks good! Glad it worked.

Me too! I'm not that that there are any tests for it :)
 
> I don't think a second ListenAndServeTLS function for SSL should be
> necessary as the default function could simply accept an array of
> certificates instead of only a single certificate and key.
The default one is useful if you don't want SNI and changing it would be an API change.
I don't know if it's worth it, since we have all the pieces to do it (as you just did).

Anyway, were you thinking of something like:
ListenAndServeTLS(addr string, certKeyFiles [][2]string, handler Handler) error
with certKeyFiles a slice of [2]string{certFile, keyFile string}?

Adding Adam for comment.

ListenAndServeTLS is a helper function for the common case, it doesn't even let you config the tls.Config. I'm not sure that it should be stretched to cover more complex cases because they will probably always be something that it doesn't manage.

However, an example in the godoc for doing something more complex might be a good idea if someone wants to write that.


Cheers

AGL 

StalkR

unread,
Jun 4, 2013, 2:33:13 PM6/4/13
to agl, golan...@googlegroups.com, Paul van Brouwershaven
There's also (*Server).ListenAndServeTLS which allows you to give a Server with a custom TLSConfig, but it will replace the Certificates slice of tls.Config: https://code.google.com/p/go/source/browse/src/pkg/net/http/server.go?name=release#1656
First I was thinking we could change that with an append, so it keeps any already-defined Certificates. The caller still have to do LoadX509KeyPair/BuildNameToCertificate for its certificates. But then what's the purpose of certFile/keyFile, maybe ignore if they're empty? In the end, it would just be to avoid a few lines, and as you said there will always be more complex cases.

However, an example in the godoc for doing something more complex might be a good idea if someone wants to write that.
Good idea, I'll try this.



Cheers

AGL 
Reply all
Reply to author
Forward
0 new messages