From 28edca1b63dc1c4aeb3d947884c6f161c6b2924e Mon Sep 17 00:00:00 2001 From: houseme Date: Tue, 1 Apr 2025 23:09:47 +0800 Subject: [PATCH] add rustls --- Cargo.lock | 4 ++++ Cargo.toml | 4 ++++ rustfs/Cargo.toml | 4 ++++ rustfs/src/config/mod.rs | 4 ++-- rustfs/src/main.rs | 31 ++++++++++++++++++++++++++----- rustfs/src/utils.rs | 27 +++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fea149c5e..147bd87dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6696,6 +6696,9 @@ dependencies = [ "query", "rmp-serde", "rust-embed", + "rustls 0.23.23", + "rustls-pemfile", + "rustls-pki-types", "s3s", "serde", "serde_json", @@ -6703,6 +6706,7 @@ dependencies = [ "shadow-rs", "time", "tokio", + "tokio-rustls 0.26.2", "tokio-stream", "tokio-util", "tonic", diff --git a/Cargo.toml b/Cargo.toml index ed7abe714..8e71d2a49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,9 @@ rfd = { version = "0.15.2", default-features = false, features = ["xdg-portal", rmp = "0.8.14" rmp-serde = "1.3.0" rust-embed = "8.6.0" +rustls = { version = "0.23" } +rustls-pki-types = "1.11.0" +rustls-pemfile = "2.2.0" s3s = { git = "https://github.com/Nugine/s3s.git", rev = "ab139f72fe768fb9d8cecfe36269451da1ca9779", default-features = true, features = [ "tower", ] } @@ -99,6 +102,7 @@ tokio = { version = "1.43.0", features = ["fs", "rt-multi-thread"] } tonic = { version = "0.12.3", features = ["gzip"] } tonic-build = "0.12.3" tonic-reflection = "0.12" +tokio-rustls = { version = "0.26", default-features = false } tokio-stream = "0.1.17" tokio-util = { version = "0.7.13", features = ["io", "compat"] } tower = { version = "0.5.2", features = ["timeout"] } diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index 4c506863c..da0f2ddf8 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -43,6 +43,9 @@ prost-types.workspace = true protos.workspace = true protobuf.workspace = true rmp-serde.workspace = true +rustls.workspace = true +rustls-pemfile.workspace = true +rustls-pki-types.workspace = true s3s.workspace = true serde.workspace = true serde_json.workspace = true @@ -55,6 +58,7 @@ tokio = { workspace = true, features = [ "net", "signal", ] } +tokio-rustls.workspace = true lazy_static.workspace = true tokio-stream.workspace = true tonic = { version = "0.12.3", features = ["gzip"] } diff --git a/rustfs/src/config/mod.rs b/rustfs/src/config/mod.rs index b6c3d3701..ada5307ef 100644 --- a/rustfs/src/config/mod.rs +++ b/rustfs/src/config/mod.rs @@ -9,11 +9,11 @@ pub const DEFAULT_SECRET_KEY: &str = "rustfsadmin"; /// Default TLS key for rustfs /// This is the default key for TLS. -pub const RUSTFS_TLS_KEY: &str = "rustfs_tls_key.pem"; +pub(crate) const RUSTFS_TLS_KEY: &str = "rustfs_tls_key.pem"; /// Default TLS cert for rustfs /// This is the default cert for TLS. -pub const RUSTFS_TLS_CERT: &str = "rustfs_tls_cert.pem"; +pub(crate) const RUSTFS_TLS_CERT: &str = "rustfs_tls_cert.pem"; #[allow(clippy::const_is_empty)] const SHORT_VERSION: &str = { diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index cd4f5c627..a34b182d7 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -9,13 +9,14 @@ mod utils; use crate::auth::IAMAuth; use crate::console::{init_console_cfg, CONSOLE_CONFIG}; +use crate::utils::error; use chrono::Datelike; use clap::Parser; use common::{ error::{Error, Result}, globals::set_global_addr, }; -use config::{DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY}; +use config::{DEFAULT_ACCESS_KEY, DEFAULT_SECRET_KEY, RUSTFS_TLS_CERT, RUSTFS_TLS_KEY}; use ecstore::heal::background_heal_ops::init_auto_heal; use ecstore::utils::net::{self, get_available_port}; use ecstore::{ @@ -26,6 +27,7 @@ use ecstore::{ update_erasure_type, }; use ecstore::{global::set_global_rustfs_port, notification_sys::new_global_notification_sys}; +use futures_util::TryFutureExt; use grpc::make_server; use hyper_util::{ rt::{TokioExecutor, TokioIo}, @@ -34,10 +36,13 @@ use hyper_util::{ }; use iam::init_iam_sys; use protos::proto_gen::node_service::node_service_server::NodeServiceServer; +use rustls::ServerConfig; use s3s::{host::MultiDomain, service::S3ServiceBuilder}; use service::hybrid; +use std::sync::Arc; use std::{io::IsTerminal, net::SocketAddr}; use tokio::net::TcpListener; +use tokio_rustls::TlsAcceptor; use tonic::{metadata::MetadataValue, Request, Status}; use tower_http::cors::CorsLayer; use tracing::{debug, error, info, warn}; @@ -211,6 +216,22 @@ async fn run(opt: config::Opt) -> Result<()> { }; let rpc_service = NodeServiceServer::with_interceptor(make_server(), check_auth); + let tls_path = opt.tls_path.clone().unwrap_or_default(); + let key_path = format!("{}/{}", tls_path, RUSTFS_TLS_KEY); + let cert_path = format!("{}/{}", tls_path, RUSTFS_TLS_CERT); + + let has_tls_certs = tokio::try_join!(tokio::fs::metadata(key_path.clone()), tokio::fs::metadata(cert_path.clone())).is_ok(); + + if has_tls_certs { + let certs = utils::load_certs(cert_path.as_str()).map_err(|e| error(e.to_string()))?; + let key = utils::load_private_key(key_path.as_str()).map_err(|e| error(e.to_string()))?; + let mut server_config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, key) + .map_err(|e| error(e.to_string()))?; + server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; + let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); + }; tokio::spawn(async move { let hyper_service = service.into_shared(); @@ -250,10 +271,10 @@ async fn run(opt: config::Opt) -> Result<()> { tokio::select! { () = graceful.shutdown() => { - tracing::debug!("Gracefully shutdown!"); + debug!("Gracefully shutdown!"); }, () = tokio::time::sleep(std::time::Duration::from_secs(10)) => { - tracing::debug!("Waited 10 seconds for graceful shutdown, aborting..."); + debug!("Waited 10 seconds for graceful shutdown, aborting..."); } } }); @@ -267,7 +288,7 @@ async fn run(opt: config::Opt) -> Result<()> { })?; ECStore::init(store.clone()).await.map_err(|err| { - error!("ECStore init faild {:?}", &err); + error!("ECStore init failed {:?}", &err); Error::from_string(err.to_string()) })?; debug!("init store success!"); @@ -275,7 +296,7 @@ async fn run(opt: config::Opt) -> Result<()> { init_iam_sys(store.clone()).await.unwrap(); new_global_notification_sys(endpoint_pools.clone()).await.map_err(|err| { - error!("new_global_notification_sys faild {:?}", &err); + error!("new_global_notification_sys failed {:?}", &err); Error::from_string(err.to_string()) })?; diff --git a/rustfs/src/utils.rs b/rustfs/src/utils.rs index 1496bd3f6..b90d942b4 100644 --- a/rustfs/src/utils.rs +++ b/rustfs/src/utils.rs @@ -1,4 +1,7 @@ +use rustls_pemfile::{certs, private_key}; +use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use std::net::IpAddr; +use std::{fs, io}; pub(crate) fn get_local_ip() -> Option { match local_ip_address::local_ip() { @@ -7,3 +10,27 @@ pub(crate) fn get_local_ip() -> Option { Ok(IpAddr::V6(_)) => todo!(), } } + +/// Load public certificate from file. +pub(crate) fn load_certs(filename: &str) -> io::Result>> { + // Open certificate file. + let cert_file = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; + let mut reader = io::BufReader::new(cert_file); + + // Load and return certificate. + certs(&mut reader).collect() +} + +/// Load private key from file. +pub(crate) fn load_private_key(filename: &str) -> io::Result> { + // Open keyfile. + let keyfile = fs::File::open(filename).map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; + let mut reader = io::BufReader::new(keyfile); + + // Load and return a single private key. + private_key(&mut reader).map(|key| key.unwrap()) +} + +pub(crate) fn error(err: String) -> io::Error { + io::Error::new(io::ErrorKind::Other, err) +}