Skip to content

Commit

Permalink
ssl proxy suport (#16)
Browse files Browse the repository at this point in the history
* chore: adjusted handlers

* feat: added ssl support to http

* feat(proxy): added certs from envs

* feat(proxy): adjusted example to use cert
  • Loading branch information
paulobressan authored Feb 21, 2024
1 parent e9b29a6 commit 873fcc4
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 19 deletions.
72 changes: 64 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion proxy/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
fake-api
.env*
.env*
cert
6 changes: 5 additions & 1 deletion proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ tokio = { version = "1.35.1", features = ["full"] }
tokio-tungstenite = "0.21.0"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = "2.5.0"
url = "2.5.0"
rustls-pemfile = "2.1.0"
rustls = "0.22.2"
rustls-pki-types = "1.3.0"
tokio-rustls = "0.25.0"
2 changes: 2 additions & 0 deletions proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The proxy exposes metrics about HTTP requests and WebSocket frames.
| PROXY_ADDR | "0.0.0.0:8100" |
| PROMETHEUS_ADDR | "0.0.0.0:5000" |
| OGMIOS_PORT | - |
| SSL_CRT_PATH | file.crt |
| SSL_KEY_PATH | file.key |


## Commands
Expand Down
70 changes: 69 additions & 1 deletion proxy/examples/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,62 @@ spec:
protocol: TCP
---
# Run proxy
apiVersion: v1
data:
localhost.crt: |
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIUM+uDlS5+M0PRTrSwcAlAMC1zYkwwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDIyMDE3Mjk1OFoXDTI0MDMy
MTE3Mjk1OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAn9O3c1TMOzrYgA2zRoxokV31pMhARUDc9i4h7IFYNubz
AHJI0ThZ8XWFtyWgWsKSjJOM1V3H3wzvpR9TDK7ksUjocP8a385Hy5dNtiWHaM0Z
lCVVqAxps6T3qqXMRU/ttnfzHwToiWTjiSFP9RszCmXJigYVoNW4YPwpH8+EdPrU
C+CNWRf6P3OvhKY9djSNAmrKWl1snc9n/LwGofKoC2ggmyYYZZqtySAcsoHlk58q
n0nik5+rqCBKqUSEnGoRU+iz/xbrXB+2MGAsk/v6CXA6rQvdCDW2Z+e0JdqqszXM
NpN4bVR107mxPia2tvMLJrMaVbFv1ysHNbQWoXQg3wIDAQABo1kwVzAUBgNVHREE
DTALgglsb2NhbGhvc3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB
MB0GA1UdDgQWBBRYCnOgk68CN7DUMTPlrvo7hZxRVDANBgkqhkiG9w0BAQsFAAOC
AQEATBDWZ29zGjB9uzeEy8nsRK3KtTSRXHLF7Haog9Q0BAYD/nGZVQk0PzMBbAAs
T3vtA+RWGt3qXuxCEjqxIVFUZGO1JPOk05T9rrV2iv+cBBrgPLka7yeZDlmdRE9k
/Y6O43nZrGidqDjA5Na+S+vqMA4SRlp2Nd8vtbGaNxUkm+8VYaI2EgeBB6LtbtAj
GjwWDj2sR2bh4Rx9VhNg1DhCBjN52ww5gC7UzDlYSFB3vcA9CX7WIHvfHcXSJ8oV
TkR8NJopLMlwUFazsNBe7kEZeiyv4XPKiCysUKfh6q0VhAUjlVgg6Ljd38N5e6ep
Fzcbv6g2bfI4vKicDA7Bqi/xNA==
-----END CERTIFICATE-----
localhost.key: |
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCf07dzVMw7OtiA
DbNGjGiRXfWkyEBFQNz2LiHsgVg25vMAckjROFnxdYW3JaBawpKMk4zVXcffDO+l
H1MMruSxSOhw/xrfzkfLl022JYdozRmUJVWoDGmzpPeqpcxFT+22d/MfBOiJZOOJ
IU/1GzMKZcmKBhWg1bhg/Ckfz4R0+tQL4I1ZF/o/c6+Epj12NI0CaspaXWydz2f8
vAah8qgLaCCbJhhlmq3JIByygeWTnyqfSeKTn6uoIEqpRIScahFT6LP/FutcH7Yw
YCyT+/oJcDqtC90INbZn57Ql2qqzNcw2k3htVHXTubE+Jra28wsmsxpVsW/XKwc1
tBahdCDfAgMBAAECggEAGiQ88JuWvrwEnAf292ZmgXBcNKme/t95xLe4S2tp4aur
ZLqh97vp1OP4t64V060uNIQQbaMQhVvOTrRtlw4m0GsxFFpa1kRyhcr8+6VDx/vU
CovYviCETCKCx+H6eVPLeSRBcHoTDCCjqX2jYF8kiAzXXBYGr5natdzzX6/yOwJm
zMfrJdTDll4njIfThk06dZA7dpGxSRxK/689J/TNG/sT6PWduZOOQ4gZDIlBwG9s
mNzbZo+YrS9OreV3cMq8MnPDQKcIpxCogoiXlDIY9iFL8BfKqpjKlYoIvWR5FuNu
XFiKqBZxtK3dFfQKX5btabICiKggeutUZ/upa5gfoQKBgQDXqU20v22ikNm6qj32
m9bCp4edJ68JJWU6JARCbb9gDXtcP6P+oSteoSihY9RikI5rEM9lvVjDpzjgbJEV
3aQxBTHT9MKweona+jhqNgqZnea4d3h4uTu24fINWhxedeWWFgbFDbBt/lnmRv4k
Kj/r3O/D/1/l9dCsdOw3Thw/2QKBgQC9uNmyrnX5m3xoiZr0BuFpCl9SrHfYaOKz
jg6IqUV0M+z+Fmefen9EKQpuIA16oPYqr0ogyx/GFpbbI4aU+3H2eTm1UDeSenJ5
I6DayZhZM8Y3G1PtTYJBy87QB7C0klpa+X0jML3seDp6jQgNnWbqGIgvDyJWHFy2
B+ZkWcErdwKBgFxWJOsquypLkq2Vjoo0FzOovyvOfecQl9LY8OnwS2w42YSZywGO
yB7wKZFQSPMaqZ+1xtbsx0CeLIAKe+Q8zbwfWUJDHcip7rRPRjBTix5SuSJqJK6r
wKGBBD4rQtI+8FnefG+KeOvfZ2ZtJwsc+9lk81Ob19eB9CKivTDAxN+hAoGAH0SE
1Hb+SIoAofXzzL4JjldASI7WHZuDqVYDPTCwmqsoJuQoZdc5fFFLP8UWk5xNldFX
5Tm03d/BMxKSzqD2MkneYex7jC+UCDUAAK7y5dirlU9ysIxyqEdfqVdrHwdzzsSJ
hDA3TO6vrJzrs9q6KGCsqRzUat63xOReazGDrZcCgYAOC+ZXjnvb4sHffmUGZn7R
p1JvAS1eoOgwR284jS3DGdNRYxXsaqpD5SLpLzWDXPDfsPKDg613MjaBrghceA9i
1+pvxRA1z1SjK0UtkVc280pRwNZjue/GtNnc0KgkqN3guJ5WeDumBxmStJ5KGNYG
T4jj2oxkaijVhOEOzUkq4w==
-----END PRIVATE KEY-----
kind: ConfigMap
metadata:
name: proxy-vol
namespace: prj-mainnet-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
Expand Down Expand Up @@ -176,6 +232,18 @@ spec:
value: "0.0.0.0:9187"
- name: OGMIOS_PORT
value: "80"
- name: SSL_CRT_PATH
value: "/certs/localhost.crt"
- name: SSL_KEY_PATH
value: "/certs/localhost.key"
volumeMounts:
- name: certs
mountPath: /certs

volumes:
- name: certs
configMap:
name: proxy-vol
---
apiVersion: v1
kind: Service
Expand Down Expand Up @@ -310,4 +378,4 @@ metadata:
namespace: prj-mainnet-test
spec:
network: "mainnet"
version: 1
version: 1
10 changes: 9 additions & 1 deletion proxy/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::env;
use std::{env, path::PathBuf};

#[derive(Debug, Clone)]
pub struct Config {
pub proxy_addr: String,
pub proxy_namespace: String,
pub prometheus_addr: String,
pub ogmios_port: u16,
pub ssl_crt_path: PathBuf,
pub ssl_key_path: PathBuf,
}

impl Config {
Expand All @@ -14,6 +16,12 @@ impl Config {
proxy_addr: env::var("PROXY_ADDR").expect("PROXY_ADDR must be set"),
proxy_namespace: env::var("PROXY_NAMESPACE").unwrap_or("ftr-ogmios-v1".into()),
prometheus_addr: env::var("PROMETHEUS_ADDR").expect("PROMETHEUS_ADDR must be set"),
ssl_crt_path: env::var("SSL_CRT_PATH")
.map(|e| e.into())
.expect("SSL_CRT_PATH must be set"),
ssl_key_path: env::var("SSL_KEY_PATH")
.map(|e| e.into())
.expect("SSL_KEY_PATH must be set"),
ogmios_port: env::var("OGMIOS_PORT")
.expect("OGMIOS_PORT must be set")
.parse()
Expand Down
62 changes: 55 additions & 7 deletions proxy/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ use hyper::client::conn::http1 as http1_client;
use hyper::header::{
HeaderValue, CONNECTION, HOST, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_KEY, UPGRADE,
};
use hyper::server::conn::http1 as http1_server;
use hyper::service::service_fn;
use hyper::{Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto::Builder;
use rustls::ServerConfig;
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
use std::error::Error;
use std::fmt::Display;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::{fs, io};
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::RwLock;
use tokio_rustls::TlsAcceptor;
use tokio_tungstenite::tungstenite::handshake::derive_accept_key;
use tokio_tungstenite::tungstenite::protocol::Role;
use tokio_tungstenite::{connect_async, WebSocketStream};
Expand All @@ -41,6 +47,13 @@ pub async fn start(rw_state: Arc<RwLock<State>>) {
}
let listener = listener_result.unwrap();

let tls_acceptor_result = build_tls_acceptor(&state);
if let Err(err) = tls_acceptor_result {
error!(error = err.to_string(), "fail to load tls");
std::process::exit(1);
}
let tls_acceptor = tls_acceptor_result.unwrap();

info!(addr = state.config.proxy_addr, "proxy listening");

loop {
Expand All @@ -52,14 +65,23 @@ pub async fn start(rw_state: Arc<RwLock<State>>) {
}
let (stream, _) = accept_result.unwrap();

tokio::task::spawn(async move {
let io = TokioIo::new(stream);
let tls_acceptor = tls_acceptor.clone();

tokio::spawn(async move {
let tls_stream = match tls_acceptor.accept(stream).await {
Ok(tls_stream) => tls_stream,
Err(err) => {
error!(error = err.to_string(), "failed to perform tls handshake");
return;
}
};

let io = TokioIo::new(tls_stream);

let service = service_fn(move |req| handle(req, rw_state.clone()));

if let Err(err) = http1_server::Builder::new()
.serve_connection(io, service)
.with_upgrades()
if let Err(err) = Builder::new(TokioExecutor::new())
.serve_connection_with_upgrades(io, service)
.await
{
error!(error = err.to_string(), "failed proxy server connection");
Expand Down Expand Up @@ -246,3 +268,29 @@ impl ProxyRequest {
}
}
}

fn build_tls_acceptor(state: &State) -> Result<TlsAcceptor, Box<dyn Error>> {
let certs = load_certs(&state.config.ssl_crt_path)?;

let key = load_private_key(&state.config.ssl_key_path)?;

let server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.unwrap();

let tls_acceptor = TlsAcceptor::from(Arc::new(server_config));
Ok(tls_acceptor)
}

fn load_certs(path: &PathBuf) -> io::Result<Vec<CertificateDer<'static>>> {
let cert_file = fs::File::open(path)?;
let mut reader = io::BufReader::new(cert_file);
rustls_pemfile::certs(&mut reader).collect()
}

fn load_private_key(path: &PathBuf) -> io::Result<PrivateKeyDer<'static>> {
let key_file = fs::File::open(path)?;
let mut reader = io::BufReader::new(key_file);
rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap())
}

0 comments on commit 873fcc4

Please sign in to comment.