loonfs_server/http/
tls.rs1use crate::config::TlsServerConfig;
5use rustls::pki_types::{CertificateDer, PrivateKeyDer};
6use std::future::Future;
7use std::io;
8use std::net::SocketAddr;
9use std::path::Path;
10use std::pin::Pin;
11use std::sync::Arc;
12use std::task::{ready, Context, Poll};
13use std::time::Duration;
14use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
15use tokio::net::{TcpListener, TcpStream};
16use tokio_rustls::server::TlsStream as RustlsStream;
17use tokio_rustls::{Accept, TlsAcceptor};
18
19#[derive(Debug, thiserror::Error)]
22pub enum TlsConfigError {
23 #[error("failed to read `{path}`: {source}")]
24 Read {
25 path: String,
26 #[source]
27 source: io::Error,
28 },
29 #[error("`{path}` is not valid PEM: {reason}")]
30 Pem { path: String, reason: String },
31 #[error(
32 "`{path}` and the certificate in `{cert_path}` do not form a usable identity: {reason}"
33 )]
34 Identity {
35 path: String,
36 cert_path: String,
37 reason: String,
38 },
39}
40
41pub(super) fn server_config(
47 config: &TlsServerConfig,
48) -> Result<rustls::ServerConfig, TlsConfigError> {
49 let certs = load_cert_chain(&config.cert_path)?;
50 let key = load_private_key(&config.key_path)?;
51 let provider = Arc::new(rustls::crypto::ring::default_provider());
52 let mut server_config = rustls::ServerConfig::builder_with_provider(provider)
53 .with_safe_default_protocol_versions()
54 .map_err(|error| TlsConfigError::Identity {
55 path: config.key_path.clone(),
56 cert_path: config.cert_path.clone(),
57 reason: error.to_string(),
58 })?
59 .with_no_client_auth()
60 .with_single_cert(certs, key)
61 .map_err(|error| TlsConfigError::Identity {
62 path: config.key_path.clone(),
63 cert_path: config.cert_path.clone(),
64 reason: error.to_string(),
65 })?;
66 server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
70 Ok(server_config)
71}
72
73fn load_cert_chain(path: &str) -> Result<Vec<CertificateDer<'static>>, TlsConfigError> {
74 let mut reader = io::BufReader::new(open(path)?);
75 let certs = rustls_pemfile::certs(&mut reader)
76 .collect::<Result<Vec<_>, _>>()
77 .map_err(|error| TlsConfigError::Pem {
78 path: path.to_owned(),
79 reason: error.to_string(),
80 })?;
81 if certs.is_empty() {
82 return Err(TlsConfigError::Pem {
83 path: path.to_owned(),
84 reason: "no CERTIFICATE section found; expected the PEM chain, leaf first".to_owned(),
85 });
86 }
87 Ok(certs)
88}
89
90fn load_private_key(path: &str) -> Result<PrivateKeyDer<'static>, TlsConfigError> {
91 let mut reader = io::BufReader::new(open(path)?);
92 rustls_pemfile::private_key(&mut reader)
93 .map_err(|error| TlsConfigError::Pem {
94 path: path.to_owned(),
95 reason: error.to_string(),
96 })?
97 .ok_or_else(|| TlsConfigError::Pem {
98 path: path.to_owned(),
99 reason: "no PRIVATE KEY section found; expected a PKCS#8, RSA, or EC private key"
100 .to_owned(),
101 })
102}
103
104fn open(path: &str) -> Result<std::fs::File, TlsConfigError> {
105 std::fs::File::open(Path::new(path)).map_err(|source| TlsConfigError::Read {
106 path: path.to_owned(),
107 source,
108 })
109}
110
111pub(super) struct TlsListener {
120 tcp: TcpListener,
121 acceptor: TlsAcceptor,
122}
123
124impl TlsListener {
125 pub(super) fn new(tcp: TcpListener, config: rustls::ServerConfig) -> Self {
126 Self {
127 tcp,
128 acceptor: TlsAcceptor::from(Arc::new(config)),
129 }
130 }
131}
132
133impl axum::serve::Listener for TlsListener {
134 type Io = TlsIo;
135 type Addr = SocketAddr;
136
137 async fn accept(&mut self) -> (Self::Io, Self::Addr) {
138 loop {
139 match self.tcp.accept().await {
140 Ok((stream, addr)) => {
141 return (
142 TlsIo::Handshaking(Box::new(self.acceptor.accept(stream))),
143 addr,
144 )
145 }
146 Err(error) => handle_accept_error(error).await,
147 }
148 }
149 }
150
151 fn local_addr(&self) -> io::Result<Self::Addr> {
152 self.tcp.local_addr()
153 }
154}
155
156async fn handle_accept_error(error: io::Error) {
162 if matches!(
163 error.kind(),
164 io::ErrorKind::ConnectionRefused
165 | io::ErrorKind::ConnectionAborted
166 | io::ErrorKind::ConnectionReset
167 ) {
168 return;
169 }
170 tracing::error!("accept error: {error}");
171 accept_retry_pause().await;
172}
173
174#[allow(clippy::disallowed_methods)]
175async fn accept_retry_pause() {
178 tokio::time::sleep(ACCEPT_RETRY_PAUSE).await;
179}
180
181const ACCEPT_RETRY_PAUSE: Duration = Duration::from_secs(1);
182
183pub(super) enum TlsIo {
190 Handshaking(Box<Accept<TcpStream>>),
191 Ready(Box<RustlsStream<TcpStream>>),
192 Failed,
195}
196
197impl TlsIo {
198 fn poll_stream(
202 &mut self,
203 cx: &mut Context<'_>,
204 ) -> Poll<io::Result<Pin<&mut RustlsStream<TcpStream>>>> {
205 if let Self::Handshaking(accept) = self {
206 match Pin::new(accept.as_mut()).poll(cx) {
207 Poll::Ready(Ok(stream)) => *self = Self::Ready(Box::new(stream)),
208 Poll::Ready(Err(error)) => {
209 *self = Self::Failed;
210 return Poll::Ready(Err(error));
211 }
212 Poll::Pending => return Poll::Pending,
213 }
214 }
215 match self {
216 Self::Ready(stream) => Poll::Ready(Ok(Pin::new(stream.as_mut()))),
217 Self::Handshaking(_) | Self::Failed => Poll::Ready(Err(handshake_failed())),
220 }
221 }
222}
223
224fn handshake_failed() -> io::Error {
225 io::Error::new(
226 io::ErrorKind::InvalidData,
227 "tls handshake failed on this connection",
228 )
229}
230
231impl AsyncRead for TlsIo {
232 fn poll_read(
233 self: Pin<&mut Self>,
234 cx: &mut Context<'_>,
235 buf: &mut ReadBuf<'_>,
236 ) -> Poll<io::Result<()>> {
237 ready!(self.get_mut().poll_stream(cx))?.poll_read(cx, buf)
238 }
239}
240
241impl AsyncWrite for TlsIo {
242 fn poll_write(
243 self: Pin<&mut Self>,
244 cx: &mut Context<'_>,
245 buf: &[u8],
246 ) -> Poll<io::Result<usize>> {
247 ready!(self.get_mut().poll_stream(cx))?.poll_write(cx, buf)
248 }
249
250 fn poll_write_vectored(
251 self: Pin<&mut Self>,
252 cx: &mut Context<'_>,
253 bufs: &[io::IoSlice<'_>],
254 ) -> Poll<io::Result<usize>> {
255 ready!(self.get_mut().poll_stream(cx))?.poll_write_vectored(cx, bufs)
256 }
257
258 fn is_write_vectored(&self) -> bool {
262 true
263 }
264
265 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
266 ready!(self.get_mut().poll_stream(cx))?.poll_flush(cx)
267 }
268
269 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
274 match self.get_mut() {
275 TlsIo::Ready(stream) => Pin::new(stream.as_mut()).poll_shutdown(cx),
276 TlsIo::Handshaking(_) | TlsIo::Failed => Poll::Ready(Ok(())),
277 }
278 }
279}