itsi 0.1.5 → 0.1.6
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 +4 -4
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +1 -1
- data/crates/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
- data/crates/itsi_server/src/server/tls.rs +37 -3
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +1 -1
- data/gems/scheduler/ext/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
- data/gems/scheduler/ext/itsi_server/src/server/tls.rs +37 -3
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +1 -1
- data/gems/server/ext/itsi_server/src/server/tls/locked_dir_cache.rs +55 -17
- data/gems/server/ext/itsi_server/src/server/tls.rs +37 -3
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/lib/itsi/version.rb +1 -1
- data/tasks.txt +0 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43c8c15592c8e8e749938e5c5c4b5e481ba3ea88d82bac63059a612e35bf2213
|
4
|
+
data.tar.gz: e57ab7eacc32f7a0ac702be2e15ac9a36d48aff27aaa3705f77c428df2acf0f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ea20d0e8d9d82cb4b095674192ead9837334c5472761cce1dc093d43d829c2d71ecb74d06a667f4c8274607791bd1cd1ad12f005bf1bc8a3fa5179499a61d8f
|
7
|
+
data.tar.gz: c1aa8a84ebd694f3f39d287fd67e320c69f89dd35315af3203e09259c500404825790e925abd8b9a6ee26d5a97638a1d7442a9bb369653398922a42a08742498
|
@@ -83,7 +83,7 @@ impl SingleMode {
|
|
83
83
|
Ok(())
|
84
84
|
}
|
85
85
|
|
86
|
-
#[instrument(parent=None, skip(self))]
|
86
|
+
#[instrument(parent=None, skip(self), fields(pid=format!("{}", Pid::this())))]
|
87
87
|
pub fn run(self: Arc<Self>) -> Result<()> {
|
88
88
|
let mut listener_task_set = JoinSet::new();
|
89
89
|
let self_ref = Arc::new(self);
|
@@ -1,6 +1,8 @@
|
|
1
1
|
use async_trait::async_trait;
|
2
|
-
use fs2::FileExt;
|
3
|
-
use
|
2
|
+
use fs2::FileExt;
|
3
|
+
use parking_lot::Mutex;
|
4
|
+
use std::env;
|
5
|
+
use std::fs::{self, OpenOptions};
|
4
6
|
use std::io::Error as IoError;
|
5
7
|
use std::path::{Path, PathBuf};
|
6
8
|
use tokio_rustls_acme::caches::DirCache;
|
@@ -10,25 +12,57 @@ use tokio_rustls_acme::{AccountCache, CertCache};
|
|
10
12
|
pub struct LockedDirCache<P: AsRef<Path> + Send + Sync> {
|
11
13
|
inner: DirCache<P>,
|
12
14
|
lock_path: PathBuf,
|
15
|
+
current_lock: Mutex<Option<std::fs::File>>,
|
13
16
|
}
|
14
17
|
|
15
18
|
impl<P: AsRef<Path> + Send + Sync> LockedDirCache<P> {
|
16
19
|
pub fn new(dir: P) -> Self {
|
17
20
|
let dir_path = dir.as_ref().to_path_buf();
|
18
|
-
|
21
|
+
std::fs::create_dir_all(&dir_path).unwrap();
|
22
|
+
let lock_path =
|
23
|
+
dir_path.join(env::var("ITSI_ACME_LOCK_FILE_NAME").unwrap_or(".acme.lock".to_string()));
|
24
|
+
Self::touch_file(&lock_path).expect("Failed to create lock file");
|
25
|
+
|
19
26
|
Self {
|
20
27
|
inner: DirCache::new(dir),
|
21
28
|
lock_path,
|
29
|
+
current_lock: Mutex::new(None),
|
22
30
|
}
|
23
31
|
}
|
24
32
|
|
25
|
-
fn
|
33
|
+
fn touch_file(path: &PathBuf) -> std::io::Result<()> {
|
34
|
+
if let Some(parent) = path.parent() {
|
35
|
+
fs::create_dir_all(parent)?;
|
36
|
+
}
|
37
|
+
fs::OpenOptions::new()
|
38
|
+
.create(true)
|
39
|
+
.write(true)
|
40
|
+
.truncate(true)
|
41
|
+
.open(path)?;
|
42
|
+
Ok(())
|
43
|
+
}
|
44
|
+
|
45
|
+
fn lock_exclusive(&self) -> Result<(), IoError> {
|
46
|
+
if self.current_lock.lock().is_some() {
|
47
|
+
return Ok(());
|
48
|
+
}
|
49
|
+
|
50
|
+
if let Some(parent) = self.lock_path.parent() {
|
51
|
+
std::fs::create_dir_all(parent)?;
|
52
|
+
}
|
26
53
|
let lockfile = OpenOptions::new()
|
54
|
+
.create(true)
|
27
55
|
.write(true)
|
28
56
|
.truncate(true)
|
29
57
|
.open(&self.lock_path)?;
|
30
58
|
lockfile.lock_exclusive()?;
|
31
|
-
|
59
|
+
*self.current_lock.lock() = Some(lockfile);
|
60
|
+
Ok(())
|
61
|
+
}
|
62
|
+
|
63
|
+
fn unlock(&self) -> Result<(), IoError> {
|
64
|
+
self.current_lock.lock().take();
|
65
|
+
Ok(())
|
32
66
|
}
|
33
67
|
}
|
34
68
|
|
@@ -41,8 +75,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
41
75
|
domains: &[String],
|
42
76
|
directory_url: &str,
|
43
77
|
) -> Result<Option<Vec<u8>>, Self::EC> {
|
44
|
-
|
45
|
-
self.inner.load_cert(domains, directory_url).await
|
78
|
+
self.lock_exclusive()?;
|
79
|
+
let result = self.inner.load_cert(domains, directory_url).await;
|
80
|
+
|
81
|
+
if let Ok(Some(_)) = result {
|
82
|
+
self.unlock()?;
|
83
|
+
}
|
84
|
+
|
85
|
+
result
|
46
86
|
}
|
47
87
|
|
48
88
|
async fn store_cert(
|
@@ -52,13 +92,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
52
92
|
cert: &[u8],
|
53
93
|
) -> Result<(), Self::EC> {
|
54
94
|
// Acquire the lock before storing
|
55
|
-
|
95
|
+
self.lock_exclusive()?;
|
56
96
|
|
57
97
|
// Perform the store operation
|
58
98
|
let result = self.inner.store_cert(domains, directory_url, cert).await;
|
59
99
|
|
60
|
-
|
61
|
-
|
100
|
+
if let Ok(()) = result {
|
101
|
+
self.unlock()?;
|
102
|
+
}
|
62
103
|
result
|
63
104
|
}
|
64
105
|
}
|
@@ -72,6 +113,7 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
72
113
|
contact: &[String],
|
73
114
|
directory_url: &str,
|
74
115
|
) -> Result<Option<Vec<u8>>, Self::EA> {
|
116
|
+
self.lock_exclusive()?;
|
75
117
|
self.inner.load_account(contact, directory_url).await
|
76
118
|
}
|
77
119
|
|
@@ -81,14 +123,10 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
81
123
|
directory_url: &str,
|
82
124
|
account: &[u8],
|
83
125
|
) -> Result<(), Self::EA> {
|
84
|
-
|
126
|
+
self.lock_exclusive()?;
|
85
127
|
|
86
|
-
|
87
|
-
.inner
|
128
|
+
self.inner
|
88
129
|
.store_account(contact, directory_url, account)
|
89
|
-
.await
|
90
|
-
|
91
|
-
let _ = fs2::FileExt::unlock(&lockfile);
|
92
|
-
result
|
130
|
+
.await
|
93
131
|
}
|
94
132
|
}
|
@@ -3,7 +3,10 @@ use itsi_error::Result;
|
|
3
3
|
use itsi_tracing::info;
|
4
4
|
use locked_dir_cache::LockedDirCache;
|
5
5
|
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
|
6
|
-
use rustls::
|
6
|
+
use rustls::{
|
7
|
+
pki_types::{CertificateDer, PrivateKeyDer},
|
8
|
+
ClientConfig, RootCertStore,
|
9
|
+
};
|
7
10
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
8
11
|
use std::{
|
9
12
|
collections::HashMap,
|
@@ -49,10 +52,41 @@ pub fn configure_tls(
|
|
49
52
|
domains = format!("{:?}", domains),
|
50
53
|
directory_url, "Requesting acme cert"
|
51
54
|
);
|
55
|
+
|
56
|
+
let mut root_cert_store = RootCertStore::empty();
|
57
|
+
if let Ok(ca_pem_path) = env::var("ITSI_ACME_CA_PEM_PATH") {
|
58
|
+
let ca_pem = fs::read(ca_pem_path).expect("failed to read CA pem file");
|
59
|
+
let mut ca_reader = BufReader::new(&ca_pem[..]);
|
60
|
+
let der_certs: Vec<CertificateDer> = certs(&mut ca_reader)
|
61
|
+
.collect::<std::result::Result<Vec<CertificateDer>, _>>()
|
62
|
+
.map_err(|e| {
|
63
|
+
itsi_error::ItsiError::ArgumentError(format!(
|
64
|
+
"Invalid ACME CA Pem path {:?}",
|
65
|
+
e
|
66
|
+
))
|
67
|
+
})?;
|
68
|
+
root_cert_store.add_parsable_certificates(der_certs);
|
69
|
+
}
|
70
|
+
|
71
|
+
let client_config = ClientConfig::builder()
|
72
|
+
.with_root_certificates(root_cert_store)
|
73
|
+
.with_no_client_auth();
|
74
|
+
|
75
|
+
let contact_email = env::var("ITSI_ACME_CONTACT_EMAIL").map_err(|_| {
|
76
|
+
itsi_error::ItsiError::ArgumentError(
|
77
|
+
"ITSI_ACME_CONTACT_EMAIL must be set before you can auto-generate production certificates"
|
78
|
+
.to_string(),
|
79
|
+
)
|
80
|
+
})?;
|
81
|
+
|
82
|
+
let cache_dir = env::var("ITSI_ACME_CACHE_DIR")
|
83
|
+
.unwrap_or_else(|_| "./.rustls_acme_cache".to_string());
|
84
|
+
|
52
85
|
let acme_state = AcmeConfig::new(domains)
|
53
|
-
.contact(["mailto:
|
54
|
-
.cache(LockedDirCache::new(
|
86
|
+
.contact([format!("mailto:{}", contact_email)])
|
87
|
+
.cache(LockedDirCache::new(cache_dir))
|
55
88
|
.directory(directory_url)
|
89
|
+
.client_tls_config(Arc::new(client_config))
|
56
90
|
.state();
|
57
91
|
let rustls_config = ServerConfig::builder()
|
58
92
|
.with_no_client_auth()
|
@@ -83,7 +83,7 @@ impl SingleMode {
|
|
83
83
|
Ok(())
|
84
84
|
}
|
85
85
|
|
86
|
-
#[instrument(parent=None, skip(self))]
|
86
|
+
#[instrument(parent=None, skip(self), fields(pid=format!("{}", Pid::this())))]
|
87
87
|
pub fn run(self: Arc<Self>) -> Result<()> {
|
88
88
|
let mut listener_task_set = JoinSet::new();
|
89
89
|
let self_ref = Arc::new(self);
|
@@ -1,6 +1,8 @@
|
|
1
1
|
use async_trait::async_trait;
|
2
|
-
use fs2::FileExt;
|
3
|
-
use
|
2
|
+
use fs2::FileExt;
|
3
|
+
use parking_lot::Mutex;
|
4
|
+
use std::env;
|
5
|
+
use std::fs::{self, OpenOptions};
|
4
6
|
use std::io::Error as IoError;
|
5
7
|
use std::path::{Path, PathBuf};
|
6
8
|
use tokio_rustls_acme::caches::DirCache;
|
@@ -10,25 +12,57 @@ use tokio_rustls_acme::{AccountCache, CertCache};
|
|
10
12
|
pub struct LockedDirCache<P: AsRef<Path> + Send + Sync> {
|
11
13
|
inner: DirCache<P>,
|
12
14
|
lock_path: PathBuf,
|
15
|
+
current_lock: Mutex<Option<std::fs::File>>,
|
13
16
|
}
|
14
17
|
|
15
18
|
impl<P: AsRef<Path> + Send + Sync> LockedDirCache<P> {
|
16
19
|
pub fn new(dir: P) -> Self {
|
17
20
|
let dir_path = dir.as_ref().to_path_buf();
|
18
|
-
|
21
|
+
std::fs::create_dir_all(&dir_path).unwrap();
|
22
|
+
let lock_path =
|
23
|
+
dir_path.join(env::var("ITSI_ACME_LOCK_FILE_NAME").unwrap_or(".acme.lock".to_string()));
|
24
|
+
Self::touch_file(&lock_path).expect("Failed to create lock file");
|
25
|
+
|
19
26
|
Self {
|
20
27
|
inner: DirCache::new(dir),
|
21
28
|
lock_path,
|
29
|
+
current_lock: Mutex::new(None),
|
22
30
|
}
|
23
31
|
}
|
24
32
|
|
25
|
-
fn
|
33
|
+
fn touch_file(path: &PathBuf) -> std::io::Result<()> {
|
34
|
+
if let Some(parent) = path.parent() {
|
35
|
+
fs::create_dir_all(parent)?;
|
36
|
+
}
|
37
|
+
fs::OpenOptions::new()
|
38
|
+
.create(true)
|
39
|
+
.write(true)
|
40
|
+
.truncate(true)
|
41
|
+
.open(path)?;
|
42
|
+
Ok(())
|
43
|
+
}
|
44
|
+
|
45
|
+
fn lock_exclusive(&self) -> Result<(), IoError> {
|
46
|
+
if self.current_lock.lock().is_some() {
|
47
|
+
return Ok(());
|
48
|
+
}
|
49
|
+
|
50
|
+
if let Some(parent) = self.lock_path.parent() {
|
51
|
+
std::fs::create_dir_all(parent)?;
|
52
|
+
}
|
26
53
|
let lockfile = OpenOptions::new()
|
54
|
+
.create(true)
|
27
55
|
.write(true)
|
28
56
|
.truncate(true)
|
29
57
|
.open(&self.lock_path)?;
|
30
58
|
lockfile.lock_exclusive()?;
|
31
|
-
|
59
|
+
*self.current_lock.lock() = Some(lockfile);
|
60
|
+
Ok(())
|
61
|
+
}
|
62
|
+
|
63
|
+
fn unlock(&self) -> Result<(), IoError> {
|
64
|
+
self.current_lock.lock().take();
|
65
|
+
Ok(())
|
32
66
|
}
|
33
67
|
}
|
34
68
|
|
@@ -41,8 +75,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
41
75
|
domains: &[String],
|
42
76
|
directory_url: &str,
|
43
77
|
) -> Result<Option<Vec<u8>>, Self::EC> {
|
44
|
-
|
45
|
-
self.inner.load_cert(domains, directory_url).await
|
78
|
+
self.lock_exclusive()?;
|
79
|
+
let result = self.inner.load_cert(domains, directory_url).await;
|
80
|
+
|
81
|
+
if let Ok(Some(_)) = result {
|
82
|
+
self.unlock()?;
|
83
|
+
}
|
84
|
+
|
85
|
+
result
|
46
86
|
}
|
47
87
|
|
48
88
|
async fn store_cert(
|
@@ -52,13 +92,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
52
92
|
cert: &[u8],
|
53
93
|
) -> Result<(), Self::EC> {
|
54
94
|
// Acquire the lock before storing
|
55
|
-
|
95
|
+
self.lock_exclusive()?;
|
56
96
|
|
57
97
|
// Perform the store operation
|
58
98
|
let result = self.inner.store_cert(domains, directory_url, cert).await;
|
59
99
|
|
60
|
-
|
61
|
-
|
100
|
+
if let Ok(()) = result {
|
101
|
+
self.unlock()?;
|
102
|
+
}
|
62
103
|
result
|
63
104
|
}
|
64
105
|
}
|
@@ -72,6 +113,7 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
72
113
|
contact: &[String],
|
73
114
|
directory_url: &str,
|
74
115
|
) -> Result<Option<Vec<u8>>, Self::EA> {
|
116
|
+
self.lock_exclusive()?;
|
75
117
|
self.inner.load_account(contact, directory_url).await
|
76
118
|
}
|
77
119
|
|
@@ -81,14 +123,10 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
81
123
|
directory_url: &str,
|
82
124
|
account: &[u8],
|
83
125
|
) -> Result<(), Self::EA> {
|
84
|
-
|
126
|
+
self.lock_exclusive()?;
|
85
127
|
|
86
|
-
|
87
|
-
.inner
|
128
|
+
self.inner
|
88
129
|
.store_account(contact, directory_url, account)
|
89
|
-
.await
|
90
|
-
|
91
|
-
let _ = fs2::FileExt::unlock(&lockfile);
|
92
|
-
result
|
130
|
+
.await
|
93
131
|
}
|
94
132
|
}
|
@@ -3,7 +3,10 @@ use itsi_error::Result;
|
|
3
3
|
use itsi_tracing::info;
|
4
4
|
use locked_dir_cache::LockedDirCache;
|
5
5
|
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
|
6
|
-
use rustls::
|
6
|
+
use rustls::{
|
7
|
+
pki_types::{CertificateDer, PrivateKeyDer},
|
8
|
+
ClientConfig, RootCertStore,
|
9
|
+
};
|
7
10
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
8
11
|
use std::{
|
9
12
|
collections::HashMap,
|
@@ -49,10 +52,41 @@ pub fn configure_tls(
|
|
49
52
|
domains = format!("{:?}", domains),
|
50
53
|
directory_url, "Requesting acme cert"
|
51
54
|
);
|
55
|
+
|
56
|
+
let mut root_cert_store = RootCertStore::empty();
|
57
|
+
if let Ok(ca_pem_path) = env::var("ITSI_ACME_CA_PEM_PATH") {
|
58
|
+
let ca_pem = fs::read(ca_pem_path).expect("failed to read CA pem file");
|
59
|
+
let mut ca_reader = BufReader::new(&ca_pem[..]);
|
60
|
+
let der_certs: Vec<CertificateDer> = certs(&mut ca_reader)
|
61
|
+
.collect::<std::result::Result<Vec<CertificateDer>, _>>()
|
62
|
+
.map_err(|e| {
|
63
|
+
itsi_error::ItsiError::ArgumentError(format!(
|
64
|
+
"Invalid ACME CA Pem path {:?}",
|
65
|
+
e
|
66
|
+
))
|
67
|
+
})?;
|
68
|
+
root_cert_store.add_parsable_certificates(der_certs);
|
69
|
+
}
|
70
|
+
|
71
|
+
let client_config = ClientConfig::builder()
|
72
|
+
.with_root_certificates(root_cert_store)
|
73
|
+
.with_no_client_auth();
|
74
|
+
|
75
|
+
let contact_email = env::var("ITSI_ACME_CONTACT_EMAIL").map_err(|_| {
|
76
|
+
itsi_error::ItsiError::ArgumentError(
|
77
|
+
"ITSI_ACME_CONTACT_EMAIL must be set before you can auto-generate production certificates"
|
78
|
+
.to_string(),
|
79
|
+
)
|
80
|
+
})?;
|
81
|
+
|
82
|
+
let cache_dir = env::var("ITSI_ACME_CACHE_DIR")
|
83
|
+
.unwrap_or_else(|_| "./.rustls_acme_cache".to_string());
|
84
|
+
|
52
85
|
let acme_state = AcmeConfig::new(domains)
|
53
|
-
.contact(["mailto:
|
54
|
-
.cache(LockedDirCache::new(
|
86
|
+
.contact([format!("mailto:{}", contact_email)])
|
87
|
+
.cache(LockedDirCache::new(cache_dir))
|
55
88
|
.directory(directory_url)
|
89
|
+
.client_tls_config(Arc::new(client_config))
|
56
90
|
.state();
|
57
91
|
let rustls_config = ServerConfig::builder()
|
58
92
|
.with_no_client_auth()
|
@@ -83,7 +83,7 @@ impl SingleMode {
|
|
83
83
|
Ok(())
|
84
84
|
}
|
85
85
|
|
86
|
-
#[instrument(parent=None, skip(self))]
|
86
|
+
#[instrument(parent=None, skip(self), fields(pid=format!("{}", Pid::this())))]
|
87
87
|
pub fn run(self: Arc<Self>) -> Result<()> {
|
88
88
|
let mut listener_task_set = JoinSet::new();
|
89
89
|
let self_ref = Arc::new(self);
|
@@ -1,6 +1,8 @@
|
|
1
1
|
use async_trait::async_trait;
|
2
|
-
use fs2::FileExt;
|
3
|
-
use
|
2
|
+
use fs2::FileExt;
|
3
|
+
use parking_lot::Mutex;
|
4
|
+
use std::env;
|
5
|
+
use std::fs::{self, OpenOptions};
|
4
6
|
use std::io::Error as IoError;
|
5
7
|
use std::path::{Path, PathBuf};
|
6
8
|
use tokio_rustls_acme::caches::DirCache;
|
@@ -10,25 +12,57 @@ use tokio_rustls_acme::{AccountCache, CertCache};
|
|
10
12
|
pub struct LockedDirCache<P: AsRef<Path> + Send + Sync> {
|
11
13
|
inner: DirCache<P>,
|
12
14
|
lock_path: PathBuf,
|
15
|
+
current_lock: Mutex<Option<std::fs::File>>,
|
13
16
|
}
|
14
17
|
|
15
18
|
impl<P: AsRef<Path> + Send + Sync> LockedDirCache<P> {
|
16
19
|
pub fn new(dir: P) -> Self {
|
17
20
|
let dir_path = dir.as_ref().to_path_buf();
|
18
|
-
|
21
|
+
std::fs::create_dir_all(&dir_path).unwrap();
|
22
|
+
let lock_path =
|
23
|
+
dir_path.join(env::var("ITSI_ACME_LOCK_FILE_NAME").unwrap_or(".acme.lock".to_string()));
|
24
|
+
Self::touch_file(&lock_path).expect("Failed to create lock file");
|
25
|
+
|
19
26
|
Self {
|
20
27
|
inner: DirCache::new(dir),
|
21
28
|
lock_path,
|
29
|
+
current_lock: Mutex::new(None),
|
22
30
|
}
|
23
31
|
}
|
24
32
|
|
25
|
-
fn
|
33
|
+
fn touch_file(path: &PathBuf) -> std::io::Result<()> {
|
34
|
+
if let Some(parent) = path.parent() {
|
35
|
+
fs::create_dir_all(parent)?;
|
36
|
+
}
|
37
|
+
fs::OpenOptions::new()
|
38
|
+
.create(true)
|
39
|
+
.write(true)
|
40
|
+
.truncate(true)
|
41
|
+
.open(path)?;
|
42
|
+
Ok(())
|
43
|
+
}
|
44
|
+
|
45
|
+
fn lock_exclusive(&self) -> Result<(), IoError> {
|
46
|
+
if self.current_lock.lock().is_some() {
|
47
|
+
return Ok(());
|
48
|
+
}
|
49
|
+
|
50
|
+
if let Some(parent) = self.lock_path.parent() {
|
51
|
+
std::fs::create_dir_all(parent)?;
|
52
|
+
}
|
26
53
|
let lockfile = OpenOptions::new()
|
54
|
+
.create(true)
|
27
55
|
.write(true)
|
28
56
|
.truncate(true)
|
29
57
|
.open(&self.lock_path)?;
|
30
58
|
lockfile.lock_exclusive()?;
|
31
|
-
|
59
|
+
*self.current_lock.lock() = Some(lockfile);
|
60
|
+
Ok(())
|
61
|
+
}
|
62
|
+
|
63
|
+
fn unlock(&self) -> Result<(), IoError> {
|
64
|
+
self.current_lock.lock().take();
|
65
|
+
Ok(())
|
32
66
|
}
|
33
67
|
}
|
34
68
|
|
@@ -41,8 +75,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
41
75
|
domains: &[String],
|
42
76
|
directory_url: &str,
|
43
77
|
) -> Result<Option<Vec<u8>>, Self::EC> {
|
44
|
-
|
45
|
-
self.inner.load_cert(domains, directory_url).await
|
78
|
+
self.lock_exclusive()?;
|
79
|
+
let result = self.inner.load_cert(domains, directory_url).await;
|
80
|
+
|
81
|
+
if let Ok(Some(_)) = result {
|
82
|
+
self.unlock()?;
|
83
|
+
}
|
84
|
+
|
85
|
+
result
|
46
86
|
}
|
47
87
|
|
48
88
|
async fn store_cert(
|
@@ -52,13 +92,14 @@ impl<P: AsRef<Path> + Send + Sync> CertCache for LockedDirCache<P> {
|
|
52
92
|
cert: &[u8],
|
53
93
|
) -> Result<(), Self::EC> {
|
54
94
|
// Acquire the lock before storing
|
55
|
-
|
95
|
+
self.lock_exclusive()?;
|
56
96
|
|
57
97
|
// Perform the store operation
|
58
98
|
let result = self.inner.store_cert(domains, directory_url, cert).await;
|
59
99
|
|
60
|
-
|
61
|
-
|
100
|
+
if let Ok(()) = result {
|
101
|
+
self.unlock()?;
|
102
|
+
}
|
62
103
|
result
|
63
104
|
}
|
64
105
|
}
|
@@ -72,6 +113,7 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
72
113
|
contact: &[String],
|
73
114
|
directory_url: &str,
|
74
115
|
) -> Result<Option<Vec<u8>>, Self::EA> {
|
116
|
+
self.lock_exclusive()?;
|
75
117
|
self.inner.load_account(contact, directory_url).await
|
76
118
|
}
|
77
119
|
|
@@ -81,14 +123,10 @@ impl<P: AsRef<Path> + Send + Sync> AccountCache for LockedDirCache<P> {
|
|
81
123
|
directory_url: &str,
|
82
124
|
account: &[u8],
|
83
125
|
) -> Result<(), Self::EA> {
|
84
|
-
|
126
|
+
self.lock_exclusive()?;
|
85
127
|
|
86
|
-
|
87
|
-
.inner
|
128
|
+
self.inner
|
88
129
|
.store_account(contact, directory_url, account)
|
89
|
-
.await
|
90
|
-
|
91
|
-
let _ = fs2::FileExt::unlock(&lockfile);
|
92
|
-
result
|
130
|
+
.await
|
93
131
|
}
|
94
132
|
}
|
@@ -3,7 +3,10 @@ use itsi_error::Result;
|
|
3
3
|
use itsi_tracing::info;
|
4
4
|
use locked_dir_cache::LockedDirCache;
|
5
5
|
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
|
6
|
-
use rustls::
|
6
|
+
use rustls::{
|
7
|
+
pki_types::{CertificateDer, PrivateKeyDer},
|
8
|
+
ClientConfig, RootCertStore,
|
9
|
+
};
|
7
10
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
8
11
|
use std::{
|
9
12
|
collections::HashMap,
|
@@ -49,10 +52,41 @@ pub fn configure_tls(
|
|
49
52
|
domains = format!("{:?}", domains),
|
50
53
|
directory_url, "Requesting acme cert"
|
51
54
|
);
|
55
|
+
|
56
|
+
let mut root_cert_store = RootCertStore::empty();
|
57
|
+
if let Ok(ca_pem_path) = env::var("ITSI_ACME_CA_PEM_PATH") {
|
58
|
+
let ca_pem = fs::read(ca_pem_path).expect("failed to read CA pem file");
|
59
|
+
let mut ca_reader = BufReader::new(&ca_pem[..]);
|
60
|
+
let der_certs: Vec<CertificateDer> = certs(&mut ca_reader)
|
61
|
+
.collect::<std::result::Result<Vec<CertificateDer>, _>>()
|
62
|
+
.map_err(|e| {
|
63
|
+
itsi_error::ItsiError::ArgumentError(format!(
|
64
|
+
"Invalid ACME CA Pem path {:?}",
|
65
|
+
e
|
66
|
+
))
|
67
|
+
})?;
|
68
|
+
root_cert_store.add_parsable_certificates(der_certs);
|
69
|
+
}
|
70
|
+
|
71
|
+
let client_config = ClientConfig::builder()
|
72
|
+
.with_root_certificates(root_cert_store)
|
73
|
+
.with_no_client_auth();
|
74
|
+
|
75
|
+
let contact_email = env::var("ITSI_ACME_CONTACT_EMAIL").map_err(|_| {
|
76
|
+
itsi_error::ItsiError::ArgumentError(
|
77
|
+
"ITSI_ACME_CONTACT_EMAIL must be set before you can auto-generate production certificates"
|
78
|
+
.to_string(),
|
79
|
+
)
|
80
|
+
})?;
|
81
|
+
|
82
|
+
let cache_dir = env::var("ITSI_ACME_CACHE_DIR")
|
83
|
+
.unwrap_or_else(|_| "./.rustls_acme_cache".to_string());
|
84
|
+
|
52
85
|
let acme_state = AcmeConfig::new(domains)
|
53
|
-
.contact(["mailto:
|
54
|
-
.cache(LockedDirCache::new(
|
86
|
+
.contact([format!("mailto:{}", contact_email)])
|
87
|
+
.cache(LockedDirCache::new(cache_dir))
|
55
88
|
.directory(directory_url)
|
89
|
+
.client_tls_config(Arc::new(client_config))
|
56
90
|
.state();
|
57
91
|
let rustls_config = ServerConfig::builder()
|
58
92
|
.with_no_client_auth()
|
data/lib/itsi/version.rb
CHANGED
data/tasks.txt
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itsi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
@@ -15,28 +15,28 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: 0.1.
|
18
|
+
version: 0.1.6
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 0.1.
|
25
|
+
version: 0.1.6
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: itsi-scheduler
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.1.
|
32
|
+
version: 0.1.6
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 0.1.
|
39
|
+
version: 0.1.6
|
40
40
|
description: Wrapper Gem for both the Itsi server and it's Fiber scheduler
|
41
41
|
email:
|
42
42
|
- wc@pico.net.nz
|