Kaynağa Gözat

ircd: Implement IRC TLS listener.

parazyd 4 yıl önce
ebeveyn
işleme
b436d659ba

+ 1 - 0
Cargo.lock

@@ -2256,6 +2256,7 @@ dependencies = [
  "log",
  "rand",
  "ringbuffer",
+ "rustls-pemfile",
  "serde",
  "serde_json",
  "simplelog",

+ 1 - 0
bin/ircd/Cargo.toml

@@ -14,6 +14,7 @@ darkfi = {path = "../../", features = ["net", "rpc"]}
 smol = "1.2.5"
 futures = "0.3.21"
 futures-rustls = "0.22.1"
+rustls-pemfile = "1.0.0"
 async-std = "1.11.0"
 async-trait = "0.1.53"
 async-channel = "1.6.1"

+ 8 - 1
bin/ircd/ircd_config.toml

@@ -2,7 +2,14 @@
 #rpc_listen="tcp://127.0.0.1:11055"
 
 ## IRC listen URL
-#irc_listen="tcp://127.0.0.1:11066"
+#irc_listen="tcp://127.0.0.1:6667"
+#irc_listen="tls://0.0.0.0:6697"
+
+## TLS certificate path if IRC acceptor uses TLS (optional)
+#irc_tls_cert = "/etc/letsencrypt/ircd/fullchain.pem"
+
+## TLS secret key path if IRC acceptor uses TLS (optional)
+#irc_tls_secret = "/etc/letsencrypt/ircd/privkey.pem"
 
 ## List of channels to autojoin for new client connections
 autojoin = ["#dev"]

+ 55 - 11
bin/ircd/src/main.rs

@@ -1,12 +1,13 @@
 use async_std::{
-    net::{TcpListener, TcpStream},
+    net::TcpListener,
     sync::{Arc, Mutex},
 };
-use std::net::SocketAddr;
+use std::{fs::File, net::SocketAddr};
 
 use async_channel::Receiver;
 use async_executor::Executor;
-use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, FutureExt};
+use futures::{io::BufReader, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, FutureExt};
+use futures_rustls::{rustls, TlsAcceptor};
 use fxhash::FxHashMap;
 use log::{error, info, warn};
 use rand::rngs::OsRng;
@@ -19,6 +20,7 @@ use darkfi::{
     system::{Subscriber, SubscriberPtr},
     util::{
         cli::{get_log_config, get_log_level, spawn_config},
+        expand_path,
         path::get_config_path,
     },
     Error, Result,
@@ -79,10 +81,10 @@ impl Ircd {
             .detach();
     }
 
-    async fn process_new_connection(
+    async fn process_new_connection<C: AsyncRead + AsyncWrite + Send + Unpin + 'static>(
         &self,
         executor: Arc<Executor<'_>>,
-        stream: TcpStream,
+        stream: C,
         peer_addr: SocketAddr,
     ) -> Result<()> {
         let (reader, writer) = stream.split();
@@ -209,10 +211,42 @@ async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
     //
     // IRC instance
     //
-    let irc_listen_addr = settings.irc_listen.socket_addrs(|| None)?[0];
-    let listener = TcpListener::bind(irc_listen_addr).await?;
-    let local_addr = listener.local_addr()?;
-    info!("IRC listening on {}", local_addr);
+    let listenaddr = settings.irc_listen.socket_addrs(|| None)?[0];
+    let listener = TcpListener::bind(listenaddr).await?;
+
+    let acceptor = match settings.irc_listen.scheme() {
+        "tls" => {
+            // openssl genpkey -algorithm ED25519 > example.com.key
+            // openssl req -new -out example.com.csr -key example.com.key
+            // openssl x509 -req -days 700 -in example.com.csr -signkey example.com.key -out example.com.crt
+
+            if settings.irc_tls_secret.is_none() || settings.irc_tls_cert.is_none() {
+                error!("To listen using TLS, please set irc_tls_secret and irc_tls_cert in your config file.");
+                return Err(Error::KeypairPathNotFound)
+            }
+
+            let file = File::open(expand_path(&settings.irc_tls_secret.unwrap())?)?;
+            let mut reader = std::io::BufReader::new(file);
+            let secret = &rustls_pemfile::pkcs8_private_keys(&mut reader)?[0];
+            let secret = rustls::PrivateKey(secret.clone());
+
+            let file = File::open(expand_path(&settings.irc_tls_cert.unwrap())?)?;
+            let mut reader = std::io::BufReader::new(file);
+            let certificate = &rustls_pemfile::certs(&mut reader)?[0];
+            let certificate = rustls::Certificate(certificate.clone());
+
+            let config = rustls::ServerConfig::builder()
+                .with_safe_defaults()
+                .with_no_client_auth()
+                .with_single_cert(vec![certificate], secret)?;
+
+            let acceptor = TlsAcceptor::from(Arc::new(config));
+            Some(acceptor)
+        }
+        _ => None,
+    };
+
+    info!("IRC listening on {}", settings.irc_listen);
 
     let executor_cloned = executor.clone();
     executor
@@ -236,8 +270,18 @@ async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
                     }
                 };
 
-                let result =
-                    ircd.process_new_connection(executor_cloned.clone(), stream, peer_addr).await;
+                let result = if let Some(acceptor) = acceptor.clone() {
+                    let stream = match acceptor.accept(stream).await {
+                        Ok(s) => s,
+                        Err(e) => {
+                            error!("Failed accepting TLS connection: {}", e);
+                            continue
+                        }
+                    };
+                    ircd.process_new_connection(executor_cloned.clone(), stream, peer_addr).await
+                } else {
+                    ircd.process_new_connection(executor_cloned.clone(), stream, peer_addr).await
+                };
 
                 if let Err(e) = result {
                     error!("Failed processing connection {}: {}", peer_addr, e);

+ 5 - 6
bin/ircd/src/server.rs

@@ -1,7 +1,6 @@
-use async_std::net::TcpStream;
 use std::net::SocketAddr;
 
-use futures::{io::WriteHalf, AsyncWriteExt};
+use futures::{io::WriteHalf, AsyncRead, AsyncWrite, AsyncWriteExt};
 use fxhash::FxHashMap;
 use log::{debug, info, warn};
 use rand::{rngs::OsRng, RngCore};
@@ -20,9 +19,9 @@ const RPL_TOPIC: u32 = 332;
 const RPL_NAMEREPLY: u32 = 353;
 const RPL_ENDOFNAMES: u32 = 366;
 
-pub struct IrcServerConnection {
+pub struct IrcServerConnection<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> {
     // server stream
-    write_stream: WriteHalf<TcpStream>,
+    write_stream: WriteHalf<C>,
     peer_address: SocketAddr,
     // msg ids
     seen_msg_ids: SeenMsgIds,
@@ -42,10 +41,10 @@ pub struct IrcServerConnection {
     subscriber_id: u64,
 }
 
-impl IrcServerConnection {
+impl<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcServerConnection<C> {
     #[allow(clippy::too_many_arguments)]
     pub fn new(
-        write_stream: WriteHalf<TcpStream>,
+        write_stream: WriteHalf<C>,
         peer_address: SocketAddr,
         seen_msg_ids: SeenMsgIds,
         privmsgs_buffer: PrivmsgsBuffer,

+ 6 - 0
bin/ircd/src/settings.rs

@@ -30,6 +30,12 @@ pub struct Args {
     #[structopt(long = "irc", default_value = "tcp://127.0.0.1:11066")]
     pub irc_listen: Url,
 
+    /// Optional TLS certificate file path if `irc_listen` uses TLS
+    pub irc_tls_cert: Option<String>,
+
+    /// Optional TLS certificate key file path if `irc_listen` uses TLS
+    pub irc_tls_secret: Option<String>,
+
     /// Generate a new NaCl secret and exit
     #[structopt(long)]
     pub gen_secret: bool,

+ 4 - 0
src/error.rs

@@ -184,6 +184,10 @@ pub enum Error {
     #[error("Invalid DarkFi address")]
     InvalidAddress,
 
+    #[cfg(feature = "futures-rustls")]
+    #[error(transparent)]
+    RustlsError(#[from] futures_rustls::rustls::Error),
+
     // =======================
     // Protocol-related errors
     // =======================