tungstenite-rs icon indicating copy to clipboard operation
tungstenite-rs copied to clipboard

Add example for usage with TLS

Open brunogauthier1 opened this issue 5 years ago • 13 comments
trafficstars

Hi,

Anyone have a working example of a secure WebSocket on localhost for example, client and server? I'm a bit confuse about how to instantiate the client and the server over TLS.

I simply did #[cfg(feature = "tls")] in both the .../example/client.rs and server.rs and use a wss prefix instead of ws in the client URL. But it crash.

Thanks for helping

Bruno

brunogauthier1 avatar Jun 04 '20 19:06 brunogauthier1

This works for the client but not for the server. In general, for the server you have to accept TLS connection yourself and then pass it to Tungstenite after accepting. This is done so because you must configure certificates and TLS options on the server side.

There are examples how to accept TLS connection in both native_tls and rustls, i.e. https://docs.rs/native-tls/0.2.4/native_tls/

What you do is just add Tungstenite to their example:

fn handle_client(stream: TlsStream<TcpStream>) {
    let mut websocket = tungstenite::accept(stream)
        .add_your_error_handling_here();
    // ...
}

Both native_tls and rustls can be used with Tungstenite.

agalakhov avatar Jun 05 '20 08:06 agalakhov

I don't really understand what is being said above. I have 2 cases: 1 - to connect using wss to a server and ignore any certificate problems or 2 - to connect using wss and to use a certificate file (.pem or .cert) that I have been given to verify the server's cert.

How do I do these 2 cases with a tungstenite client app?

captainmannering avatar Jun 11 '20 05:06 captainmannering

I think what Alexey meant is that tungstenite-rs does not add any additional layer of abstractions at this point. If you're writing a server that accepts wss, you have to create the corresponding TlsAcceptor (see example from native-tls). After the stream is accepted, you can pass the TlsStream to tungstenite::accept().

daniel-abramov avatar Jun 11 '20 11:06 daniel-abramov

Thanks, got that much working now.

captainmannering avatar Jun 12 '20 06:06 captainmannering

@captainmannering, How did you get it?

ProgrammingLife avatar Jun 23 '20 12:06 ProgrammingLife

@ProgrammingLife just try to use the example from native-tls (the link is in a comment above) and pass it to tungstenite::accept(), this should do the job.

daniel-abramov avatar Jun 24 '20 10:06 daniel-abramov

For the record, here's how I did it with rustls

  static KEY: &[u8] = include_bytes!("../keys/key.pem");    
  static CERT: &[u8] = include_bytes!("../keys/cert.pem");

[...]

      let mut tls_config: rustls::ServerConfig = rustls::ServerConfig::new(rustls::NoClientAuth::new());    
      tls_config.set_single_cert(    
          rustls::internal::pemfile::certs(&mut &*CERT).unwrap(),    
          rustls::internal::pemfile::pkcs8_private_keys(&mut &*KEY).unwrap()    
              .pop().unwrap()    
      ).unwrap();    
      let tls_config = Arc::new(tls_config);

      for stream in server.incoming() { 
          let tls_session = rustls::ServerSession::new(&tls_config);    
          let stream = rustls::StreamOwned::new(tls_session, stream.unwrap());
[...]
          let mut websocket = accept_hdr(stream, handshake_callback).unwrap();

I generated the certs with https://github.com/ctz/rustls/blob/0507dd0/bogo/regen-certs

aidanhs avatar Feb 13 '21 14:02 aidanhs

@aidanhs I am having issues getting a simple example working with rustls. Could you (or anyone else who knows these kinds of things) take a look? I have an example repo with information about how I created keys. It should be something simple, but I have been bashing my head against this all for quite some time and I am getting a bit disheartened to my own lack of skill with certs and tls.

// thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: 
// HandshakeError::Failure(Io(Custom { kind: InvalidData, error: AlertReceived(CertificateUnknown) }))'
let mut websocket = tungstenite::accept(tls_stream).unwrap(); // <- panicks

I am connecting to "wss://localhost:8443" through a web page (code included).

All I want is to be able to have a web page connect to a server that will host a game (in the end).

SauceChord avatar Jul 29 '21 01:07 SauceChord

I have not checked it in the detail, but it looks like you're using the self-signed certificates while relying on the default root certificate (i.e. your certificates are not "signed" by a CA and being rejected by the client that connects to your server), so the validation fails.

daniel-abramov avatar Aug 09 '21 13:08 daniel-abramov

I'd like to recommend another approach, using nginx to proxy TLS stream. So you could keep your websocket code nice and clean and leave TLS auth to nginx. Here's an nginx config example to do that: http://disq.us/p/20a6j7c in case anyone is intereted in.

hp-goldfinger avatar Nov 12 '21 09:11 hp-goldfinger

Yes, that's actually what we would generally suggest ;) I think most of the typical services nowadays do it like this, i.e. having a reverse-proxy that does the TLS.

daniel-abramov avatar Nov 15 '21 16:11 daniel-abramov