bumped rustls & related pkgs (#1066)

This commit is contained in:
Eugene 2024-09-18 11:28:39 +02:00 committed by GitHub
parent 9ca95b7eb7
commit c191e54c07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 1002 additions and 565 deletions

1039
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,18 +7,18 @@ version = "0.10.2"
[dependencies]
anyhow = { version = "1.0", features = ["std"] }
async-trait = "0.1"
bytes = "1.3"
bytes = "1.4"
chrono = { version = "0.4", default-features = false }
futures = "0.3"
hex = "0.4"
mime_guess = { version = "2.0", default-features = false }
poem = { version = "1.3.50", features = [
poem = { version = "3.1", features = [
"cookie",
"session",
"anyhow",
"websocket",
] }
poem-openapi = { version = "2.0", features = [
poem-openapi = { version = "5.1", features = [
"swagger-ui",
"chrono",
"uuid",
@ -26,7 +26,7 @@ poem-openapi = { version = "2.0", features = [
] }
russh = { version = "0.44.1", features = ["legacy-ed25519-pkcs8-parser"] }
rust-embed = "8.3"
sea-orm = { version = "0.12.2", features = [
sea-orm = { version = "0.12", features = [
"runtime-tokio-rustls",
"macros",
], default-features = false }
@ -35,7 +35,7 @@ serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1.20", features = ["tracing"] }
tracing = "0.1"
uuid = { version = "1.2", features = ["v4", "serde"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
warpgate-common = { version = "*", path = "../warpgate-common" }
warpgate-core = { version = "*", path = "../warpgate-core" }
warpgate-db-entities = { version = "*", path = "../warpgate-db-entities" }

View file

@ -8,7 +8,7 @@ version = "0.10.2"
anyhow = "1.0"
argon2 = "0.4"
async-trait = "0.1"
bytes = "1.3"
bytes = "1.4"
chrono = { version = "0.4", default-features = false, features = ["serde"] }
data-encoding = "2.3"
delegate = "0.6"
@ -16,8 +16,8 @@ humantime-serde = "1.1"
futures = "0.3"
once_cell = "1.17"
password-hash = "0.4"
poem = { version = "1.3.50", features = ["rustls"] }
poem-openapi = { version = "2.0", features = [
poem = { version = "3.1", features = ["rustls"] }
poem-openapi = { version = "5.1", features = [
"swagger-ui",
"chrono",
"uuid",
@ -38,8 +38,8 @@ totp-rs = { version = "5.0", features = ["otpauth"] }
tracing = "0.1"
tracing-core = "0.1"
url = "2.2"
uuid = { version = "1.2", features = ["v4", "serde"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
warpgate-sso = { version = "*", path = "../warpgate-sso" }
rustls = { version = "0.20", features = ["dangerous_configuration"] }
rustls = { version = "0.23", features = ["ring"], default-features = false}
rustls-pemfile = "1.0"
webpki = "0.22"

View file

@ -2,8 +2,8 @@ use std::path::Path;
use std::sync::Arc;
use poem::listener::RustlsCertificate;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::sign::{CertifiedKey, SigningKey};
use rustls::{Certificate, PrivateKey};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
@ -11,7 +11,7 @@ use crate::RustlsSetupError;
pub struct TlsCertificateBundle {
bytes: Vec<u8>,
certificates: Vec<Certificate>,
certificates: Vec<CertificateDer<'static>>,
}
pub struct TlsPrivateKey {
@ -36,8 +36,8 @@ impl TlsCertificateBundle {
let certificates = rustls_pemfile::certs(&mut &bytes[..]).map(|mut certs| {
certs
.drain(..)
.map(Certificate)
.collect::<Vec<Certificate>>()
.map(CertificateDer::from)
.collect::<Vec<CertificateDer>>()
})?;
if certificates.is_empty() {
return Err(RustlsSetupError::NoCertificates);
@ -61,17 +61,17 @@ impl TlsPrivateKey {
let mut key = rustls_pemfile::pkcs8_private_keys(&mut bytes.as_slice())?
.drain(..)
.next()
.map(PrivateKey);
.and_then(|x| PrivateKeyDer::try_from(x).ok());
if key.is_none() {
key = rustls_pemfile::rsa_private_keys(&mut bytes.as_slice())?
.drain(..)
.next()
.map(PrivateKey);
.and_then(|x| PrivateKeyDer::try_from(x).ok());
}
let key = key.ok_or(RustlsSetupError::NoKeys)?;
let key = rustls::sign::any_supported_type(&key)?;
let key = rustls::crypto::ring::sign::any_supported_type(&key)?;
Ok(Self { bytes, key })
}
@ -105,7 +105,6 @@ impl From<TlsCertificateAndPrivateKey> for CertifiedKey {
cert: cert.certificates,
key: key.key,
ocsp: None,
sct_list: None,
}
}
}

View file

@ -1,9 +1,11 @@
use rustls::server::VerifierBuilderError;
#[derive(thiserror::Error, Debug)]
pub enum RustlsSetupError {
#[error("rustls: {0}")]
Rustls(#[from] rustls::Error),
#[error("sign: {0}")]
Sign(#[from] rustls::sign::SignError),
#[error("verifier setup: {0}")]
VerifierBuilder(#[from] VerifierBuilderError),
#[error("no certificates found in certificate file")]
NoCertificates,
#[error("no private keys found in key file")]
@ -11,5 +13,5 @@ pub enum RustlsSetupError {
#[error("I/O: {0}")]
Io(#[from] std::io::Error),
#[error("PKI: {0}")]
Pki(#[from] webpki::Error),
Pki(webpki::Error),
}

View file

@ -20,8 +20,8 @@ futures = "0.3"
once_cell = "1.17"
packet = "0.1"
password-hash = "0.4"
poem = { version = "1.3.50", features = ["rustls"] }
poem-openapi = { version = "2.0", features = [
poem = { version = "3.1", features = ["rustls"] }
poem-openapi = { version = "5.1", features = [
"swagger-ui",
"chrono",
"uuid",
@ -30,7 +30,7 @@ poem-openapi = { version = "2.0", features = [
rand = "0.8"
rand_chacha = "0.3"
rand_core = { version = "0.6", features = ["std"] }
sea-orm = { version = "0.12.2", features = [
sea-orm = { version = "0.12", features = [
"runtime-tokio-rustls",
"macros",
], default-features = false }
@ -43,9 +43,9 @@ tracing = "0.1"
tracing-core = "0.1"
tracing-subscriber = "0.3"
url = "2.2"
uuid = { version = "1.2", features = ["v4", "serde"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
warpgate-sso = { version = "*", path = "../warpgate-sso" }
rustls = { version = "0.20", features = ["dangerous_configuration"] }
rustls = { version = "0.23", features = ["logging"], default-features = false }
rustls-pemfile = "1.0"
webpki = "0.22"

View file

@ -14,11 +14,11 @@ authors = [
[dependencies]
tokio = { version = "1.20", features = ["io-util"] }
bitflags = { version = "1.3", default-features = false }
bytes = "1.3"
bytes = "1.4"
futures-core = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false, features = [
"alloc",
"sink",
] }
memchr = { version = "2.5.0", default-features = false }
memchr = { version = "2.5", default-features = false }
thiserror = "1.0"

View file

@ -6,8 +6,8 @@ version = "0.10.2"
[dependencies]
chrono = { version = "0.4", default-features = false, features = ["serde"] }
poem-openapi = { version = "2.0", features = ["chrono", "uuid"] }
sea-orm = { version = "0.12.2", features = [
poem-openapi = { version = "5.1", features = ["chrono", "uuid"] }
sea-orm = { version = "0.12", features = [
"macros",
"with-chrono",
"with-uuid",
@ -15,5 +15,5 @@ sea-orm = { version = "0.12.2", features = [
], default-features = false }
serde = "1.0"
serde_json = "1.0"
uuid = { version = "1.2", features = ["v4", "serde"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
warpgate-common = { version = "*", path = "../warpgate-common" }

View file

@ -10,15 +10,15 @@ version = "0.10.2"
[dependencies]
async-std = { version = "^1.11", features = ["attributes"] }
chrono = { version = "0.4", default-features = false, features = ["serde"] }
sea-orm = { version = "0.12.2", features = [
sea-orm = { version = "0.12", features = [
"runtime-tokio-rustls",
"macros",
"with-chrono",
"with-uuid",
"with-json",
], default-features = false }
sea-orm-migration = { version = "0.12.2", default-features = false, features = [
sea-orm-migration = { version = "0.12", default-features = false, features = [
"cli",
] }
uuid = { version = "1.2", features = ["v4", "serde"] }
uuid = { version = "1.3", features = ["v4", "serde"] }
serde_json = "1.0"

View file

@ -8,13 +8,13 @@ version = "0.10.2"
anyhow = "1.0"
async-trait = "0.1"
chrono = { version = "0.4", default-features = false, features = ["serde"] }
cookie = "0.16"
cookie = "0.17"
data-encoding = "2.3"
delegate = "0.6"
futures = "0.3"
http = "0.2"
http = "1.0"
once_cell = "1.17"
poem = { version = "^1.3.50", features = [
poem = { version = "3.1", features = [
"cookie",
"session",
"anyhow",
@ -23,15 +23,15 @@ poem = { version = "^1.3.50", features = [
"sse",
"embed",
] }
poem-openapi = { version = "2.0", features = ["swagger-ui"] }
reqwest = { version = "0.11", features = [
poem-openapi = { version = "5.1", features = ["swagger-ui"] }
reqwest = { version = "0.12", features = [
"rustls-tls-native-roots",
"stream",
], default-features = false }
serde = "1.0"
serde_json = "1.0"
tokio = { version = "1.20", features = ["tracing", "signal"] }
tokio-tungstenite = { version = "0.17", features = ["rustls-tls-native-roots"] }
tokio-tungstenite = { version = "0.24", features = ["rustls-tls-native-roots"] }
tracing = "0.1"
warpgate-admin = { version = "*", path = "../warpgate-admin" }
warpgate-common = { version = "*", path = "../warpgate-common" }
@ -40,6 +40,6 @@ warpgate-db-entities = { version = "*", path = "../warpgate-db-entities" }
warpgate-web = { version = "*", path = "../warpgate-web" }
warpgate-sso = { version = "*", path = "../warpgate-sso" }
percent-encoding = "2.1"
uuid = { version = "1.2", features = ["v4"] }
uuid = { version = "1.3", features = ["v4"] }
regex = "1.6"
url = "2.4.1"
url = "2.4"

View file

@ -61,9 +61,9 @@ async fn get_target_for_request(
req: &Request,
services: &Services,
) -> poem::Result<Option<(Target, TargetHTTPOptions)>> {
let session: &Session = <_>::from_request_without_body(req).await?;
let session = <&Session>::from_request_without_body(req).await?;
let params: QueryParams = req.params()?;
let auth: Data<&SessionAuthorization> = <_>::from_request_without_body(req).await?;
let auth = Data::<&SessionAuthorization>::from_request_without_body(req).await?;
let selected_target_name;
let need_role_auth;

View file

@ -110,7 +110,7 @@ impl SessionAuthorization {
}
async fn is_user_admin(req: &Request, auth: &SessionAuthorization) -> poem::Result<bool> {
let services: Data<&Services> = <_>::from_request_without_body(req).await?;
let services = Data::<&Services>::from_request_without_body(req).await?;
let SessionAuthorization::User(username) = auth else {
return Ok(false);
@ -133,7 +133,7 @@ async fn is_user_admin(req: &Request, auth: &SessionAuthorization) -> poem::Resu
pub fn endpoint_admin_auth<E: Endpoint + 'static>(e: E) -> impl Endpoint {
e.around(|ep, req| async move {
let auth: Data<&SessionAuthorization> = <_>::from_request_without_body(&req).await?;
let auth = Data::<&SessionAuthorization>::from_request_without_body(&req).await?;
if is_user_admin(&req, &auth).await? {
return Ok(ep.call(req).await?.into_response());
}
@ -143,8 +143,8 @@ pub fn endpoint_admin_auth<E: Endpoint + 'static>(e: E) -> impl Endpoint {
pub fn page_admin_auth<E: Endpoint + 'static>(e: E) -> impl Endpoint {
e.around(|ep, req| async move {
let auth: Data<&SessionAuthorization> = <_>::from_request_without_body(&req).await?;
let session: &Session = <_>::from_request_without_body(&req).await?;
let auth = Data::<&SessionAuthorization>::from_request_without_body(&req).await?;
let session = <&Session>::from_request_without_body(&req).await?;
if is_user_admin(&req, &auth).await? {
return Ok(ep.call(req).await?.into_response());
}
@ -157,7 +157,7 @@ pub async fn _inner_auth<E: Endpoint + 'static>(
ep: Arc<E>,
req: Request,
) -> poem::Result<Option<E::Output>> {
let session: &Session = FromRequest::from_request_without_body(&req).await?;
let session = <&Session>::from_request_without_body(&req).await?;
Ok(match session.get_auth() {
Some(auth) => Some(ep.data(auth).call(req).await?),
@ -235,9 +235,9 @@ pub async fn get_auth_state_for_request(
}
pub async fn authorize_session(req: &Request, username: String) -> poem::Result<()> {
let session_middleware: Data<&Arc<Mutex<SessionStore>>> =
<_>::from_request_without_body(req).await?;
let session: &Session = <_>::from_request_without_body(req).await?;
let session_middleware =
Data::<&Arc<Mutex<SessionStore>>>::from_request_without_body(req).await?;
let session = <&Session>::from_request_without_body(req).await?;
let server_handle = session_middleware
.lock()

View file

@ -17,7 +17,7 @@ use std::time::Duration;
use anyhow::{Context, Result};
use async_trait::async_trait;
use common::page_admin_auth;
pub use common::{PROTOCOL_NAME, SsoLoginState};
pub use common::{SsoLoginState, PROTOCOL_NAME};
use http::HeaderValue;
use logging::{get_client_ip, log_request_result, span_for_request};
use poem::endpoint::{EmbeddedFileEndpoint, EmbeddedFilesEndpoint};

View file

@ -35,7 +35,7 @@ pub fn log_request_result(method: &Method, url: &Uri, client_ip: String, status:
}
pub async fn get_client_ip(req: &Request) -> poem::Result<String> {
let services: Option<Data<&Services>> = <_>::from_request_without_body(req).await.ok();
let services = Data::<&Services>::from_request_without_body(req).await.ok();
let trust_x_forwarded_headers = if let Some(services) = services {
let config = services.config.lock().await;
config.store.http.trust_x_forwarded_headers

View file

@ -1,4 +1,5 @@
use async_trait::async_trait;
use std::future::Future;
use http::header::Entry;
use poem::web::cookie::Cookie;
use poem::{Endpoint, IntoResponse, Middleware, Request, Response};
@ -25,29 +26,32 @@ impl<E: Endpoint> Middleware<E> for CookieHostMiddleware {
}
}
#[async_trait]
impl<E: Endpoint> Endpoint for CookieHostMiddlewareEndpoint<E> {
type Output = Response;
async fn call(&self, req: Request) -> poem::Result<Self::Output> {
let host = req.original_uri().host().map(|x| x.to_string());
fn call(&self, req: Request) -> impl Future<Output = poem::Result<Self::Output>> + Send {
async move {
let host = req.original_uri().host().map(|x| x.to_string());
let mut resp = self.inner.call(req).await?.into_response();
let mut resp = self.inner.call(req).await?.into_response();
if let Some(host) = host {
if let Entry::Occupied(mut entry) = resp.headers_mut().entry(http::header::SET_COOKIE) {
if let Ok(cookie_str) = entry.get().to_str() {
if let Ok(mut cookie) = Cookie::parse(cookie_str) {
if cookie.name() == SESSION_COOKIE_NAME {
cookie.set_domain(host);
if let Ok(value) = cookie.to_string().parse() {
entry.insert(value);
if let Some(host) = host {
if let Entry::Occupied(mut entry) =
resp.headers_mut().entry(http::header::SET_COOKIE)
{
if let Ok(cookie_str) = entry.get().to_str() {
if let Ok(mut cookie) = Cookie::parse(cookie_str) {
if cookie.name() == SESSION_COOKIE_NAME {
cookie.set_domain(host);
if let Ok(value) = cookie.to_string().parse() {
entry.insert(value);
}
}
}
}
}
}
Ok(resp)
}
Ok(resp)
}
}

View file

@ -1,4 +1,5 @@
use async_trait::async_trait;
use std::future::Future;
use poem::session::Session;
use poem::web::{Data, FromRequest};
use poem::{Endpoint, Middleware, Request};
@ -34,58 +35,59 @@ struct QueryParams {
ticket: Option<String>,
}
#[async_trait]
impl<E: Endpoint> Endpoint for TicketMiddlewareEndpoint<E> {
type Output = E::Output;
async fn call(&self, req: Request) -> poem::Result<Self::Output> {
let mut session_is_temporary = false;
let session: &Session = <_>::from_request_without_body(&req).await?;
let session = session.clone();
fn call(&self, req: Request) -> impl Future<Output = poem::Result<Self::Output>> {
async move {
let mut session_is_temporary = false;
let session = <&Session>::from_request_without_body(&req).await?;
let session = session.clone();
{
let params: QueryParams = req.params()?;
{
let params: QueryParams = req.params()?;
let mut ticket_value = None;
if let Some(t) = params.ticket {
ticket_value = Some(t);
}
for h in req.headers().get_all(http::header::AUTHORIZATION) {
let header_value = h.to_str().unwrap_or("").to_string();
if let Some((token_type, token_value)) = header_value.split_once(' ') {
if &token_type.to_lowercase() == "warpgate" {
ticket_value = Some(token_value.to_string());
session_is_temporary = true;
let mut ticket_value = None;
if let Some(t) = params.ticket {
ticket_value = Some(t);
}
for h in req.headers().get_all(http::header::AUTHORIZATION) {
let header_value = h.to_str().unwrap_or("").to_string();
if let Some((token_type, token_value)) = header_value.split_once(' ') {
if &token_type.to_lowercase() == "warpgate" {
ticket_value = Some(token_value.to_string());
session_is_temporary = true;
}
}
}
if let Some(ticket) = ticket_value {
let services = Data::<&Services>::from_request_without_body(&req).await?;
if let Some(ticket_model) = {
let ticket = Secret::new(ticket);
if let Some(res) = authorize_ticket(&services.db, &ticket).await? {
consume_ticket(&services.db, &res.id).await?;
Some(res)
} else {
None
}
} {
session.set_auth(crate::common::SessionAuthorization::Ticket {
username: ticket_model.username,
target_name: ticket_model.target,
});
}
}
}
if let Some(ticket) = ticket_value {
let services: Data<&Services> = <_>::from_request_without_body(&req).await?;
let resp = self.inner.call(req).await;
if let Some(ticket_model) = {
let ticket = Secret::new(ticket);
if let Some(res) = authorize_ticket(&services.db, &ticket).await? {
consume_ticket(&services.db, &res.id).await?;
Some(res)
} else {
None
}
} {
session.set_auth(crate::common::SessionAuthorization::Ticket {
username: ticket_model.username,
target_name: ticket_model.target,
});
}
if session_is_temporary {
session.clear();
}
resp
}
let resp = self.inner.call(req).await;
if session_is_temporary {
session.clear();
}
resp
}
}

View file

@ -5,7 +5,7 @@ use std::str::FromStr;
use anyhow::{Context, Result};
use cookie::Cookie;
use delegate::delegate;
use futures::{SinkExt, StreamExt};
use futures::{SinkExt, StreamExt, TryStreamExt};
use http::header::HeaderName;
use http::uri::{Authority, Scheme};
use http::Uri;
@ -313,7 +313,11 @@ async fn copy_client_body(
return Ok(());
}
response.set_body(Body::from_bytes_stream(client_response.bytes_stream()));
response.set_body(Body::from_bytes_stream(
client_response
.bytes_stream()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
));
Ok(())
}
@ -397,6 +401,7 @@ async fn proxy_ws_inner(
.body(())
.map_err(poem::error::InternalServerError)?,
None,
true,
)
.await
.map_err(poem::error::BadGateway)?;

View file

@ -1,9 +1,9 @@
use std::collections::{BTreeMap, HashMap};
use std::future::Future;
use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
use async_trait::async_trait;
use poem::session::{Session, SessionStorage};
use poem::session::{MemoryStorage, Session, SessionStorage};
use poem::web::{Data, RemoteAddr};
use poem::{FromRequest, Request};
use serde_json::Value;
@ -18,42 +18,50 @@ use crate::session_handle::{
};
#[derive(Clone)]
pub struct SharedSessionStorage(pub Arc<Mutex<Box<dyn SessionStorage>>>);
pub struct SharedSessionStorage(pub Arc<Mutex<Box<MemoryStorage>>>);
static POEM_SESSION_ID_SESSION_KEY: &str = "poem_session_id";
#[async_trait]
impl SessionStorage for SharedSessionStorage {
async fn load_session(
&self,
session_id: &str,
) -> poem::Result<Option<BTreeMap<String, Value>>> {
self.0.lock().await.load_session(session_id).await.map(|o| {
o.map(|mut s| {
s.insert(
POEM_SESSION_ID_SESSION_KEY.to_string(),
session_id.to_string().into(),
);
s
fn load_session<'a>(
&'a self,
session_id: &'a str,
) -> impl Future<Output = poem::Result<Option<BTreeMap<String, Value>>>> + Send + 'a {
async move {
self.0.lock().await.load_session(session_id).await.map(|o| {
o.map(|mut s| {
s.insert(
POEM_SESSION_ID_SESSION_KEY.to_string(),
session_id.to_string().into(),
);
s
})
})
})
}
}
async fn update_session(
&self,
session_id: &str,
entries: &BTreeMap<String, Value>,
/// Insert or update a session.
fn update_session<'a>(
&'a self,
session_id: &'a str,
entries: &'a BTreeMap<String, Value>,
expires: Option<Duration>,
) -> poem::Result<()> {
self.0
.lock()
.await
.update_session(session_id, entries, expires)
.await
) -> impl Future<Output = poem::Result<()>> + Send + 'a {
async move {
self.0
.lock()
.await
.update_session(session_id, entries, expires)
.await
}
}
async fn remove_session(&self, session_id: &str) -> poem::Result<()> {
self.0.lock().await.remove_session(session_id).await
/// Remove a session by session id.
fn remove_session<'a>(
&'a self,
session_id: &'a str,
) -> impl Future<Output = poem::Result<()>> + Send + 'a {
async move { self.0.lock().await.remove_session(session_id).await }
}
}
@ -78,7 +86,7 @@ impl SessionStore {
}
pub async fn process_request(&mut self, req: Request) -> poem::Result<Request> {
let session: &Session = <_>::from_request_without_body(&req).await?;
let session = <&Session>::from_request_without_body(&req).await?;
let request_counter = session.get::<u64>(REQUEST_COUNTER_SESSION_KEY).unwrap_or(0);
session.set(REQUEST_COUNTER_SESSION_KEY, request_counter + 1);
@ -97,14 +105,14 @@ impl SessionStore {
&mut self,
req: &Request,
) -> poem::Result<WarpgateServerHandleFromRequest> {
let session: &Session = <_>::from_request_without_body(req).await?;
let session = <&Session>::from_request_without_body(req).await?;
if let Some(handle) = self.handle_for(session) {
return Ok(handle.into());
}
let services = Data::<&Services>::from_request_without_body(req).await?;
let remote_address: &RemoteAddr = <_>::from_request_without_body(req).await?;
let remote_address = <&RemoteAddr>::from_request_without_body(req).await?;
let session_storage = Data::<&SharedSessionStorage>::from_request_without_body(req).await?;
let (session_handle, mut session_handle_rx) = HttpSessionHandle::new();

View file

@ -1,4 +1,5 @@
use std::any::type_name;
use std::future::Future;
use std::sync::Arc;
use poem::error::GetDataError;
@ -43,17 +44,21 @@ impl std::ops::Deref for WarpgateServerHandleFromRequest {
}
}
#[async_trait::async_trait]
impl<'a> FromRequest<'a> for WarpgateServerHandleFromRequest {
async fn from_request(req: &'a Request, _: &mut RequestBody) -> poem::Result<Self> {
let sm = Data::<&Arc<Mutex<SessionStore>>>::from_request_without_body(req).await?;
let session: &Session = <_>::from_request_without_body(req).await?;
Ok(sm
.lock()
.await
.handle_for(session)
.map(WarpgateServerHandleFromRequest)
.ok_or_else(|| GetDataError(type_name::<WarpgateServerHandle>()))?)
fn from_request(
req: &'a Request,
_: &mut RequestBody,
) -> impl Future<Output = poem::Result<Self>> {
async move {
let sm = Data::<&Arc<Mutex<SessionStore>>>::from_request_without_body(req).await?;
let session = <&Session>::from_request_without_body(req).await?;
Ok(sm
.lock()
.await
.handle_for(session)
.map(WarpgateServerHandleFromRequest)
.ok_or_else(|| GetDataError(type_name::<WarpgateServerHandle>()))?)
}
}
}

View file

@ -13,15 +13,15 @@ anyhow = { version = "1.0", features = ["std"] }
async-trait = "0.1"
tokio = { version = "1.20", features = ["tracing", "signal"] }
tracing = "0.1"
uuid = { version = "1.2", features = ["v4"] }
bytes = "1.3"
uuid = { version = "1.3", features = ["v4"] }
bytes = "1.4"
mysql_common = "0.29"
rand = "0.8"
sha1 = "0.10.5"
sha1 = "0.10"
password-hash = { version = "0.2", features = ["std"] }
rustls = { version = "0.20", features = ["dangerous_configuration"] }
rustls = { version = "0.23", features = ["ring"], default-features = false }
rustls-pemfile = "1.0"
tokio-rustls = "0.23"
tokio-rustls = "0.26"
thiserror = "1.0"
webpki = "0.22"
once_cell = "1.17"

View file

@ -97,7 +97,7 @@ impl MySqlClient {
.upgrade((
target
.host
.as_str()
.clone()
.try_into()
.map_err(|_| MySqlError::InvalidDomainName)?,
client_config,

View file

@ -63,12 +63,13 @@ impl ProtocolServer for MySQLProtocolServer {
}
};
let tls_config = ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(NoClientAuth::new())
.with_cert_resolver(Arc::new(ResolveServerCert(Arc::new(
certificate_and_key.into(),
))));
let tls_config =
ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()?
.with_client_cert_verifier(Arc::new(NoClientAuth))
.with_cert_resolver(Arc::new(ResolveServerCert(Arc::new(
certificate_and_key.into(),
))));
info!(?address, "Listening");
let listener = TcpListener::bind(address).await?;

View file

@ -3,7 +3,8 @@ use std::sync::Arc;
use std::task::Poll;
use async_trait::async_trait;
use rustls::{ClientConfig, ServerConfig, ServerName};
use rustls::pki_types::ServerName;
use rustls::{ClientConfig, ServerConfig};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tracing::*;
@ -126,7 +127,7 @@ impl<S> UpgradableStream<tokio_rustls::client::TlsStream<S>> for S
where
S: AsyncRead + AsyncWrite + Unpin + Send,
{
type UpgradeConfig = (ServerName, Arc<ClientConfig>);
type UpgradeConfig = (ServerName<'static>, Arc<ClientConfig>);
async fn upgrade(
mut self,

View file

@ -1,15 +1,17 @@
use std::io::Cursor;
use std::sync::Arc;
use std::time::SystemTime;
use rustls::client::{ServerCertVerified, ServerCertVerifier, WebPkiVerifier};
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::client::WebPkiServerVerifier;
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::server::{ClientHello, ResolvesServerCert};
use rustls::sign::CertifiedKey;
use rustls::{ClientConfig, Error as TlsError, ServerName};
use rustls::{CertificateError, ClientConfig, Error as TlsError, SignatureScheme};
use warpgate_common::RustlsSetupError;
use super::ROOT_CERT_STORE;
#[derive(Debug)]
pub struct ResolveServerCert(pub Arc<CertifiedKey>);
impl ResolvesServerCert for ResolveServerCert {
@ -23,10 +25,13 @@ pub async fn configure_tls_connector(
accept_invalid_hostnames: bool,
root_cert: Option<&[u8]>,
) -> Result<ClientConfig, RustlsSetupError> {
let config = ClientConfig::builder().with_safe_defaults();
let config =
ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()?;
let config = if accept_invalid_certs {
config
.dangerous()
.with_custom_certificate_verifier(Arc::new(DummyTlsVerifier))
.with_no_client_auth()
} else {
@ -36,14 +41,15 @@ pub async fn configure_tls_connector(
let mut cursor = Cursor::new(data);
for cert in rustls_pemfile::certs(&mut cursor)? {
cert_store.add(&rustls::Certificate(cert))?;
cert_store.add(CertificateDer::from(cert))?;
}
}
if accept_invalid_hostnames {
let verifier = WebPkiVerifier::new(cert_store, None);
let verifier = WebPkiServerVerifier::builder(Arc::new(cert_store)).build()?;
config
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoHostnameTlsVerifier { verifier }))
.with_no_client_auth()
} else {
@ -56,50 +62,105 @@ pub async fn configure_tls_connector(
Ok(config)
}
#[derive(Debug)]
pub struct DummyTlsVerifier;
impl ServerCertVerifier for DummyTlsVerifier {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: SystemTime,
) -> Result<ServerCertVerified, TlsError> {
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, TlsError> {
Ok(HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
vec![
SignatureScheme::RSA_PKCS1_SHA1,
SignatureScheme::ECDSA_SHA1_Legacy,
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::ECDSA_NISTP521_SHA512,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::ED25519,
SignatureScheme::ED448,
]
}
}
#[derive(Debug)]
pub struct NoHostnameTlsVerifier {
verifier: WebPkiVerifier,
verifier: Arc<WebPkiServerVerifier>,
}
impl ServerCertVerifier for NoHostnameTlsVerifier {
fn verify_server_cert(
&self,
end_entity: &rustls::Certificate,
intermediates: &[rustls::Certificate],
server_name: &ServerName,
scts: &mut dyn Iterator<Item = &[u8]>,
end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: SystemTime,
) -> Result<ServerCertVerified, TlsError> {
now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
match self.verifier.verify_server_cert(
end_entity,
intermediates,
server_name,
scts,
ocsp_response,
now,
) {
Err(TlsError::InvalidCertificateData(reason))
if reason.contains("CertNotValidForName") =>
{
Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)) => {
Ok(ServerCertVerified::assertion())
}
res => res,
}
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
self.verifier.verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
self.verifier.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.verifier.supported_verify_schemes()
}
}

View file

@ -1,4 +1,5 @@
use once_cell::sync::Lazy;
use rustls::pki_types::CertificateDer;
use rustls::RootCertStore;
#[allow(clippy::expect_used)]
@ -8,7 +9,7 @@ pub static ROOT_CERT_STORE: Lazy<RootCertStore> = Lazy::new(|| {
rustls_native_certs::load_native_certs().expect("could not load root TLS certificates")
{
roots
.add(&rustls::Certificate(cert.0))
.add(CertificateDer::from(cert.0))
.expect("could not add root TLS certificate");
}
roots

View file

@ -9,21 +9,21 @@ ansi_term = "0.12"
anyhow = { version = "1.0", features = ["std"] }
async-trait = "0.1"
bimap = "0.6"
bytes = "1.3"
bytes = "1.4"
dialoguer = "0.10"
curve25519-dalek = "4.0.0" # pin due to build fail on x86
ed25519-dalek = "2.0.0" # pin due to build fail on x86 in 2.1
futures = "0.3"
russh = { version = "0.44.1", features = ["legacy-ed25519-pkcs8-parser"] }
# russh = { version = "0.35.0-beta.6", path = "../../russh/russh"}
sea-orm = { version = "0.12.2", features = [
sea-orm = { version = "0.12", features = [
"runtime-tokio-rustls",
], default-features = false }
thiserror = "1.0"
time = "0.3"
tokio = { version = "1.20", features = ["tracing", "signal"] }
tracing = "0.1"
uuid = { version = "1.2", features = ["v4"] }
uuid = { version = "1.3", features = ["v4"] }
warpgate-common = { version = "*", path = "../warpgate-common" }
warpgate-core = { version = "*", path = "../warpgate-core" }
warpgate-db-entities = { version = "*", path = "../warpgate-db-entities" }

View file

@ -5,7 +5,7 @@ name = "warpgate-sso"
version = "0.10.2"
[dependencies]
bytes = "1.3"
bytes = "1.4"
thiserror = "1.0"
tokio = { version = "1.20", features = ["tracing", "macros"] }
tracing = "0.1"
@ -15,4 +15,4 @@ serde_json = "1.0"
once_cell = "1.17"
jsonwebtoken = "8"
data-encoding = "2.3"
futures = "0.3.30"
futures = "0.3"

View file

@ -6,8 +6,7 @@ mod sso;
pub use config::*;
pub use error::*;
pub use openidconnect::core::CoreIdToken;
pub use request::*;
pub use response::*;
pub use sso::*;
pub use openidconnect::core::CoreIdToken;

View file

@ -110,6 +110,6 @@ impl SsoClient {
req = req.set_id_token_hint(&token);
req = req.set_client_id(self.config.client_id().clone());
req = req.set_post_logout_redirect_uri(PostLogoutRedirectUrl::from_url(redirect_url));
return Ok(req.http_get_url());
Ok(req.http_get_url())
}
}

View file

@ -1366,6 +1366,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Password"
],
"example": "Password"
}
}
@ -1385,6 +1388,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"PublicKey"
],
"example": "PublicKey"
}
}
@ -1572,6 +1578,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Http"
],
"example": "Http"
}
}
@ -1591,6 +1600,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"MySql"
],
"example": "MySql"
}
}
@ -1610,6 +1622,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Ssh"
],
"example": "Ssh"
}
}
@ -1629,6 +1644,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"WebAdmin"
],
"example": "WebAdmin"
}
}
@ -1808,6 +1826,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Password"
],
"example": "Password"
}
}
@ -1827,6 +1848,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"PublicKey"
],
"example": "PublicKey"
}
}
@ -1846,6 +1870,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Sso"
],
"example": "Sso"
}
}
@ -1865,6 +1892,9 @@
"properties": {
"kind": {
"type": "string",
"enum": [
"Totp"
],
"example": "Totp"
}
}

View file

@ -9,18 +9,18 @@ ansi_term = "0.12"
anyhow = { version = "1.0", features = ["backtrace"] }
async-trait = "0.1"
atty = "0.2"
bytes = "1.3"
clap = { version = "3.2", features = ["derive"] }
bytes = "1.4"
clap = { version = "4.0", features = ["derive"] }
config = { version = "0.13", features = ["yaml"], default-features = false }
console = { version = "0.15", default-features = false }
console-subscriber = { version = "0.1", optional = true }
data-encoding = "2.3"
dialoguer = "0.10"
futures = "0.3"
notify = "^5.0.0"
notify = "5.1"
rcgen = { version = "0.10", features = ["zeroize"] }
serde_json = "1.0"
serde_yaml = "0.8.23"
serde_yaml = "0.9"
sea-orm = { version = "0.12.2", default-features = false }
time = "0.3"
tokio = { version = "1.20", features = ["tracing", "signal", "macros"] }
@ -29,7 +29,7 @@ tracing-subscriber = { version = "0.3", features = [
"env-filter",
"local-time",
] }
uuid = "1.2"
uuid = "1.3"
warpgate-admin = { version = "*", path = "../warpgate-admin" }
warpgate-common = { version = "*", path = "../warpgate-common" }
warpgate-core = { version = "*", path = "../warpgate-core" }

View file

@ -5,7 +5,7 @@ mod logging;
use std::path::PathBuf;
use anyhow::Result;
use clap::{ArgAction, StructOpt};
use clap::{ArgAction, Parser};
use logging::init_logging;
use tracing::*;