-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
mtls_tunnel_and_service.rs
150 lines (136 loc) · 5.46 KB
/
mtls_tunnel_and_service.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! This example demonstrates how to create a mTLS tunnel proxy and a mTLS web service.
//! The mTLS tunnel proxy is a server that accepts mTLS connections, and forwards the mTLS transport stream to another service.
//! The mTLS web service is a server that accepts mTLS connections, and serves a simple web page.
//! You can learn more about this kind of proxy in [the rama book](https://ramaproxy.org/book/) at the [mTLS Tunnel Proxy](https://ramaproxy.org/book/proxies/tls.html) section.
//!
//! # Run the example
//!
//! ```sh
//! cargo run --example mtls_tunnel_and_service --features=http-full,rustls
//! ```
//!
//! # Expected output
//!
//! The server will start and listen on `:63014`. You can use `curl` to interact with the service:
//!
//! ```sh
//! curl -k -v https://127.0.0.1:63014
//! ```
//!
//! This won't work as the client is not authorized. You can use `curl` to interact with the service:
//!
//! ```sh
//! curl -v http://127.0.0.1:62014/hello
//! ```
//!
//! You should see a response with `HTTP/1.1 200 OK` and a body with `Hello, authorized client!`.
// rama provides everything out of the box to build mtls web services and proxies
use rama::{
graceful::Shutdown,
http::{
layer::trace::TraceLayer,
response::{Html, Redirect},
server::HttpServer,
service::web::WebService,
},
layer::TraceErrLayer,
net::address::{Authority, Host},
net::tls::client::{ClientAuth, ServerVerifyMode},
net::tls::client::{ClientConfig, ClientHelloExtension},
net::tls::server::{ClientVerifyMode, SelfSignedData, ServerAuth, ServerConfig},
net::tls::{ApplicationProtocol, DataEncoding},
rt::Executor,
tcp::client::service::Forwarder,
tcp::client::service::TcpConnector,
tcp::server::TcpListener,
tls::rustls::client::{TlsConnectorData, TlsConnectorLayer},
tls::rustls::server::{TlsAcceptorData, TlsAcceptorLayer},
Layer,
};
// everything else is provided by the standard library, community crates or tokio
use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration;
use tracing::metadata::LevelFilter;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
const LOCALHOST: Host = Host::Address(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
const SERVER_AUTHORITY: Authority = Authority::new(LOCALHOST, 63014);
const TUNNEL_AUTHORITY: Authority = Authority::new(LOCALHOST, 62014);
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::DEBUG.into())
.from_env_lossy(),
)
.init();
let shutdown = Shutdown::default();
// generate client connector data
let tls_client_data = TlsConnectorData::try_from(ClientConfig {
client_auth: Some(ClientAuth::SelfSigned),
server_verify_mode: Some(ServerVerifyMode::Disable),
extensions: Some(vec![ClientHelloExtension::ServerName(Some(
SERVER_AUTHORITY.into_host(),
))]),
..Default::default()
})
.expect("create tls connector data for client");
let tls_client_cert_chain: Vec<_> = tls_client_data
.client_auth_cert_chain()
.into_iter()
.flatten()
.map(|cert| cert.as_ref().to_vec())
.collect();
// generate server cert
let mut tls_server_config =
ServerConfig::new(ServerAuth::SelfSigned(SelfSignedData::default()));
tls_server_config.client_verify_mode =
ClientVerifyMode::ClientAuth(DataEncoding::DerStack(tls_client_cert_chain));
tls_server_config.application_layer_protocol_negotiation = Some(vec![
ApplicationProtocol::HTTP_2,
ApplicationProtocol::HTTP_11,
]);
let tls_server_data =
TlsAcceptorData::try_from(tls_server_config).expect("create tls acceptor data for server");
// create mtls web server
shutdown.spawn_task_fn(|guard| async move {
let executor = Executor::graceful(guard.clone());
let tcp_service = TlsAcceptorLayer::new(tls_server_data).layer(
HttpServer::auto(executor).service(
TraceLayer::new_for_http().layer(
WebService::default()
.get("/", Redirect::temporary("/hello"))
.get("/hello", Html("<h1>Hello, authorized client!</h1>")),
),
),
);
tracing::info!("start mtls (https) web service: {}", SERVER_AUTHORITY);
TcpListener::bind(SERVER_AUTHORITY.to_string())
.await
.unwrap_or_else(|e| {
panic!("bind TCP Listener ({SERVER_AUTHORITY}): mtls (https): web service: {e}")
})
.serve_graceful(guard, tcp_service)
.await;
});
// create mtls tunnel proxy
shutdown.spawn_task_fn(|guard| async move {
tracing::info!("start mTLS TCP Tunnel Proxys: {}", TUNNEL_AUTHORITY);
let forwarder = Forwarder::new(SERVER_AUTHORITY).connector(
TlsConnectorLayer::tunnel(Some(SERVER_AUTHORITY.into_host()))
.with_connector_data(tls_client_data)
.layer(TcpConnector::new()),
);
// L4 Proxy Service
TcpListener::bind(TUNNEL_AUTHORITY.to_string())
.await
.expect("bind TCP Listener: mTLS TCP Tunnel Proxys")
.serve_graceful(guard, TraceErrLayer::new().layer(forwarder))
.await;
});
shutdown
.shutdown_with_limit(Duration::from_secs(30))
.await
.expect("graceful shutdown");
}