Merge branch 'openssl' into revproxy-openssl
This commit is contained in:
@@ -8,9 +8,9 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "0.2", features = [ "net", "io-util", "rt-threaded" ] }
|
tokio = { version = "0.2", features = [ "net", "io-util", "rt-threaded" ] }
|
||||||
tokio-rustls = "*"
|
openssl = "0.10"
|
||||||
rustls = "*"
|
tokio-openssl = "0.4"
|
||||||
futures-util = "*"
|
futures-util = "0.3"
|
||||||
toml = "*"
|
toml = "*"
|
||||||
serde = "*"
|
serde = "*"
|
||||||
serde_derive = "*"
|
serde_derive = "*"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_rustls::server::TlsStream;
|
use tokio_openssl::SslStream;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub port: i32,
|
pub port: u16,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub server: Vec<Server>,
|
pub server: Vec<Server>,
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ pub struct ServerCfg {
|
|||||||
pub cert: String,
|
pub cert: String,
|
||||||
pub cgi: String,
|
pub cgi: String,
|
||||||
pub usrdir: bool,
|
pub usrdir: bool,
|
||||||
pub port: i32,
|
pub port: u16,
|
||||||
pub proxy: HashMap<String, String>,
|
pub proxy: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use std::io;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::prelude::*;
|
use tokio::prelude::*;
|
||||||
use tokio_rustls::server::TlsStream;
|
use tokio_openssl::SslStream;
|
||||||
|
|
||||||
use crate::status;
|
use crate::status;
|
||||||
|
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
pub stream: TlsStream<TcpStream>,
|
pub stream: SslStream<TcpStream>,
|
||||||
pub peer_addr: SocketAddr,
|
pub peer_addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
src/main.rs
20
src/main.rs
@@ -22,10 +22,8 @@ use tokio::net::TcpListener;
|
|||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::prelude::*;
|
use tokio::prelude::*;
|
||||||
use tokio::runtime;
|
use tokio::runtime;
|
||||||
use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys};
|
use tokio_openssl::SslStream;
|
||||||
use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
|
use openssl::ssl::NameType;
|
||||||
use tokio_rustls::server::TlsStream;
|
|
||||||
use tokio_rustls::TlsAcceptor;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use mime_guess;
|
use mime_guess;
|
||||||
@@ -139,9 +137,9 @@ async fn handle_connection(mut con: conn::Connection, srv: &config::ServerCfg) -
|
|||||||
con.send_status(status::Status::ProxyRequestRefused, "Url doesn't match certificate!").await?;
|
con.send_status(status::Status::ProxyRequestRefused, "Url doesn't match certificate!").await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// TODO get port from config
|
|
||||||
match url.port() {
|
match url.port() {
|
||||||
Some(p) => { if p != 1965 {
|
Some(p) => { if p != srv.port {
|
||||||
con.send_status(status::Status::ProxyRequestRefused, "Wrong Port!").await?;
|
con.send_status(status::Status::ProxyRequestRefused, "Wrong Port!").await?;
|
||||||
}},
|
}},
|
||||||
None => {}
|
None => {}
|
||||||
@@ -268,9 +266,8 @@ fn main() -> io::Result<()> {
|
|||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let handle = runtime.handle().clone();
|
let handle = runtime.handle().clone();
|
||||||
let config = tls::get_tls_config(cfg.clone());
|
|
||||||
|
|
||||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
let acceptor = tls::acceptor_conf(cfg.clone())?;
|
||||||
|
|
||||||
let fut = async {
|
let fut = async {
|
||||||
let mut listener = TcpListener::bind(&addr).await?;
|
let mut listener = TcpListener::bind(&addr).await?;
|
||||||
@@ -280,13 +277,12 @@ fn main() -> io::Result<()> {
|
|||||||
let cmap = cmap.clone();
|
let cmap = cmap.clone();
|
||||||
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
let mut stream = acceptor.accept(stream).await?;
|
let mut stream = tokio_openssl::accept(&acceptor, stream).await.expect("Couldn't accept");
|
||||||
let (_, sni) = TlsStream::get_mut(&mut stream);
|
let sni = match stream.ssl().servername(NameType::HOST_NAME) {
|
||||||
let sni = match sni.get_sni_hostname() {
|
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let srv = match cmap.get(sni) {
|
let srv = match cmap.get(sni) {
|
||||||
Some(h) => h,
|
Some(h) => h,
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
111
src/tls.rs
111
src/tls.rs
@@ -1,72 +1,65 @@
|
|||||||
|
extern crate openssl;
|
||||||
|
extern crate tokio_openssl;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, BufReader};
|
use std::io::{self, BufReader};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rustls::sign::CertifiedKey;
|
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||||
use rustls::sign::{RSASigningKey, SigningKey};
|
use openssl::ssl::SslAcceptorBuilder;
|
||||||
use rustls::ClientHello;
|
use openssl::ssl::SniError;
|
||||||
use rustls::ResolvesServerCert;
|
use openssl::error::ErrorStack;
|
||||||
use tokio_rustls::rustls::internal::pemfile::{certs, pkcs8_private_keys};
|
use openssl::ssl::SslContextBuilder;
|
||||||
use tokio_rustls::rustls::{Certificate, PrivateKey};
|
use openssl::ssl::SslVersion;
|
||||||
|
use openssl::ssl::NameType;
|
||||||
|
use tokio_openssl::SslStream;
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
pub fn get_tls_config(cfg: config::Config) -> rustls::ServerConfig {
|
pub fn acceptor_conf(cfg: config::Config) -> Result<SslAcceptor, ErrorStack> {
|
||||||
let store = rustls::RootCertStore::empty();
|
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
|
||||||
let verifier = rustls::AllowAnyAnonymousOrAuthenticatedClient::new(store);
|
acceptor.set_min_proto_version(Some(SslVersion::TLS1_2))?;
|
||||||
let mut tls_config = rustls::ServerConfig::new(verifier);
|
let mut map = HashMap::new();
|
||||||
tls_config.cert_resolver = Arc::new(CertResolver::from_config(cfg).unwrap());
|
let mut num = 1;
|
||||||
|
for server in cfg.server.iter() {
|
||||||
tls_config
|
let mut ctx = SslContextBuilder::new(SslMethod::tls())?;
|
||||||
}
|
match ctx.set_private_key_file(&server.key, SslFiletype::PEM) {
|
||||||
|
Ok(c) => c,
|
||||||
pub fn load_certs(path: &String) -> io::Result<Vec<Certificate>> {
|
Err(e) => {
|
||||||
certs(&mut BufReader::new(File::open(path)?))
|
eprintln!("Error: Can't load key file");
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
|
return Err(e)
|
||||||
}
|
},
|
||||||
|
};
|
||||||
pub fn load_key(path: &String) -> PrivateKey {
|
match ctx.set_certificate_chain_file(&server.cert) {
|
||||||
let keyfile = File::open(path).expect("cannot open private key file");
|
Ok(c) => c,
|
||||||
let mut reader = BufReader::new(keyfile);
|
Err(e) => {
|
||||||
let key = pkcs8_private_keys(&mut reader).expect("file contains invalid pkcs8 private key");
|
eprintln!("Error: Can't load cert file");
|
||||||
return key[0].clone();
|
return Err(e)
|
||||||
}
|
},
|
||||||
|
};
|
||||||
// Rustls won't let self signed certs be used with sni which gemini requires.
|
let ctx = ctx.build();
|
||||||
// At 1.4.2 in https://gemini.circumlunar.space/docs/spec-spec.txt
|
map.insert(server.hostname.clone(), ctx.clone());
|
||||||
pub struct CertResolver {
|
if num == 1 {
|
||||||
map: HashMap<String, Box<CertifiedKey>>,
|
map.insert("default".to_string(), ctx);
|
||||||
}
|
num += 1;
|
||||||
|
|
||||||
impl CertResolver {
|
|
||||||
pub fn from_config(cfg: config::Config) -> Result<Self, ()> {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
for server in cfg.server.iter() {
|
|
||||||
let key = load_key(&server.key);
|
|
||||||
let certs = load_certs(&server.cert).unwrap();
|
|
||||||
let signing_key = RSASigningKey::new(&key).unwrap();
|
|
||||||
|
|
||||||
let signing_key_boxed: Arc<Box<dyn SigningKey>> = Arc::new(Box::new(signing_key));
|
|
||||||
map.insert(
|
|
||||||
server.hostname.clone(),
|
|
||||||
Box::new(rustls::sign::CertifiedKey::new(certs, signing_key_boxed)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CertResolver { map })
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ResolvesServerCert for CertResolver {
|
let ctx_builder = &mut *acceptor;
|
||||||
fn resolve(&self, client_hello: ClientHello) -> Option<CertifiedKey> {
|
ctx_builder.set_servername_callback(move |ssl, _alert| -> Result<(), SniError> {
|
||||||
if let Some(hostname) = client_hello.server_name() {
|
ssl.set_ssl_context({
|
||||||
if let Some(cert) = self.map.get(hostname.into()) {
|
let hostname = ssl.servername(NameType::HOST_NAME);
|
||||||
return Some(*cert.clone());
|
if let Some(host) = hostname {
|
||||||
|
if let Some(ctx) = map.get(host) {
|
||||||
|
&ctx
|
||||||
|
} else {
|
||||||
|
&map.get(&"default".to_string()).expect("Can't get default")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&map.get(&"default".to_string()).expect("Can't get default")
|
||||||
}
|
}
|
||||||
}
|
}).expect("Can't get sni");
|
||||||
|
Ok(())
|
||||||
None
|
});
|
||||||
}
|
Ok(acceptor.build())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user