Server side tls code

This commit is contained in:
Arne Beer 2020-10-31 00:02:12 +01:00
parent 36435b3781
commit 3b2e711271
7 changed files with 179 additions and 16 deletions

88
Cargo.lock generated
View file

@ -136,6 +136,19 @@ version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
[[package]]
name = "async-tls"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400"
dependencies = [
"futures-core",
"futures-io",
"rustls",
"webpki",
"webpki-roots",
]
[[package]]
name = "async-trait"
version = "0.1.42"
@ -686,9 +699,9 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "indexmap"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
dependencies = [
"autocfg",
"hashbrown",
@ -1153,6 +1166,7 @@ version = "0.9.1-alpha.0"
dependencies = [
"anyhow",
"async-std",
"async-tls",
"async-trait",
"bincode",
"byteorder",
@ -1173,6 +1187,7 @@ dependencies = [
"proptest",
"psutil",
"rand",
"rustls",
"serde",
"serde_derive",
"serde_json",
@ -1289,6 +1304,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "ring"
version = "0.16.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"
@ -1301,6 +1331,19 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rustls"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b"
dependencies = [
"base64",
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "rusty-fork"
version = "0.3.0"
@ -1331,6 +1374,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.118"
@ -1443,6 +1496,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "static_assertions"
version = "1.1.0"
@ -1583,6 +1642,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "users"
version = "0.11.0"
@ -1720,6 +1785,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376"
dependencies = [
"webpki",
]
[[package]]
name = "wepoll-sys"
version = "3.0.1"

View file

@ -29,7 +29,9 @@ path = "shared/lib.rs"
[dependencies]
anyhow = "1"
async-std = { version = "1", features = ["attributes", "std"] }
async-tls = "0.11"
async-trait = "0.1"
rustls = "0.19"
dirs = "3"
chrono = { version = "^0.4", features = ["serde"] }
chrono-english = "^0.1.0"

View file

@ -1,25 +1,28 @@
use std::sync::mpsc::Sender;
use std::sync::Arc;
use anyhow::{bail, Result};
use async_std::task;
use async_tls::TlsAcceptor;
use log::{debug, info, warn};
use pueue::message::*;
use pueue::protocol::*;
use pueue::state::SharedState;
use pueue::tls::load_config;
use crate::cli::CliArguments;
use crate::instructions::handle_message;
use crate::streaming::handle_follow;
/// Poll the unix listener and accept new incoming connections.
/// Poll the listener and accept new incoming connections.
/// Create a new future to handle the message and spawn it.
pub async fn accept_incoming(
sender: Sender<Message>,
state: SharedState,
opt: CliArguments,
) -> Result<()> {
let (unix_socket_path, port) = {
let (unix_socket_path, tcp_info) = {
let state = state.lock().unwrap();
let shared = &state.settings.shared;
@ -27,29 +30,46 @@ pub async fn accept_incoming(
if shared.use_unix_socket {
(Some(shared.unix_socket_path.clone()), None)
} else {
// Otherwise use tcp sockets and a given port
// Otherwise use tcp sockets on a given port and host.
// Commandline argument overwrites the configuration files values for port.
// This also initializes the TLS acceptor.
let port = if let Some(port) = opt.port.clone() {
port
} else {
shared.port.clone()
};
(None, Some(port))
let config = load_config(&state.settings)?;
let acceptor = TlsAcceptor::from(Arc::new(config));
(None, Some((port, acceptor)))
}
};
let listener = get_listener(unix_socket_path, port).await?;
let listener = get_listener(unix_socket_path, tcp_info.clone()).await?;
loop {
// Poll if we have a new incoming connection.
let socket = listener.accept().await?;
// Poll incoming connections.
// We have to decide between Unix sockets and TCP sockets.
// In case of a TCP connection, we have to add a TLS layer.
let stream = if let Some((_, acceptor)) = tcp_info.clone() {
let stream = listener.accept().await?;
Box::new(acceptor.accept(stream).await?)
} else {
listener.accept().await?
};
//let socket = if let Some((_, ref acceptor)) = tcp_info {
// let stream = listener.accept().await?;
// Box::new(acceptor.accept(stream)?)
//} else {
// listener.accept().await?
//};
// Start a new task for the request
let sender_clone = sender.clone();
let state_clone = state.clone();
task::spawn(async move {
let _result = handle_incoming(socket, sender_clone, state_clone).await;
let _result = handle_incoming(stream, sender_clone, state_clone).await;
});
}
}

View file

@ -6,3 +6,4 @@ pub mod protocol;
pub mod settings;
pub mod state;
pub mod task;
pub mod tls;

View file

@ -4,6 +4,7 @@ use anyhow::{bail, Context, Result};
use async_std::io::{Read, Write};
use async_std::net::{TcpListener, TcpStream};
use async_std::os::unix::net::{UnixListener, UnixStream};
use async_tls::{server::TlsStream, TlsAcceptor};
use async_trait::async_trait;
/// A new trait, which can be used to represent Unix- and TcpListeners.
@ -31,9 +32,10 @@ impl GenericListener for UnixListener {
/// A new trait, which can be used to represent Unix- and TcpStream.
/// This is necessary to easily write generic functions where both types can be used.
pub trait GenericSocket: Read + Write + Unpin + Send + Sync {}
pub trait GenericSocket: Read + Write + Unpin + Send {}
impl GenericSocket for TcpStream {}
impl GenericSocket for UnixStream {}
impl GenericSocket for TlsStream<Box<dyn GenericSocket>> {}
/// Two convenient types, so we don't have type write Box<dyn ...> all the time.
pub type Listener = Box<dyn GenericListener>;
@ -73,7 +75,7 @@ pub async fn get_client(unix_socket_path: Option<String>, port: Option<String>)
/// which depends on the parameters.
pub async fn get_listener(
unix_socket_path: Option<String>,
port: Option<String>,
port: Option<(String, TlsAcceptor)>,
) -> Result<Listener> {
if let Some(socket_path) = unix_socket_path {
// Check, if the socket already exists
@ -95,7 +97,7 @@ pub async fn get_listener(
return Ok(Box::new(UnixListener::bind(socket_path).await?));
}
let port = port.unwrap();
let (port, _) = port.unwrap();
let address = format!("127.0.0.1:{}", port);
Ok(Box::new(TcpListener::bind(address).await?))
}

View file

@ -14,11 +14,16 @@ use crate::platform::directories::*;
/// All settings which are used by both, the client and the daemon
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Shared {
pub port: String,
pub secret: String,
pub pueue_directory: String,
pub use_unix_socket: bool,
pub unix_socket_path: String,
pub url: String,
pub port: String,
pub daemon_cert: PathBuf,
pub daemon_key: PathBuf,
pub client_cert: String,
}
/// All settings which are used by the client
@ -57,12 +62,19 @@ impl Settings {
pub fn new(require_config: bool, from_file: &Option<PathBuf>) -> Result<Settings> {
let mut config = Config::new();
config.set_default("shared.port", "6924")?;
let pueue_path = default_pueue_path()?;
config.set_default("shared.secret", gen_random_secret())?;
config.set_default("shared.pueue_directory", default_pueue_path()?)?;
config.set_default("shared.pueue_directory", pueue_path.clone())?;
config.set_default("shared.use_unix_socket", false)?;
config.set_default("shared.unix_socket_path", get_unix_socket_path()?)?;
config.set_default("shared.url", "localhost")?;
config.set_default("shared.port", "6924")?;
config.set_default("shared.tls_enabled", true)?;
config.set_default("shared.key_cert", pueue_path.clone() + "/daemon.key")?;
config.set_default("shared.daemon_cert", pueue_path.clone() + "/daemon.cert")?;
config.set_default("shared.client_cert", pueue_path + "/client.cert")?;
// Client specific config
config.set_default("client.read_local_logs", true)?;
config.set_default("client.show_expanded_aliases", false)?;

42
shared/tls.rs Normal file
View file

@ -0,0 +1,42 @@
use std::{
fs::File,
io::{self, BufReader},
path::Path,
};
use rustls::{
internal::pemfile::certs, internal::pemfile::rsa_private_keys, Certificate, NoClientAuth,
PrivateKey, ServerConfig,
};
use crate::settings::Settings;
/// Configure the server using rusttls
/// See https://docs.rs/rustls/0.16.0/rustls/struct.ServerConfig.html for details
///
/// A TLS server needs a certificate and a fitting private key
pub fn load_config(settings: &Settings) -> io::Result<ServerConfig> {
let certs = load_certs(&settings.shared.daemon_cert)?;
let mut keys = load_keys(&settings.shared.daemon_key)?;
// we don't use client authentication
let mut config = ServerConfig::new(NoClientAuth::new());
config
// set this server to use one cert together with the loaded private key
.set_single_cert(certs, keys.remove(0))
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
Ok(config)
}
/// Load the passed certificates file
fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
certs(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
}
/// Load the passed keys file
fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
rsa_private_keys(&mut BufReader::new(File::open(path)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
}