Skip to content

Commit

Permalink
Add TLS support (#81)
Browse files Browse the repository at this point in the history
When `-- features tls` is specified for tarpc, RPC communication can
also occur over a `TlsStream<TcpStream>` instead of a `TcpStream`.

* The functional tests have been refactored to use a common set of
functions for constructing the client and server structs so that all
the tests are shared across non-tls and tls test runs.

* Update pre-push to test TLS

* The `cfg_attr` logic caused many false warnings from clippy, so for now the crate docs for TLS are not tested.
  • Loading branch information
compressed authored and tikue committed Jan 31, 2017
1 parent c286c59 commit fafe569
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 78 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ before_script:
script:
- |
travis-cargo build && travis-cargo test
travis-cargo build && travis-cargo test &&
travis-cargo build -- --features tls && travis-cargo test -- --features tls
after_success:
- travis-cargo coveralls --no-sudo
Expand Down
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ license = "MIT"
documentation = "https://docs.rs/tarpc"
homepage = "https://github.com/google/tarpc"
repository = "https://github.com/google/tarpc"
keywords = ["rpc", "protocol", "remote", "procedure", "serialize"]
keywords = ["rpc", "protocol", "remote", "procedure", "serialize", "tls"]
readme = "README.md"
description = "An RPC framework for Rust with a focus on ease of use."

[dependencies]
bincode = "0.6"
byteorder = "0.5"
cfg-if = "0.1.0"
bytes = "0.3"
futures = "0.1.7"
lazy_static = "0.2"
log = "0.3"
native-tls = { version = "0.1.1", optional = true }
scoped-pool = "1.0"
serde = "0.8"
serde_derive = "0.8"
Expand All @@ -25,6 +27,7 @@ take = "0.1"
tokio-service = "0.1"
tokio-proto = "0.1"
tokio-core = "0.1"
tokio-tls = { version = "0.1", optional = true }
net2 = "0.2"

[dev-dependencies]
Expand All @@ -33,7 +36,12 @@ env_logger = "0.3"
futures-cpupool = "0.1"
clap = "2.0"

[target.'cfg(target_os = "macos")'.dev-dependencies]
security-framework = "0.1"

[features]
default = []
tls = ["tokio-tls", "native-tls"]
unstable = ["serde/unstable"]

[workspace]
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,85 @@ fn main() {
}
```

## Example: Futures + TLS

By default, tarpc internally uses a [`TcpStream`] for communication between your clients and
servers. However, TCP by itself has no encryption. As a result, your communication will be sent in
the clear. If you want your RPC communications to be encrypted, you can choose to use [TLS]. TLS
operates as an encryption layer on top of TCP. When using TLS, your communication will occur over a
[`TlsStream<TcpStream>`]. You can add the ability to make TLS clients and servers by adding `tarpc`
with the `tls` feature flag enabled.

When using TLS, some additional information is required. You will need to make [`TlsAcceptor`] and
`client::tls::Context` structs; `client::tls::Context` requires a [`TlsConnector`]. The
[`TlsAcceptor`] and [`TlsConnector`] types are defined in the [native-tls]. tarpc re-exports
external TLS-related types in its `native_tls` module (`tarpc::native_tls`).

[TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security
[`TcpStream`]: https://docs.rs/tokio-core/0.1/tokio_core/net/struct.TcpStream.html
[`TlsStream<TcpStream>`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsStream.html
[`TlsAcceptor`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsAcceptor.html
[`TlsConnector`]: https://docs.rs/native-tls/0.1/native_tls/struct.TlsConnector.html
[native-tls]: https://github.com/sfackler/rust-native-tls

Both TLS streams and TCP streams are supported in the same binary when the `tls` feature is enabled.
However, if you are working with both stream types, ensure that you use the TLS clients with TLS
servers and TCP clients with TCP servers.

```rust
#![feature(conservative_impl_trait, plugin)]
#![plugin(tarpc_plugins)]

extern crate futures;
#[macro_use]
extern crate tarpc;
extern crate tokio_core;

use futures::Future;
use tarpc::{client, server};
use tarpc::client::future::Connect;
use tarpc::util::{FirstSocketAddr, Never};
use tokio_core::reactor;
use tarpc::native_tls::{Pkcs12, TlsAcceptor};

service! {
rpc hello(name: String) -> String;
}

#[derive(Clone)]
struct HelloServer;

impl FutureService for HelloServer {
type HelloFut = futures::Finished<String, Never>;

fn hello(&mut self, name: String) -> Self::HelloFut {
futures::finished(format!("Hello, {}!", name))
}
}

fn get_acceptor() -> TlsAcceptor {
let buf = include_bytes!("test/identity.p12");
let pkcs12 = Pkcs12::from_der(buf, "password").unwrap();
TlsAcceptor::builder(pkcs12).unwrap().build().unwrap()
}

fn main() {
let addr = "localhost:10000".first_socket_addr();
let mut core = reactor::Core::new().unwrap();
let acceptor = get_acceptor();
HelloServer.listen(addr, server::Options::default()
.handle(core.handle())
.tls(acceptor)).wait().unwrap();
let options = client::Options::default().handle(core.handle()
.tls(client::tls::Context::new("foobar.com").unwrap()));
core.run(FutureClient::connect(addr, options)
.map_err(tarpc::Error::from)
.and_then(|client| client.hello("Mom".to_string()))
.map(|resp| println!("{}", resp)))
.unwrap();
}
```

## Tips

### Sync vs Futures
Expand Down
2 changes: 2 additions & 0 deletions hooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ run_cargo() {
rustup run $2 cargo $1 &>/dev/null
else
rustup run nightly cargo $1 --features unstable &>/dev/null
rustup run nightly cargo $1 --features unstable,tls &>/dev/null
fi
else
printf "${PREFIX} $VERB... "
cargo $1 &>/dev/null
cargo $1 --features tls &>/dev/null
fi
if [ "$?" != "0" ]; then
printf "${FAILURE}\n"
Expand Down
Loading

0 comments on commit fafe569

Please sign in to comment.