itsi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +1541 -0
- data/Cargo.toml +13 -0
- data/README.md +82 -0
- data/Rakefile +87 -0
- data/crates/itsi_error/Cargo.lock +368 -0
- data/crates/itsi_error/Cargo.toml +9 -0
- data/crates/itsi_error/src/lib.rs +49 -0
- data/crates/itsi_rb_helpers/Cargo.lock +355 -0
- data/crates/itsi_rb_helpers/Cargo.toml +8 -0
- data/crates/itsi_rb_helpers/src/lib.rs +98 -0
- data/crates/itsi_scheduler/Cargo.toml +16 -0
- data/crates/itsi_scheduler/extconf.rb +6 -0
- data/crates/itsi_scheduler/src/lib.rs +17 -0
- data/crates/itsi_server/Cargo.toml +29 -0
- data/crates/itsi_server/extconf.rb +6 -0
- data/crates/itsi_server/src/lib.rs +52 -0
- data/crates/itsi_server/src/request/itsi_request.rs +143 -0
- data/crates/itsi_server/src/request/mod.rs +1 -0
- data/crates/itsi_server/src/server/bind.rs +138 -0
- data/crates/itsi_server/src/server/itsi_ca/itsi_ca.crt +32 -0
- data/crates/itsi_server/src/server/itsi_ca/itsi_ca.key +52 -0
- data/crates/itsi_server/src/server/itsi_server.rs +182 -0
- data/crates/itsi_server/src/server/listener.rs +218 -0
- data/crates/itsi_server/src/server/mod.rs +5 -0
- data/crates/itsi_server/src/server/tls.rs +138 -0
- data/crates/itsi_server/src/server/transfer_protocol.rs +23 -0
- data/crates/itsi_server/src/stream_writer/mod.rs +21 -0
- data/crates/itsi_tracing/Cargo.lock +274 -0
- data/crates/itsi_tracing/Cargo.toml +12 -0
- data/crates/itsi_tracing/src/lib.rs +11 -0
- data/gems/scheduler/.gitignore +14 -0
- data/gems/scheduler/.rubocop.yml +8 -0
- data/gems/scheduler/CHANGELOG.md +5 -0
- data/gems/scheduler/CODE_OF_CONDUCT.md +132 -0
- data/gems/scheduler/Cargo.lock +745 -0
- data/gems/scheduler/Cargo.toml +3 -0
- data/gems/scheduler/LICENSE.txt +21 -0
- data/gems/scheduler/README.md +43 -0
- data/gems/scheduler/Rakefile +22 -0
- data/gems/scheduler/bin/console +11 -0
- data/gems/scheduler/bin/setup +8 -0
- data/gems/scheduler/ext/itsi_error/Cargo.lock +368 -0
- data/gems/scheduler/ext/itsi_error/Cargo.toml +9 -0
- data/gems/scheduler/ext/itsi_error/src/lib.rs +49 -0
- data/gems/scheduler/ext/itsi_rb_helpers/Cargo.lock +355 -0
- data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +8 -0
- data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +98 -0
- data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +16 -0
- data/gems/scheduler/ext/itsi_scheduler/extconf.rb +6 -0
- data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +17 -0
- data/gems/scheduler/ext/itsi_tracing/Cargo.lock +274 -0
- data/gems/scheduler/ext/itsi_tracing/Cargo.toml +12 -0
- data/gems/scheduler/ext/itsi_tracing/src/lib.rs +11 -0
- data/gems/scheduler/itsi-scheduler.gemspec +43 -0
- data/gems/scheduler/lib/itsi/scheduler/version.rb +7 -0
- data/gems/scheduler/lib/itsi/scheduler.rb +11 -0
- data/gems/scheduler/sig/itsi_scheduler.rbs +4 -0
- data/gems/scheduler/test/test_helper.rb +6 -0
- data/gems/scheduler/test/test_itsi_scheduler.rb +9 -0
- data/gems/server/.gitignore +14 -0
- data/gems/server/.rubocop.yml +8 -0
- data/gems/server/CHANGELOG.md +5 -0
- data/gems/server/CODE_OF_CONDUCT.md +132 -0
- data/gems/server/LICENSE.txt +21 -0
- data/gems/server/README.md +43 -0
- data/gems/server/Rakefile +22 -0
- data/gems/server/bin/console +11 -0
- data/gems/server/bin/setup +8 -0
- data/gems/server/exe/itsi +84 -0
- data/gems/server/ext/itsi_error/Cargo.lock +368 -0
- data/gems/server/ext/itsi_error/Cargo.toml +9 -0
- data/gems/server/ext/itsi_error/src/lib.rs +49 -0
- data/gems/server/ext/itsi_rb_helpers/Cargo.lock +355 -0
- data/gems/server/ext/itsi_rb_helpers/Cargo.toml +8 -0
- data/gems/server/ext/itsi_rb_helpers/src/lib.rs +98 -0
- data/gems/server/ext/itsi_server/Cargo.toml +29 -0
- data/gems/server/ext/itsi_server/extconf.rb +6 -0
- data/gems/server/ext/itsi_server/src/lib.rs +52 -0
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +143 -0
- data/gems/server/ext/itsi_server/src/request/mod.rs +1 -0
- data/gems/server/ext/itsi_server/src/server/bind.rs +138 -0
- data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +32 -0
- data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +52 -0
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +182 -0
- data/gems/server/ext/itsi_server/src/server/listener.rs +218 -0
- data/gems/server/ext/itsi_server/src/server/mod.rs +5 -0
- data/gems/server/ext/itsi_server/src/server/tls.rs +138 -0
- data/gems/server/ext/itsi_server/src/server/transfer_protocol.rs +23 -0
- data/gems/server/ext/itsi_server/src/stream_writer/mod.rs +21 -0
- data/gems/server/ext/itsi_tracing/Cargo.lock +274 -0
- data/gems/server/ext/itsi_tracing/Cargo.toml +12 -0
- data/gems/server/ext/itsi_tracing/src/lib.rs +11 -0
- data/gems/server/itsi-server.gemspec +45 -0
- data/gems/server/lib/itsi/request.rb +39 -0
- data/gems/server/lib/itsi/server/version.rb +7 -0
- data/gems/server/lib/itsi/server.rb +21 -0
- data/gems/server/sig/itsi_server.rbs +4 -0
- data/gems/server/test/test_helper.rb +5 -0
- data/gems/server/test/test_itsi_server.rb +9 -0
- data/lib/itsi/version.rb +3 -0
- data/lib/itsi.rb +6 -0
- metadata +172 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
use super::bind::{Bind, BindAddress};
|
|
2
|
+
use super::transfer_protocol::TransferProtocol;
|
|
3
|
+
use hyper_util::rt::TokioIo;
|
|
4
|
+
use itsi_error::Result;
|
|
5
|
+
use itsi_tracing::info;
|
|
6
|
+
use socket2::{Domain, Protocol, Socket, Type};
|
|
7
|
+
use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
|
|
8
|
+
use std::pin::Pin;
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
use std::{os::unix::net::UnixListener as StdUnixListener, path::PathBuf};
|
|
11
|
+
use tokio::net::{unix, TcpListener, TcpStream, UnixListener, UnixStream};
|
|
12
|
+
use tokio_rustls::TlsAcceptor;
|
|
13
|
+
|
|
14
|
+
pub(crate) trait IoStream:
|
|
15
|
+
tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin
|
|
16
|
+
{
|
|
17
|
+
}
|
|
18
|
+
impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin> IoStream for T {}
|
|
19
|
+
|
|
20
|
+
pub(crate) enum Listener {
|
|
21
|
+
Tcp(TcpListener),
|
|
22
|
+
TcpTls((TcpListener, TlsAcceptor)),
|
|
23
|
+
Unix(UnixListener),
|
|
24
|
+
UnixTls((UnixListener, TlsAcceptor)),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
enum Stream {
|
|
28
|
+
TcpStream((TcpStream, SocketAddr)),
|
|
29
|
+
UnixStream((UnixStream, unix::SocketAddr)),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#[derive(Clone, Debug)]
|
|
33
|
+
pub enum SockAddr {
|
|
34
|
+
Tcp(Arc<SocketAddr>),
|
|
35
|
+
Unix(Arc<unix::SocketAddr>),
|
|
36
|
+
}
|
|
37
|
+
impl std::fmt::Display for SockAddr {
|
|
38
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
39
|
+
match self {
|
|
40
|
+
SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
|
|
41
|
+
SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
|
|
42
|
+
Some(path) => write!(f, "{:?}", path),
|
|
43
|
+
None => write!(f, ""),
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl Listener {
|
|
50
|
+
pub(crate) async fn accept(&self) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
51
|
+
match self {
|
|
52
|
+
Listener::Tcp(listener) => Listener::accept_tcp(listener).await,
|
|
53
|
+
Listener::TcpTls((listener, acceptor)) => {
|
|
54
|
+
Listener::accept_tls(listener, acceptor).await
|
|
55
|
+
}
|
|
56
|
+
Listener::Unix(stream) => Listener::accept_unix(stream).await,
|
|
57
|
+
Listener::UnixTls((listener, acceptor)) => {
|
|
58
|
+
Listener::accept_unix_tls(listener, acceptor).await
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async fn to_tokio_io(
|
|
64
|
+
input_stream: Stream,
|
|
65
|
+
tls_acceptor: Option<&TlsAcceptor>,
|
|
66
|
+
) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
67
|
+
match tls_acceptor {
|
|
68
|
+
Some(acceptor) => match input_stream {
|
|
69
|
+
Stream::TcpStream((tcp_stream, socket_address)) => {
|
|
70
|
+
match acceptor.accept(tcp_stream).await {
|
|
71
|
+
Ok(tls_stream) => Ok((
|
|
72
|
+
TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
|
|
73
|
+
SockAddr::Tcp(Arc::new(socket_address)),
|
|
74
|
+
)),
|
|
75
|
+
Err(err) => Err(err.into()),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
Stream::UnixStream((unix_stream, socket_address)) => {
|
|
79
|
+
match acceptor.accept(unix_stream).await {
|
|
80
|
+
Ok(tls_stream) => Ok((
|
|
81
|
+
TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
|
|
82
|
+
SockAddr::Unix(Arc::new(socket_address)),
|
|
83
|
+
)),
|
|
84
|
+
Err(err) => Err(err.into()),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
None => match input_stream {
|
|
89
|
+
Stream::TcpStream((tcp_stream, socket_address)) => Ok((
|
|
90
|
+
TokioIo::new(Box::pin(tcp_stream) as Pin<Box<dyn IoStream>>),
|
|
91
|
+
SockAddr::Tcp(Arc::new(socket_address)),
|
|
92
|
+
)),
|
|
93
|
+
Stream::UnixStream((unix_stream, socket_address)) => Ok((
|
|
94
|
+
TokioIo::new(Box::pin(unix_stream) as Pin<Box<dyn IoStream>>),
|
|
95
|
+
SockAddr::Unix(Arc::new(socket_address)),
|
|
96
|
+
)),
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async fn accept_tcp(
|
|
102
|
+
listener: &TcpListener,
|
|
103
|
+
) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
104
|
+
let tcp_stream = listener.accept().await?;
|
|
105
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async fn accept_tls(
|
|
109
|
+
listener: &TcpListener,
|
|
110
|
+
acceptor: &TlsAcceptor,
|
|
111
|
+
) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
112
|
+
let tcp_stream = listener.accept().await?;
|
|
113
|
+
Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async fn accept_unix(
|
|
117
|
+
listener: &UnixListener,
|
|
118
|
+
) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
119
|
+
let unix_stream = listener.accept().await?;
|
|
120
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async fn accept_unix_tls(
|
|
124
|
+
listener: &UnixListener,
|
|
125
|
+
acceptor: &TlsAcceptor,
|
|
126
|
+
) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
|
|
127
|
+
let unix_stream = listener.accept().await?;
|
|
128
|
+
Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
pub(crate) fn scheme(&self) -> String {
|
|
132
|
+
match self {
|
|
133
|
+
Listener::Tcp(_) => "http".to_string(),
|
|
134
|
+
Listener::TcpTls(_) => "https".to_string(),
|
|
135
|
+
Listener::Unix(_) => "http".to_string(),
|
|
136
|
+
Listener::UnixTls(_) => "https".to_string(),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
pub(crate) fn port(&self) -> u16 {
|
|
141
|
+
match self {
|
|
142
|
+
Listener::Tcp(listener) => listener.local_addr().unwrap().port(),
|
|
143
|
+
Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().port(),
|
|
144
|
+
Listener::Unix(_) => 0,
|
|
145
|
+
Listener::UnixTls(_) => 0,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
pub(crate) fn host(&self) -> String {
|
|
150
|
+
match self {
|
|
151
|
+
Listener::Tcp(listener) => listener.local_addr().unwrap().ip().to_string(),
|
|
152
|
+
Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().ip().to_string(),
|
|
153
|
+
Listener::Unix(_) => "unix".to_string(),
|
|
154
|
+
Listener::UnixTls(_) => "unix".to_string(),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
impl From<Bind> for Listener {
|
|
160
|
+
fn from(bind: Bind) -> Self {
|
|
161
|
+
match bind.address {
|
|
162
|
+
BindAddress::Ip(addr) => match bind.protocol {
|
|
163
|
+
TransferProtocol::Http => Listener::Tcp(
|
|
164
|
+
TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap())).unwrap(),
|
|
165
|
+
),
|
|
166
|
+
TransferProtocol::Https => {
|
|
167
|
+
let tcp_listener =
|
|
168
|
+
TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap()))
|
|
169
|
+
.unwrap();
|
|
170
|
+
let tls_acceptor = TlsAcceptor::from(Arc::new(bind.tls_config.unwrap()));
|
|
171
|
+
Listener::TcpTls((tcp_listener, tls_acceptor))
|
|
172
|
+
}
|
|
173
|
+
_ => unreachable!(),
|
|
174
|
+
},
|
|
175
|
+
BindAddress::UnixSocket(path) => match bind.tls_config {
|
|
176
|
+
Some(tls_config) => {
|
|
177
|
+
let tls_acceptor = TlsAcceptor::from(Arc::new(tls_config));
|
|
178
|
+
Listener::UnixTls((
|
|
179
|
+
UnixListener::from_std(connect_unix_socket(&path)).unwrap(),
|
|
180
|
+
tls_acceptor,
|
|
181
|
+
))
|
|
182
|
+
}
|
|
183
|
+
None => Listener::Unix(UnixListener::from_std(connect_unix_socket(&path)).unwrap()),
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fn connect_tcp_socket(addr: IpAddr, port: u16) -> StdTcpListener {
|
|
190
|
+
let domain = match addr {
|
|
191
|
+
IpAddr::V4(_) => Domain::IPV4,
|
|
192
|
+
IpAddr::V6(_) => Domain::IPV6,
|
|
193
|
+
};
|
|
194
|
+
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
|
|
195
|
+
let socket_address: SocketAddr = SocketAddr::new(addr, port);
|
|
196
|
+
socket.set_reuse_address(true).ok();
|
|
197
|
+
socket.set_reuse_port(true).ok();
|
|
198
|
+
socket.set_nonblocking(true).ok();
|
|
199
|
+
socket.set_nodelay(true).ok();
|
|
200
|
+
socket.set_recv_buffer_size(1_048_576).ok();
|
|
201
|
+
info!("Binding to {}", socket_address);
|
|
202
|
+
socket.bind(&socket_address.into()).unwrap();
|
|
203
|
+
socket.listen(1024).unwrap();
|
|
204
|
+
socket.into()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn connect_unix_socket(path: &PathBuf) -> StdUnixListener {
|
|
208
|
+
let _ = std::fs::remove_file(path);
|
|
209
|
+
let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap();
|
|
210
|
+
socket.set_nonblocking(true).ok();
|
|
211
|
+
let socket_address = socket2::SockAddr::unix(path).unwrap();
|
|
212
|
+
|
|
213
|
+
info!("Binding to {:?}", path);
|
|
214
|
+
socket.bind(&socket_address).unwrap();
|
|
215
|
+
socket.listen(1024).unwrap();
|
|
216
|
+
|
|
217
|
+
socket.into()
|
|
218
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
use base64::{engine::general_purpose, Engine as _};
|
|
2
|
+
use itsi_error::Result;
|
|
3
|
+
use itsi_tracing::{info, warn};
|
|
4
|
+
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
|
|
5
|
+
use rustls_pemfile::{certs, pkcs8_private_keys};
|
|
6
|
+
use std::{collections::HashMap, fs, io::BufReader};
|
|
7
|
+
use tokio_rustls::rustls::{Certificate, PrivateKey, ServerConfig};
|
|
8
|
+
|
|
9
|
+
const ITS_CA_CERT: &str = include_str!("./itsi_ca/itsi_ca.crt");
|
|
10
|
+
const ITS_CA_KEY: &str = include_str!("./itsi_ca/itsi_ca.key");
|
|
11
|
+
|
|
12
|
+
// Generates a TLS configuration based on either :
|
|
13
|
+
// * Input "cert" and "key" options (either paths or Base64-encoded strings) or
|
|
14
|
+
// * Performs automatic certificate generation/retrieval. Generated certs use an internal self-signed Isti CA.
|
|
15
|
+
// If a non-local host or optional domain parameter is provided,
|
|
16
|
+
// an automated certificate will attempt to be fetched using let's encrypt.
|
|
17
|
+
pub fn configure_tls(host: &str, query_params: &HashMap<String, String>) -> Result<ServerConfig> {
|
|
18
|
+
let (certs, key) = if let (Some(cert_path), Some(key_path)) =
|
|
19
|
+
(query_params.get("cert"), query_params.get("key"))
|
|
20
|
+
{
|
|
21
|
+
// Load from file or Base64
|
|
22
|
+
let certs = load_certs(cert_path);
|
|
23
|
+
let key = load_private_key(key_path);
|
|
24
|
+
(certs, key)
|
|
25
|
+
} else if query_params
|
|
26
|
+
.get("cert")
|
|
27
|
+
.map(|v| v == "auto")
|
|
28
|
+
.unwrap_or(false)
|
|
29
|
+
{
|
|
30
|
+
let domain_param = query_params.get("domain");
|
|
31
|
+
let host_string = host.to_string();
|
|
32
|
+
let domain = domain_param.or_else(|| {
|
|
33
|
+
if host_string != "localhost" {
|
|
34
|
+
Some(&host_string)
|
|
35
|
+
} else {
|
|
36
|
+
None
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if let Some(domain) = domain {
|
|
41
|
+
retrieve_acme_cert(domain)?
|
|
42
|
+
} else {
|
|
43
|
+
generate_ca_signed_cert(host)?
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
generate_ca_signed_cert(host)?
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let mut config = ServerConfig::builder()
|
|
50
|
+
.with_safe_defaults()
|
|
51
|
+
.with_no_client_auth()
|
|
52
|
+
.with_single_cert(certs, key)
|
|
53
|
+
.expect("Failed to build TLS config");
|
|
54
|
+
|
|
55
|
+
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
|
56
|
+
Ok(config)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
pub fn load_certs(path: &str) -> Vec<Certificate> {
|
|
60
|
+
let data = if let Some(stripped) = path.strip_prefix("base64:") {
|
|
61
|
+
general_purpose::STANDARD
|
|
62
|
+
.decode(stripped)
|
|
63
|
+
.expect("Invalid base64 certificate")
|
|
64
|
+
} else {
|
|
65
|
+
fs::read(path).expect("Failed to read certificate file")
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if data.starts_with(b"-----BEGIN ") {
|
|
69
|
+
let mut reader = BufReader::new(&data[..]);
|
|
70
|
+
let certs_der: Vec<Vec<u8>> = certs(&mut reader)
|
|
71
|
+
.map(|r| {
|
|
72
|
+
r.map(|der| der.as_ref().to_vec())
|
|
73
|
+
.map_err(itsi_error::ItsiError::from)
|
|
74
|
+
})
|
|
75
|
+
.collect::<Result<_>>()
|
|
76
|
+
.expect("Failed to parse certificate file");
|
|
77
|
+
certs_der.into_iter().map(Certificate).collect()
|
|
78
|
+
} else {
|
|
79
|
+
vec![Certificate(data)]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Loads a private key from a file or Base64.
|
|
84
|
+
pub fn load_private_key(path: &str) -> PrivateKey {
|
|
85
|
+
let key_data = if let Some(stripped) = path.strip_prefix("base64:") {
|
|
86
|
+
general_purpose::STANDARD
|
|
87
|
+
.decode(stripped)
|
|
88
|
+
.expect("Invalid base64 private key")
|
|
89
|
+
} else {
|
|
90
|
+
fs::read(path).expect("Failed to read private key file")
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if key_data.starts_with(b"-----BEGIN ") {
|
|
94
|
+
let mut reader = BufReader::new(&key_data[..]);
|
|
95
|
+
let keys: Vec<Vec<u8>> = pkcs8_private_keys(&mut reader)
|
|
96
|
+
.map(|r| {
|
|
97
|
+
r.map(|key| key.secret_pkcs8_der().to_vec())
|
|
98
|
+
.map_err(itsi_error::ItsiError::from)
|
|
99
|
+
})
|
|
100
|
+
.collect::<Result<_>>()
|
|
101
|
+
.expect("Failed to parse private key");
|
|
102
|
+
if !keys.is_empty() {
|
|
103
|
+
return PrivateKey(keys[0].clone());
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
PrivateKey(key_data)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn generate_ca_signed_cert(domain: &str) -> Result<(Vec<Certificate>, PrivateKey)> {
|
|
110
|
+
info!("Generating New Itsi CA - Self signed Certificate. Use `itsi ca export` to export the CA certificate for import into your local trust store.");
|
|
111
|
+
|
|
112
|
+
let ca_kp = KeyPair::from_pem(ITS_CA_KEY).unwrap();
|
|
113
|
+
let params = CertificateParams::from_ca_cert_pem(ITS_CA_CERT).unwrap();
|
|
114
|
+
|
|
115
|
+
let ca_cert = params.self_signed(&ca_kp).unwrap();
|
|
116
|
+
let ee_key = KeyPair::generate().unwrap();
|
|
117
|
+
let mut ee_params = CertificateParams::default();
|
|
118
|
+
|
|
119
|
+
// Set the domain in the subject alternative names (SAN)
|
|
120
|
+
ee_params.subject_alt_names = vec![SanType::DnsName(domain.try_into()?)];
|
|
121
|
+
// Optionally, set the Common Name (CN) in the distinguished name:
|
|
122
|
+
ee_params
|
|
123
|
+
.distinguished_name
|
|
124
|
+
.push(DnType::CommonName, domain);
|
|
125
|
+
|
|
126
|
+
ee_params.use_authority_key_identifier_extension = true;
|
|
127
|
+
|
|
128
|
+
let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ee_key).unwrap();
|
|
129
|
+
let ee_cert_der = ee_cert.der().to_vec();
|
|
130
|
+
let ee_cert = Certificate(ee_cert_der);
|
|
131
|
+
Ok((vec![ee_cert], PrivateKey(ee_key.serialize_der())))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Retrieves an ACME certificate for a given domain.
|
|
135
|
+
pub fn retrieve_acme_cert(domain: &str) -> Result<(Vec<Certificate>, PrivateKey)> {
|
|
136
|
+
warn!("Retrieving ACME cert for {}", domain);
|
|
137
|
+
generate_ca_signed_cert(domain)
|
|
138
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
use itsi_error::ItsiError;
|
|
2
|
+
use std::str::FromStr;
|
|
3
|
+
|
|
4
|
+
#[derive(Debug, Default, Clone)]
|
|
5
|
+
pub enum TransferProtocol {
|
|
6
|
+
#[default]
|
|
7
|
+
Https,
|
|
8
|
+
Http,
|
|
9
|
+
Unix,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl FromStr for TransferProtocol {
|
|
13
|
+
type Err = ItsiError;
|
|
14
|
+
|
|
15
|
+
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
16
|
+
match s {
|
|
17
|
+
"http" => Ok(TransferProtocol::Http),
|
|
18
|
+
"https" => Ok(TransferProtocol::Https),
|
|
19
|
+
"unix" => Ok(TransferProtocol::Unix),
|
|
20
|
+
_ => Err(ItsiError::UnsupportedProtocol(s.to_string())),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
use bytes::Bytes;
|
|
2
|
+
use magnus::{error::Result as MagnusResult, exception::io_error, Error};
|
|
3
|
+
use tokio::sync::mpsc::Sender;
|
|
4
|
+
|
|
5
|
+
#[magnus::wrap(class = "Itsi::StreamWriter", free_immediately, size)]
|
|
6
|
+
pub struct StreamWriter {
|
|
7
|
+
sender: Sender<Bytes>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl StreamWriter {
|
|
11
|
+
pub fn new(sender: Sender<Bytes>) -> Self {
|
|
12
|
+
StreamWriter { sender }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub fn write(&self, bytes: Bytes) -> MagnusResult<()> {
|
|
16
|
+
self.sender
|
|
17
|
+
.blocking_send(bytes)
|
|
18
|
+
.map_err(|e| Error::new(io_error(), format!("{:?}", e)))?;
|
|
19
|
+
Ok(())
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "aho-corasick"
|
|
7
|
+
version = "1.1.3"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"memchr",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "cfg-if"
|
|
16
|
+
version = "1.0.0"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
19
|
+
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "itsi_tracing"
|
|
22
|
+
version = "0.1.0"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"tracing",
|
|
25
|
+
"tracing-subscriber",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[[package]]
|
|
29
|
+
name = "lazy_static"
|
|
30
|
+
version = "1.5.0"
|
|
31
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
32
|
+
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
33
|
+
|
|
34
|
+
[[package]]
|
|
35
|
+
name = "log"
|
|
36
|
+
version = "0.4.26"
|
|
37
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
38
|
+
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
|
39
|
+
|
|
40
|
+
[[package]]
|
|
41
|
+
name = "matchers"
|
|
42
|
+
version = "0.1.0"
|
|
43
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
44
|
+
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
|
45
|
+
dependencies = [
|
|
46
|
+
"regex-automata 0.1.10",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[[package]]
|
|
50
|
+
name = "memchr"
|
|
51
|
+
version = "2.7.4"
|
|
52
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
53
|
+
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|
54
|
+
|
|
55
|
+
[[package]]
|
|
56
|
+
name = "nu-ansi-term"
|
|
57
|
+
version = "0.46.0"
|
|
58
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
59
|
+
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
|
60
|
+
dependencies = [
|
|
61
|
+
"overload",
|
|
62
|
+
"winapi",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[[package]]
|
|
66
|
+
name = "once_cell"
|
|
67
|
+
version = "1.20.3"
|
|
68
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
69
|
+
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "overload"
|
|
73
|
+
version = "0.1.1"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|
76
|
+
|
|
77
|
+
[[package]]
|
|
78
|
+
name = "pin-project-lite"
|
|
79
|
+
version = "0.2.16"
|
|
80
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
81
|
+
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|
82
|
+
|
|
83
|
+
[[package]]
|
|
84
|
+
name = "proc-macro2"
|
|
85
|
+
version = "1.0.93"
|
|
86
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
+
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
|
88
|
+
dependencies = [
|
|
89
|
+
"unicode-ident",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[[package]]
|
|
93
|
+
name = "quote"
|
|
94
|
+
version = "1.0.38"
|
|
95
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
96
|
+
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
|
97
|
+
dependencies = [
|
|
98
|
+
"proc-macro2",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
[[package]]
|
|
102
|
+
name = "regex"
|
|
103
|
+
version = "1.11.1"
|
|
104
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
105
|
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|
106
|
+
dependencies = [
|
|
107
|
+
"aho-corasick",
|
|
108
|
+
"memchr",
|
|
109
|
+
"regex-automata 0.4.9",
|
|
110
|
+
"regex-syntax 0.8.5",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[[package]]
|
|
114
|
+
name = "regex-automata"
|
|
115
|
+
version = "0.1.10"
|
|
116
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
117
|
+
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
|
118
|
+
dependencies = [
|
|
119
|
+
"regex-syntax 0.6.29",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "regex-automata"
|
|
124
|
+
version = "0.4.9"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
|
127
|
+
dependencies = [
|
|
128
|
+
"aho-corasick",
|
|
129
|
+
"memchr",
|
|
130
|
+
"regex-syntax 0.8.5",
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
[[package]]
|
|
134
|
+
name = "regex-syntax"
|
|
135
|
+
version = "0.6.29"
|
|
136
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
137
|
+
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|
138
|
+
|
|
139
|
+
[[package]]
|
|
140
|
+
name = "regex-syntax"
|
|
141
|
+
version = "0.8.5"
|
|
142
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
143
|
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
144
|
+
|
|
145
|
+
[[package]]
|
|
146
|
+
name = "sharded-slab"
|
|
147
|
+
version = "0.1.7"
|
|
148
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
149
|
+
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
|
150
|
+
dependencies = [
|
|
151
|
+
"lazy_static",
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
[[package]]
|
|
155
|
+
name = "smallvec"
|
|
156
|
+
version = "1.14.0"
|
|
157
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
158
|
+
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
|
159
|
+
|
|
160
|
+
[[package]]
|
|
161
|
+
name = "syn"
|
|
162
|
+
version = "2.0.98"
|
|
163
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
164
|
+
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
|
165
|
+
dependencies = [
|
|
166
|
+
"proc-macro2",
|
|
167
|
+
"quote",
|
|
168
|
+
"unicode-ident",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "thread_local"
|
|
173
|
+
version = "1.1.8"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"cfg-if",
|
|
178
|
+
"once_cell",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "tracing"
|
|
183
|
+
version = "0.1.41"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
|
186
|
+
dependencies = [
|
|
187
|
+
"pin-project-lite",
|
|
188
|
+
"tracing-attributes",
|
|
189
|
+
"tracing-core",
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
[[package]]
|
|
193
|
+
name = "tracing-attributes"
|
|
194
|
+
version = "0.1.28"
|
|
195
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
196
|
+
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
|
197
|
+
dependencies = [
|
|
198
|
+
"proc-macro2",
|
|
199
|
+
"quote",
|
|
200
|
+
"syn",
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
[[package]]
|
|
204
|
+
name = "tracing-core"
|
|
205
|
+
version = "0.1.33"
|
|
206
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
207
|
+
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
|
208
|
+
dependencies = [
|
|
209
|
+
"once_cell",
|
|
210
|
+
"valuable",
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
[[package]]
|
|
214
|
+
name = "tracing-log"
|
|
215
|
+
version = "0.2.0"
|
|
216
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
217
|
+
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
|
218
|
+
dependencies = [
|
|
219
|
+
"log",
|
|
220
|
+
"once_cell",
|
|
221
|
+
"tracing-core",
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
[[package]]
|
|
225
|
+
name = "tracing-subscriber"
|
|
226
|
+
version = "0.3.19"
|
|
227
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
228
|
+
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
|
229
|
+
dependencies = [
|
|
230
|
+
"matchers",
|
|
231
|
+
"nu-ansi-term",
|
|
232
|
+
"once_cell",
|
|
233
|
+
"regex",
|
|
234
|
+
"sharded-slab",
|
|
235
|
+
"smallvec",
|
|
236
|
+
"thread_local",
|
|
237
|
+
"tracing",
|
|
238
|
+
"tracing-core",
|
|
239
|
+
"tracing-log",
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
[[package]]
|
|
243
|
+
name = "unicode-ident"
|
|
244
|
+
version = "1.0.17"
|
|
245
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
246
|
+
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
|
247
|
+
|
|
248
|
+
[[package]]
|
|
249
|
+
name = "valuable"
|
|
250
|
+
version = "0.1.1"
|
|
251
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
252
|
+
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
|
253
|
+
|
|
254
|
+
[[package]]
|
|
255
|
+
name = "winapi"
|
|
256
|
+
version = "0.3.9"
|
|
257
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
258
|
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
259
|
+
dependencies = [
|
|
260
|
+
"winapi-i686-pc-windows-gnu",
|
|
261
|
+
"winapi-x86_64-pc-windows-gnu",
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
[[package]]
|
|
265
|
+
name = "winapi-i686-pc-windows-gnu"
|
|
266
|
+
version = "0.4.0"
|
|
267
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
268
|
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
269
|
+
|
|
270
|
+
[[package]]
|
|
271
|
+
name = "winapi-x86_64-pc-windows-gnu"
|
|
272
|
+
version = "0.4.0"
|
|
273
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
274
|
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|