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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c3da3f1e894f11eff41ba4294705e11b323b0c67ced5056ded2ba3bf3fe214e
4
- data.tar.gz: 7f47c950da4d638c639a9a5045bc21c6b9a1fa33180700bce2a1605816067ce2
3
+ metadata.gz: 43c8c15592c8e8e749938e5c5c4b5e481ba3ea88d82bac63059a612e35bf2213
4
+ data.tar.gz: e57ab7eacc32f7a0ac702be2e15ac9a36d48aff27aaa3705f77c428df2acf0f8
5
5
  SHA512:
6
- metadata.gz: 97a77b338eb51a461bc93d8f822111cd7582105252174fd4b29665eb6be508acb1ad314afa4286bba915d57da38d78922a81cc7a32c61a9c95495aee6f348aff
7
- data.tar.gz: 0c1484b3474f3a1acf5df54eeb0657a1dc4e5fb8d63a388576f39a9883e6bb19acb9630fedb19852d55c78bf4d81553ab0337201a573516c9a52da66988959a9
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; // for lock_exclusive, unlock
3
- use std::fs::OpenOptions;
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
- let lock_path = dir_path.join(".acme.lock");
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 lock_exclusive(&self) -> Result<std::fs::File, IoError> {
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
- Ok(lockfile)
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
- // Just delegate to the inner DirCache
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
- let lockfile = self.lock_exclusive()?;
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
- // Unlock and return
61
- let _ = fs2::FileExt::unlock(&lockfile);
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
- let lockfile = self.lock_exclusive()?;
126
+ self.lock_exclusive()?;
85
127
 
86
- let result = self
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::pki_types::{CertificateDer, PrivateKeyDer};
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:wc@pico.net.nz"])
54
- .cache(LockedDirCache::new("./rustls_acme_cache"))
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; // for lock_exclusive, unlock
3
- use std::fs::OpenOptions;
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
- let lock_path = dir_path.join(".acme.lock");
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 lock_exclusive(&self) -> Result<std::fs::File, IoError> {
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
- Ok(lockfile)
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
- // Just delegate to the inner DirCache
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
- let lockfile = self.lock_exclusive()?;
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
- // Unlock and return
61
- let _ = fs2::FileExt::unlock(&lockfile);
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
- let lockfile = self.lock_exclusive()?;
126
+ self.lock_exclusive()?;
85
127
 
86
- let result = self
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::pki_types::{CertificateDer, PrivateKeyDer};
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:wc@pico.net.nz"])
54
- .cache(LockedDirCache::new("./rustls_acme_cache"))
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()
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Scheduler
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.6"
6
6
  end
7
7
  end
@@ -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; // for lock_exclusive, unlock
3
- use std::fs::OpenOptions;
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
- let lock_path = dir_path.join(".acme.lock");
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 lock_exclusive(&self) -> Result<std::fs::File, IoError> {
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
- Ok(lockfile)
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
- // Just delegate to the inner DirCache
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
- let lockfile = self.lock_exclusive()?;
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
- // Unlock and return
61
- let _ = fs2::FileExt::unlock(&lockfile);
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
- let lockfile = self.lock_exclusive()?;
126
+ self.lock_exclusive()?;
85
127
 
86
- let result = self
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::pki_types::{CertificateDer, PrivateKeyDer};
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:wc@pico.net.nz"])
54
- .cache(LockedDirCache::new("./rustls_acme_cache"))
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()
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Itsi
4
4
  class Server
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.6"
6
6
  end
7
7
  end
data/lib/itsi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Itsi
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
data/tasks.txt CHANGED
@@ -1,5 +1,3 @@
1
- - Release v0.1.3
2
- - Autocert (Hard. And dev certs use local CA)
3
1
  - Test on MOD
4
2
  - Filters (Medium)
5
3
  - Static File Serve (Medium).error pages
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.5
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.4
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.4
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.4
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.4
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