tls support
When will TLS 1.3 support with tarpc come back?
Hey, thanks for the question! Tarpc in its latest iteration is generic over the transport layer. My expectation is you should be able to wrap your transport in a TLS later without requiring any changes to the tarpc service API. Can you provide some details on what you've tried thus far?
I haven't tried anything with tls so far. I did try the example_service client and server and found them to be exactly what you claim tarpc to be....easy to use but constrained to exposing only one service from one server as there is no documentation demonstrating many services from one server.
In order for tarpc to become more popular, there would need to be more documentation and examples i.e. an example_service demonstrating tls 1.3 with it. i.e. an example_service demonstrating exposing many services from one server.
As it stands, as user looking at the code, it is difficult for me to quickly add the necessary capability to become useful for me. I am still finding golang's rpcx package easier to use to be quite honest. I haven't found a rust equivalent to this easy to use rpc package.
Thanks for the feedback! Adding an example showing TLS usage is a great idea. I'd definitely take a contribution if you were interested in working on such an example.
I tried wrapping tarpc_bincode around tokio_tls::TlsConnector, however tarpc_bincode requires tokio_tcp::TCPStream, maybe it should be around anything that implements Stream?
Hmm, it is supposed to be generic over AsyncRead and AsyncWrite. Did you see Transport::from(t)?
No I didn't, building on top of the hello example
let server_addr = flags.value_of("server_addr").unwrap();
let server_addr = server_addr
.parse::<SocketAddr>()
.unwrap_or_else(|e| panic!(r#"--serve_addr value "{}" invalid: {}"#, server_addr, e));
let socket = tokio::net::TcpStream::connect(&server_addr).await?;
let connector = native_tls::TlsConnector::builder().build().unwrap();
let connector = tokio_tls::TlsConnector::from(connector);
let socket = connector.connect("google.com", socket).await.unwrap();
let transport = tarpc_bincode_transport::Transport::from(socket);
let mut client = = service::WorldClient::new(client::Config::default(), transport).spawn()?;
Gives me:
error[E0277]: the trait bound
tokio_tls::TlsStream<tokio_net::tcp::stream::TcpStream>: tokio_io::async_read::AsyncReadis not satisfied
but it looks like both tokio_tls::TlsStream and tokio_net::tcp::stream::TcpStream implement AsyncRead not sure what is missing here.
I am using this to connect over TLS to a tarpc server behind a reverse TLS proxy. It might be of some use if anyone has trouble setting up TLS.
async fn connect_tcp(domain: &str, port: u16) -> Result<TlsStream<TcpStream>, std::io::Error> {
use std::sync::Arc;
let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load os certificates") {
roots.add(&rustls::Certificate(cert.0)).unwrap();
}
let config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(config));
let servername = rustls::ServerName::try_from(domain).unwrap();
let host = format!("{}:{}", domain, port);
let stream = TcpStream::connect(host).await?;
connector.connect(servername, stream).await
}
let stream = connect_tcp(domain, port).await?;
let transport = tarpc::serde_transport::Transport::from((stream, Bincode::default()));
let client = WorldClient::new(Config::default(), transport).spawn();
edit in response to question below (2023-02-26): The snippet above was put together from: https://github.com/dvdsk/WorldSync/blob/157999b02dd0af00af67be1f23e0fac710b4dcd1/protocol/src/connect.rs#L13 and https://github.com/dvdsk/WorldSync/blob/157999b02dd0af00af67be1f23e0fac710b4dcd1/protocol/src/connect.rs#L40
note: variables are named slightly different there and this is all about 2 years old now. I can not guarantee it will still work.
@dvdsk It would be really great if you could also provide the used crates and use statements. I am after hours of fiddling not able to import correctly to solve all the incompatibilities. Especially i am no able to overcome the prolblem in:
36 | let connector = TlsConnector::from(config);
| ------------------ ^^^^^^ the trait `From<rustls::ClientConfig>` is not implemented for `TlsConnector`
| |
| required by a bound introduced by this call
|
= help: the trait `From<Arc<tokio_rustls::rustls::ClientConfig>>` is implemented for `TlsConnector`
@dvdsk Thanks for the help so far. I additionally noticed that my setup will need to run it's own tls server instead of running through an ssl-termination proxy. So i tried to combine here the example of tokio_rustls server with the tarpc example and your method @dvdsk to connect the tcp tls stream with tarpc::serde_transport:
https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/server/src/main.rs https://github.com/tokio-rs/tls/blob/master/tokio-rustls/tests/test.rs
https://github.com/google/tarpc/blob/master/example-service/src/server.rs
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// -------------------- start here to setup tls tcp tokio stream --------------------------
// https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/server/src/main.rs
// https://github.com/tokio-rs/tls/blob/master/tokio-rustls/tests/test.rs
let cert: Vec<Certificate> = certs(&mut to_mem_cursor(
// &Asset::get("server-cert.pem").expect("file load server-cert.pem"),
&Asset::get("end.cert").expect("file load server-cert.pem"),
))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
.map(|mut certs| certs.drain(..).map(Certificate).collect())
.unwrap();
let key: Vec<PrivateKey> = rsa_private_keys(&mut to_mem_cursor(
// &Asset::get("server-key.pem").expect("file load server-key.pem"),
&Asset::get("end.rsa").expect("file load server-key.pem"),
))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
.map(|mut keys| keys.drain(..).map(PrivateKey).collect())
.unwrap();
let flags = Flags::parse();
init_tracing("Tarpc Example Server").unwrap();
let server_addr = (IpAddr::V4(Ipv4Addr::LOCALHOST), flags.port);
let config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(
cert,
key.first().expect("get first tls server key").to_owned(),
)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))
.unwrap();
let acceptor = TlsAcceptor::from(Arc::new(config));
let listener = TcpListener::bind(&server_addr).await.unwrap();
let (stream, peer_addr) = listener.accept().await.unwrap();
let acceptor = acceptor.clone();
let mut stream = acceptor.accept(stream).await.unwrap();
// --------------- start here with tarpc handover to create server from tls stream ----------------------
// https://github.com/google/tarpc/blob/master/example-service/src/server.rs
// https://github.com/google/tarpc/issues/225#issuecomment-986263501
// let mut listener = tarpc::serde_transport::tcp::listen(&server_addr, Bincode::default).await?;
let mut listener = tarpc::serde_transport::Transport::from((stream, Bincode::default()));
// let mut listener = tarpc::serde_transport::tcp::listen(&server_addr, Bincode::default).await?;
// tracing::info!("Listening on port {}", listener.local_addr().port());
tracing::info!("Listening on port {}", peer_addr);
// listener.config_mut().max_frame_length(usize::MAX);
listener
// Ignore accept errors.
.filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults)
// Limit channels to 1 per IP.
.max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip())
// serve is generated by the service attribute. It takes as input any type implementing
// the generated World trait.
.map(|channel| {
let server = HelloServer(channel.transport().peer_addr().unwrap());
channel.execute(server.serve())
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {})
.await;
Ok(())
}
but here i get stuck with the problem of needing to specify the Item type in transport in the .filter_map method. But this was strangely not needed before in the pure tarpc example and i can not give a sepcific one here. Does maybe anyone have an idea here?
error[E0282]: type annotations needed for `Result<Item, std::io::Error>`
--> controller/src/server.rs:105:22
|
105 | .filter_map(|r| future::ready(r.ok()))
| ^
...
108 | .max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip())
| --------- type must be known at this point
|
help: consider giving this closure parameter an explicit type, where the type for type parameter `T` is specified
|
105 | .filter_map(|r: Result<Item, std::io::Error>| future::ready(r.ok()))
| ++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0282`.
@cguentherTUChemnitz I do not have time the coming few days however I do not mind adding an example on how to use TLS with tarpc
Earlier in the thread @tikue indicated they wouldn't mind accepting such a PR. That would also close this issue.
If you want @cguentherTUChemnitz we could work on it together? I also wouldn't mind with a code review. I'm in the European timezone btw.
You can email me at opensource@da_vi*d+sk*.dev (remove the _, + and *'s from the adress (they are there to feed the spam bots)
I was finally able to create a working example for tarpc using tls over tcp for server and client side. Please comment / review the linked PR
I think @cguentherTUChemnitz fixed this in https://github.com/google/tarpc/pull/398 so I'm closing this. If I missed something, feel free to reopen!