Skip to content

Added TLS to WebSocket#405

Merged
jeffallen merged 9 commits intodedis:masterfrom
ValentinMoullet:onet_fork/issue_383
May 3, 2018
Merged

Added TLS to WebSocket#405
jeffallen merged 9 commits intodedis:masterfrom
ValentinMoullet:onet_fork/issue_383

Conversation

@ValentinMoullet
Copy link
Contributor

@ValentinMoullet ValentinMoullet commented Apr 19, 2018

  • Added 2 fields in private.toml for setting the WebSocket TLS certificate and key, if wanted.
  • WebSocket is now using ListenAndServeTLS using the TLS configuration found in the private.toml if given.
  • Client (in websocket.go) can now communicate in TLS, if asked.

In the private.toml file, we can set the 2 WebSocket TLS fields using 2 different ways:

  • string://{THE CONTENT OF THE CERTIFICATE}
  • file://{THE PATH OF THE FILE CONTAINING THE CERTIFICATE}

Fixes #383

@ValentinMoullet ValentinMoullet self-assigned this Apr 19, 2018
}

// Adapted from 'https://golang.org/src/crypto/tls/generate_cert.go'
func generateSelfSignedCert() (string, string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please simplify this method to only use what you're giving in the var-part.


// Adapted from 'https://golang.org/src/crypto/tls/generate_cert.go'
func generateSelfSignedCert() (string, string, error) {
var (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var is very useful outside of methods, but not needed inside. You can directly write:

host := "127.0.0.1"


var priv *ecdsa.PrivateKey
var err error
switch ecdsaCurve {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove case

var notBefore time.Time
if len(validFrom) == 0 {
notBefore = time.Now()
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only keep else

}

hosts := strings.Split(host, ",")
for _, h := range hosts {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove for - there is only one host.


hosts := strings.Split(host, ",")
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only keep this if

Val: 10,
}
sr := &SimpleResponse{}
assert.Equal(t, uint64(0), client.Rx())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need require here, else the test will continue, even if this fails.

@jeffallen
Copy link
Contributor

jeffallen commented Apr 25, 2018

local.go Outdated

// newTCPServerWithWebSocketTLS creates a new server with a tcpRouter with
// "localhost:"+port as an address and configure TLS for its websocket.
func newTCPServerWithWebSocketTLS(s network.Suite, port int, path string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is repeated code with respect to newTCPServer, right? Better would be to move the h.Start() and listening check out, and then call newTCPServer in both the TLS and non-TLS case. In the TLS case, add a call to SetWebsocketTLS.

Private methods are private for a reason: you are free to hack on them as much as necessary in order to make your new feature fit in without massive copy/paste. Becaues those copy/pastes become a huge anchor around our neck later.

server.go Outdated
}

// SetWebsocketTLS sets the TLS configuration for its websocket.
func (c *Server) SetWebsocketTLS(tlsCertificate []byte, tlsCertificateKey []byte) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is WebSocket (capital S) so all methods that talk about it should use the same capitalization.

websocket.go Outdated
if err != nil {
return err
}
w.server.Server.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this API, you make it impossible for the caller to customize the tls.Config if they need to (and we will need to, in order to support LetsEncrypt via the golang.org/x/crypto/acme/autocert package) Another way to do this would be for type WebSocket to have a field called TLSConfig, with a comment saying, "can only be modified before Start is called". Then in app/config.go, instead of calling server.SetWebsockerTLS, you'd set server.WebSocket.TLSConfig as needed. the private field websocket in type onet.Server would need to be exported, but I see no problem with that.

Go prefers correct defaults, and empowering the caller to override them by sending in custom configs. And on balance, a reasonable new variable export (onet.Server.websocket) is better than two new exported APIs.

websocket.go Outdated
tls bool
// Pool of certificates we trust, can be used for self-signed certificates;
// only useful if tls is set to true.
trustedCertificates *x509.CertPool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the server side:

This makes it impossible for callers to change the tls.Config if they want to. Instead, give them a TLSClientConfig field they can set, and then in Send(), do d := &websocket.Dialer{ TLSClientConfig: c.TLSClientConfig }

websocket.go Outdated
}

var wsProtocol string
if c.tls {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see how the caller makes c.tls == true.

websocket.go Outdated
wsProtocol = "ws"
}
serverURL := fmt.Sprintf("%s://%s/%s/%s", wsProtocol, url, c.service, path)
headers := http.Header{"Origin": []string{"http://" + url}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this http be https if wsProtocol is wss?


const (
// String is a CertificateURL type containing a certificate.
String CertificateURLType = "string"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another proposal: the type should default to file, so that WebSocketTLSCertificate = "/etc/conode.crt" will do what any reasonable person would expect.

But since it is already here, WebSocketTLSCertificate = "string:XXX" should work too. (Although it is unclear that YAML can accept the embedded newlines that you typically find in PEM-encoded cert files.)

I do like the type:blob format, because it will help with LetsEncrypt. We can add LetsEncrypt support here like this: WebSocketTLSCertificate = "le:hostname.example.com" (LE requires that the server know what it's public FQDN is at the moment it requests the certificate.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! Yes I can do that. Also, are you saying we should change the delimiter from "://" to ":", or is "://" still fine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's a mistake. I propose that the following work:

  • "filename.pem"
  • "file://filename.pem"
  • "string://certificate-goes-here"
  • "letsencrypt://FQDN-goes-here" (later, in another commit)

Address network.Address
ListenAddress string
Description string
WebSocketTLSCertificate CertificateURL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There needs to be some new code in config_test.go proving that YAML can handle string:foo\nbar\nbaz correctly, because PEM-encoded certs have newlines in them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you mean TOML, not YAML. But that's a fair concern. By checking the doc (https://github.com/toml-lang/toml) it seems it should not be an issue, right? I can still write a test for that (ParseCothority), since there isn't a test for loading and parsing the private.toml file in config_test.go anyway.

@jeffallen
Copy link
Contributor

We also chatted on Slack about the fact that "go test -v" fails while "go test" succeeds.

certFile.WriteString(wsTLSCert)
certFile.Close()

keyFile, err := ioutil.TempFile("", "temp_key.pem")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from godoc for TempFile: "It is the caller's responsibility to remove the file when no longer needed" See: https://godoc.org/io/ioutil#example-TempFile

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh right! I was in fact looking at examples in our codebase and didn't see those removed, thus I assumed it was automatically done. For example, if you look in platform_test.go and search for ioutil.TempFile, are any of those removed in any way? Or should we add an issue to fix those?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you can add an issue, thanks.

@jeffallen jeffallen merged commit 2111ca6 into dedis:master May 3, 2018
@ValentinMoullet ValentinMoullet deleted the onet_fork/issue_383 branch May 3, 2018 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants