itsi-server 0.1.19 → 0.1.20

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.

Potentially problematic release.


This version of itsi-server might be problematic. Click here for more details.

Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +950 -239
  3. data/README.md +2 -0
  4. data/exe/itsi +5 -5
  5. data/ext/itsi_acme/Cargo.toml +86 -0
  6. data/ext/itsi_acme/examples/high_level.rs +63 -0
  7. data/ext/itsi_acme/examples/high_level_warp.rs +52 -0
  8. data/ext/itsi_acme/examples/low_level.rs +87 -0
  9. data/ext/itsi_acme/examples/low_level_axum.rs +66 -0
  10. data/ext/itsi_acme/src/acceptor.rs +81 -0
  11. data/ext/itsi_acme/src/acme.rs +354 -0
  12. data/ext/itsi_acme/src/axum.rs +86 -0
  13. data/ext/itsi_acme/src/cache.rs +39 -0
  14. data/ext/itsi_acme/src/caches/boxed.rs +80 -0
  15. data/ext/itsi_acme/src/caches/composite.rs +69 -0
  16. data/ext/itsi_acme/src/caches/dir.rs +106 -0
  17. data/ext/itsi_acme/src/caches/mod.rs +11 -0
  18. data/ext/itsi_acme/src/caches/no.rs +78 -0
  19. data/ext/itsi_acme/src/caches/test.rs +136 -0
  20. data/ext/itsi_acme/src/config.rs +172 -0
  21. data/ext/itsi_acme/src/https_helper.rs +69 -0
  22. data/ext/itsi_acme/src/incoming.rs +142 -0
  23. data/ext/itsi_acme/src/jose.rs +161 -0
  24. data/ext/itsi_acme/src/lib.rs +142 -0
  25. data/ext/itsi_acme/src/resolver.rs +59 -0
  26. data/ext/itsi_acme/src/state.rs +424 -0
  27. data/ext/itsi_server/Cargo.toml +3 -3
  28. data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +2 -2
  29. data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +150 -19
  30. data/ext/itsi_server/src/ruby_types/itsi_server.rs +1 -0
  31. data/ext/itsi_server/src/server/binds/listener.rs +34 -29
  32. data/ext/itsi_server/src/server/binds/tls/locked_dir_cache.rs +2 -2
  33. data/ext/itsi_server/src/server/binds/tls.rs +1 -1
  34. data/ext/itsi_server/src/server/middleware_stack/middleware.rs +33 -28
  35. data/ext/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +56 -3
  36. data/ext/itsi_server/src/server/middleware_stack/middlewares/csp.rs +179 -0
  37. data/ext/itsi_server/src/server/middleware_stack/middlewares/mod.rs +25 -2
  38. data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +3 -3
  39. data/ext/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +2 -1
  40. data/ext/itsi_server/src/server/middleware_stack/mod.rs +32 -34
  41. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +10 -4
  42. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +30 -7
  43. data/ext/itsi_server/src/server/thread_worker.rs +2 -2
  44. data/ext/itsi_server/src/services/static_file_server.rs +30 -28
  45. data/ext/itsi_tracing/src/lib.rs +39 -8
  46. data/lib/itsi/server/config/config_helpers.rb +93 -0
  47. data/lib/itsi/server/config/dsl.rb +81 -33
  48. data/lib/itsi/server/config/known_paths/KitchensinkDirectories.txt +2346 -0
  49. data/lib/itsi/server/config/known_paths/Randomfiles.txt +24 -0
  50. data/lib/itsi/server/config/known_paths/UnixDotfiles.txt +52 -0
  51. data/lib/itsi/server/config/known_paths/backdoors/ASP_CommonBackdoors.txt +29 -0
  52. data/lib/itsi/server/config/known_paths/backdoors/bot_control_panels.txt +1668 -0
  53. data/lib/itsi/server/config/known_paths/backdoors/shells.txt +1167 -0
  54. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST.txt +7 -0
  55. data/lib/itsi/server/config/known_paths/cgi/CGI_HTTP_POST_Windows.txt +6 -0
  56. data/lib/itsi/server/config/known_paths/cgi/CGI_Microsoft.txt +79 -0
  57. data/lib/itsi/server/config/known_paths/cgi/CGI_XPlatform.txt +3948 -0
  58. data/lib/itsi/server/config/known_paths/cms/README.md +5 -0
  59. data/lib/itsi/server/config/known_paths/cms/drupal_plugins.txt +6320 -0
  60. data/lib/itsi/server/config/known_paths/cms/drupal_themes.txt +828 -0
  61. data/lib/itsi/server/config/known_paths/cms/joomla_plugins.txt +224 -0
  62. data/lib/itsi/server/config/known_paths/cms/joomla_themes.txt +30 -0
  63. data/lib/itsi/server/config/known_paths/cms/php-nuke.txt +2142 -0
  64. data/lib/itsi/server/config/known_paths/cms/wordpress.txt +1566 -0
  65. data/lib/itsi/server/config/known_paths/cms/wp_common_theme_files.txt +46 -0
  66. data/lib/itsi/server/config/known_paths/cms/wp_plugins.txt +13366 -0
  67. data/lib/itsi/server/config/known_paths/cms/wp_plugins_full.txt +68662 -0
  68. data/lib/itsi/server/config/known_paths/cms/wp_plugins_top225.txt +225 -0
  69. data/lib/itsi/server/config/known_paths/cms/wp_themes.readme +12 -0
  70. data/lib/itsi/server/config/known_paths/cms/wp_themes.txt +7336 -0
  71. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/3CharExtBrute.txt +17576 -0
  72. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/CommonWebExtensions.txt +80 -0
  73. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Backup.txt +14 -0
  74. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Common.txt +865 -0
  75. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Compressed.txt +186 -0
  76. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Mostcommon.txt +30 -0
  77. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/Extensions.Skipfish.txt +93 -0
  78. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/WordlistSkipfish.txt +1918 -0
  79. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/copy_of.txt +8 -0
  80. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories-lowercase.txt +56180 -0
  81. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-directories.txt +62290 -0
  82. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions-lowercase.txt +2367 -0
  83. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-extensions.txt +2450 -0
  84. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files-lowercase.txt +35323 -0
  85. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-files.txt +37037 -0
  86. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words-lowercase.txt +107982 -0
  87. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-large-words.txt +119600 -0
  88. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories-lowercase.txt +26593 -0
  89. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-directories.txt +30009 -0
  90. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions-lowercase.txt +1233 -0
  91. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-extensions.txt +1289 -0
  92. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files-lowercase.txt +16243 -0
  93. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-files.txt +17128 -0
  94. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words-lowercase.txt +56293 -0
  95. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-medium-words.txt +63087 -0
  96. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories-lowercase.txt +17776 -0
  97. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-directories.txt +20122 -0
  98. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions-lowercase.txt +914 -0
  99. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-extensions.txt +963 -0
  100. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files-lowercase.txt +10848 -0
  101. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-files.txt +11424 -0
  102. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words-lowercase.txt +38267 -0
  103. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/raft-small-words.txt +43003 -0
  104. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/spanish.txt +445 -0
  105. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/test_demo.txt +36 -0
  106. data/lib/itsi/server/config/known_paths/filename-dirname-bruteforce/upload_variants.txt +44 -0
  107. data/lib/itsi/server/config/known_paths/login-file-locations/Logins.txt +71 -0
  108. data/lib/itsi/server/config/known_paths/login-file-locations/cfm.txt +294 -0
  109. data/lib/itsi/server/config/known_paths/login-file-locations/html.txt +295 -0
  110. data/lib/itsi/server/config/known_paths/login-file-locations/jsp.txt +294 -0
  111. data/lib/itsi/server/config/known_paths/login-file-locations/php.txt +294 -0
  112. data/lib/itsi/server/config/known_paths/login-file-locations/windows-asp.txt +294 -0
  113. data/lib/itsi/server/config/known_paths/login-file-locations/windows-aspx.txt +294 -0
  114. data/lib/itsi/server/config/known_paths/password-file-locations/Passwords.txt +47 -0
  115. data/lib/itsi/server/config/known_paths/php/PHP.txt +30 -0
  116. data/lib/itsi/server/config/known_paths/php/PHP_CommonBackdoors.txt +5 -0
  117. data/lib/itsi/server/config/known_paths/proxy-conf.txt +31 -0
  118. data/lib/itsi/server/config/known_paths/tftp.txt +79 -0
  119. data/lib/itsi/server/config/known_paths/webservers-appservers/ADFS.txt +86 -0
  120. data/lib/itsi/server/config/known_paths/webservers-appservers/AdobeXML.txt +16 -0
  121. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache.txt +101 -0
  122. data/lib/itsi/server/config/known_paths/webservers-appservers/ApacheTomcat.txt +47 -0
  123. data/lib/itsi/server/config/known_paths/webservers-appservers/Apache_Axis.txt +16 -0
  124. data/lib/itsi/server/config/known_paths/webservers-appservers/ColdFusion.txt +111 -0
  125. data/lib/itsi/server/config/known_paths/webservers-appservers/FatwireCMS.txt +390 -0
  126. data/lib/itsi/server/config/known_paths/webservers-appservers/Frontpage.txt +38 -0
  127. data/lib/itsi/server/config/known_paths/webservers-appservers/HP_System_Mgmt_Homepage.txt +239 -0
  128. data/lib/itsi/server/config/known_paths/webservers-appservers/HTTP_POST_Microsoft.txt +2 -0
  129. data/lib/itsi/server/config/known_paths/webservers-appservers/Hyperion.txt +578 -0
  130. data/lib/itsi/server/config/known_paths/webservers-appservers/IIS.txt +187 -0
  131. data/lib/itsi/server/config/known_paths/webservers-appservers/JBoss.txt +5 -0
  132. data/lib/itsi/server/config/known_paths/webservers-appservers/JRun.txt +13 -0
  133. data/lib/itsi/server/config/known_paths/webservers-appservers/JavaServlets_Common.txt +3 -0
  134. data/lib/itsi/server/config/known_paths/webservers-appservers/Joomla_exploitable.txt +1937 -0
  135. data/lib/itsi/server/config/known_paths/webservers-appservers/LotusNotes.txt +206 -0
  136. data/lib/itsi/server/config/known_paths/webservers-appservers/Netware.txt +18 -0
  137. data/lib/itsi/server/config/known_paths/webservers-appservers/Oracle9i.txt +60 -0
  138. data/lib/itsi/server/config/known_paths/webservers-appservers/OracleAppServer.txt +192 -0
  139. data/lib/itsi/server/config/known_paths/webservers-appservers/README.md +6 -0
  140. data/lib/itsi/server/config/known_paths/webservers-appservers/Ruby_Rails.txt +121 -0
  141. data/lib/itsi/server/config/known_paths/webservers-appservers/SAP.txt +463 -0
  142. data/lib/itsi/server/config/known_paths/webservers-appservers/Sharepoint.txt +1707 -0
  143. data/lib/itsi/server/config/known_paths/webservers-appservers/SiteMinder.txt +19 -0
  144. data/lib/itsi/server/config/known_paths/webservers-appservers/SunAppServerGlassfish.txt +51 -0
  145. data/lib/itsi/server/config/known_paths/webservers-appservers/SuniPlanet.txt +35 -0
  146. data/lib/itsi/server/config/known_paths/webservers-appservers/Vignette.txt +73 -0
  147. data/lib/itsi/server/config/known_paths/webservers-appservers/Weblogic.txt +160 -0
  148. data/lib/itsi/server/config/known_paths/webservers-appservers/Websphere.txt +366 -0
  149. data/lib/itsi/server/config/known_paths/wellknown-rfc5785.txt +30 -0
  150. data/lib/itsi/server/config/known_paths.rb +17 -0
  151. data/lib/itsi/server/config/middleware/_index.md +54 -0
  152. data/lib/itsi/server/config/middleware/log_requests.md +63 -0
  153. data/lib/itsi/server/config/middleware/log_requests.rb +33 -0
  154. data/lib/itsi/server/config/middleware.rb +9 -0
  155. data/lib/itsi/server/config/option.rb +9 -0
  156. data/lib/itsi/server/config/options/_index.md +36 -0
  157. data/lib/itsi/server/config/options/fiber_scheduler.md +35 -0
  158. data/lib/itsi/server/config/options/fiber_scheduler.rb +18 -0
  159. data/lib/itsi/server/config/options/threads.md +39 -0
  160. data/lib/itsi/server/config/options/threads.rb +17 -0
  161. data/lib/itsi/server/config/options/workers.md +43 -0
  162. data/lib/itsi/server/config/options/workers.rb +17 -0
  163. data/lib/itsi/server/config/typed_struct.rb +203 -0
  164. data/lib/itsi/server/config.rb +124 -30
  165. data/lib/itsi/server/signal_trap.rb +5 -1
  166. data/lib/itsi/server/typed_handlers/source_parser.rb +1 -1
  167. data/lib/itsi/server/version.rb +1 -1
  168. data/lib/itsi/server.rb +27 -6
  169. data/lib/ruby_lsp/itsi/addon.rb +64 -48
  170. metadata +141 -5
  171. data/CHANGELOG.md +0 -10
  172. data/CODE_OF_CONDUCT.md +0 -139
  173. data/LICENSE.txt +0 -21
  174. data/_index.md +0 -6
@@ -0,0 +1,424 @@
1
+ use std::convert::Infallible;
2
+ use std::fmt::Debug;
3
+ use std::future::Future;
4
+ use std::pin::Pin;
5
+ use std::sync::Arc;
6
+ use std::task::{Context, Poll};
7
+ use std::time::Duration;
8
+
9
+ use chrono::{DateTime, TimeZone, Utc};
10
+ use futures::future::try_join_all;
11
+ use futures::{ready, FutureExt, Stream};
12
+ use rcgen::{CertificateParams, DistinguishedName, Error as RcgenError, PKCS_ECDSA_P256_SHA256};
13
+ use rustls::crypto::ring::sign::any_ecdsa_type;
14
+ use rustls::pki_types::{CertificateDer as RustlsCertificate, PrivateKeyDer, PrivatePkcs8KeyDer};
15
+ use rustls::sign::CertifiedKey;
16
+ use thiserror::Error;
17
+ use tokio::io::{AsyncRead, AsyncWrite};
18
+ use tokio::time::Sleep;
19
+ use x509_parser::parse_x509_certificate;
20
+
21
+ use crate::acceptor::AcmeAcceptor;
22
+ use crate::acme::{
23
+ Account, AcmeError, Auth, AuthStatus, Directory, Identifier, Order, OrderStatus,
24
+ };
25
+ use crate::{AcmeConfig, Incoming, ResolvesServerCertAcme};
26
+
27
+ type Timer = std::pin::Pin<Box<Sleep>>;
28
+ type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
29
+
30
+ pub fn after(d: std::time::Duration) -> Timer {
31
+ Box::pin(tokio::time::sleep(d))
32
+ }
33
+
34
+ #[allow(clippy::type_complexity)]
35
+ pub struct AcmeState<EC: Debug = Infallible, EA: Debug = EC> {
36
+ config: Arc<AcmeConfig<EC, EA>>,
37
+ resolver: Arc<ResolvesServerCertAcme>,
38
+ account_key: Option<Vec<u8>>,
39
+
40
+ early_action: Option<BoxFuture<Event<EC, EA>>>,
41
+ load_cert: Option<BoxFuture<Result<Option<Vec<u8>>, EC>>>,
42
+ load_account: Option<BoxFuture<Result<Option<Vec<u8>>, EA>>>,
43
+ order: Option<BoxFuture<Result<Vec<u8>, OrderError>>>,
44
+ backoff_cnt: usize,
45
+ wait: Option<Timer>,
46
+ }
47
+
48
+ pub type Event<EC, EA> = Result<EventOk, EventError<EC, EA>>;
49
+
50
+ #[derive(Debug)]
51
+ pub enum EventOk {
52
+ DeployedCachedCert,
53
+ DeployedNewCert,
54
+ CertCacheStore,
55
+ AccountCacheStore,
56
+ }
57
+
58
+ #[derive(Error, Debug)]
59
+ pub enum EventError<EC: Debug, EA: Debug> {
60
+ #[error("cert cache load: {0}")]
61
+ CertCacheLoad(EC),
62
+ #[error("account cache load: {0}")]
63
+ AccountCacheLoad(EA),
64
+ #[error("cert cache store: {0}")]
65
+ CertCacheStore(EC),
66
+ #[error("account cache store: {0}")]
67
+ AccountCacheStore(EA),
68
+ #[error("cached cert parse: {0}")]
69
+ CachedCertParse(CertParseError),
70
+ #[error("order: {0}")]
71
+ Order(OrderError),
72
+ #[error("new cert parse: {0}")]
73
+ NewCertParse(CertParseError),
74
+ }
75
+
76
+ #[derive(Error, Debug)]
77
+ pub enum OrderError {
78
+ #[error("acme error: {0}")]
79
+ Acme(#[from] AcmeError),
80
+ #[error("certificate generation error: {0}")]
81
+ Rcgen(#[from] RcgenError),
82
+ #[error("bad order object: {0:?}")]
83
+ BadOrder(Order),
84
+ #[error("bad auth object: {0:?}")]
85
+ BadAuth(Auth),
86
+ #[error("authorization for {0} failed too many times")]
87
+ TooManyAttemptsAuth(String),
88
+ #[error("order status stayed on processing too long")]
89
+ ProcessingTimeout(Order),
90
+ }
91
+
92
+ #[derive(Error, Debug)]
93
+ pub enum CertParseError {
94
+ #[error("X509 parsing error: {0}")]
95
+ X509(#[from] x509_parser::nom::Err<x509_parser::error::X509Error>),
96
+ #[error("expected 2 or more pem, got: {0}")]
97
+ Pem(#[from] pem::PemError),
98
+ #[error("expected 2 or more pem, got: {0}")]
99
+ TooFewPem(usize),
100
+ #[error("unsupported private key type")]
101
+ InvalidPrivateKey,
102
+ }
103
+
104
+ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
105
+ pub fn incoming<
106
+ TCP: AsyncRead + AsyncWrite + Unpin,
107
+ ETCP,
108
+ ITCP: Stream<Item = Result<TCP, ETCP>> + Unpin,
109
+ >(
110
+ self,
111
+ tcp_incoming: ITCP,
112
+ alpn_protocols: Vec<Vec<u8>>,
113
+ ) -> Incoming<TCP, ETCP, ITCP, EC, EA> {
114
+ let acceptor = self.acceptor();
115
+ Incoming::new(tcp_incoming, self, acceptor, alpn_protocols)
116
+ }
117
+ pub fn acceptor(&self) -> AcmeAcceptor {
118
+ AcmeAcceptor::new(self.resolver())
119
+ }
120
+
121
+ #[cfg(feature = "axum")]
122
+ pub fn axum_acceptor(
123
+ &self,
124
+ rustls_config: Arc<rustls::ServerConfig>,
125
+ ) -> crate::axum::AxumAcceptor {
126
+ crate::axum::AxumAcceptor::new(self.acceptor(), rustls_config)
127
+ }
128
+ pub fn resolver(&self) -> Arc<ResolvesServerCertAcme> {
129
+ self.resolver.clone()
130
+ }
131
+ pub fn new(config: AcmeConfig<EC, EA>) -> Self {
132
+ let config = Arc::new(config);
133
+ Self {
134
+ config: config.clone(),
135
+ resolver: ResolvesServerCertAcme::new(),
136
+ account_key: None,
137
+ early_action: None,
138
+ load_cert: Some(Box::pin({
139
+ let config = config.clone();
140
+ async move {
141
+ config
142
+ .cache
143
+ .load_cert(&config.domains, &config.directory_url)
144
+ .await
145
+ }
146
+ })),
147
+ load_account: Some(Box::pin({
148
+ let config = config;
149
+ async move {
150
+ config
151
+ .cache
152
+ .load_account(&config.contact, &config.directory_url)
153
+ .await
154
+ }
155
+ })),
156
+ order: None,
157
+ backoff_cnt: 0,
158
+ wait: None,
159
+ }
160
+ }
161
+ fn parse_cert(pem: &[u8]) -> Result<(CertifiedKey, [DateTime<Utc>; 2]), CertParseError> {
162
+ let mut pems = pem::parse_many(pem)?;
163
+ if pems.len() < 2 {
164
+ return Err(CertParseError::TooFewPem(pems.len()));
165
+ }
166
+ let pk_bytes = pems.remove(0).into_contents();
167
+ let pk_der: PrivatePkcs8KeyDer = pk_bytes.into();
168
+ let pk: PrivateKeyDer = pk_der.into();
169
+ let pk = match any_ecdsa_type(&pk) {
170
+ Ok(pk) => pk,
171
+ Err(_) => return Err(CertParseError::InvalidPrivateKey),
172
+ };
173
+ let cert_chain: Vec<RustlsCertificate> =
174
+ pems.into_iter().map(|p| p.into_contents().into()).collect();
175
+ let validity = match parse_x509_certificate(cert_chain[0].as_ref()) {
176
+ Ok((_, cert)) => {
177
+ let validity = cert.validity();
178
+ [validity.not_before, validity.not_after]
179
+ .map(|t| Utc.timestamp_opt(t.timestamp(), 0).earliest().unwrap())
180
+ }
181
+ Err(err) => return Err(CertParseError::X509(err)),
182
+ };
183
+ let cert = CertifiedKey::new(cert_chain, pk);
184
+ Ok((cert, validity))
185
+ }
186
+
187
+ #[allow(clippy::result_large_err)]
188
+ fn process_cert(&mut self, pem: Vec<u8>, cached: bool) -> Event<EC, EA> {
189
+ let (cert, validity) = match (Self::parse_cert(&pem), cached) {
190
+ (Ok(r), _) => r,
191
+ (Err(err), cached) => {
192
+ return match cached {
193
+ true => Err(EventError::CachedCertParse(err)),
194
+ false => Err(EventError::NewCertParse(err)),
195
+ }
196
+ }
197
+ };
198
+ self.resolver.set_cert(Arc::new(cert));
199
+ let wait_duration = (validity[1] - (validity[1] - validity[0]) / 3 - Utc::now())
200
+ .max(chrono::Duration::zero())
201
+ .to_std()
202
+ .unwrap_or_default();
203
+ self.wait = Some(after(wait_duration));
204
+ if cached {
205
+ return Ok(EventOk::DeployedCachedCert);
206
+ }
207
+ let config = self.config.clone();
208
+ self.early_action = Some(Box::pin(async move {
209
+ match config
210
+ .cache
211
+ .store_cert(&config.domains, &config.directory_url, &pem)
212
+ .await
213
+ {
214
+ Ok(()) => Ok(EventOk::CertCacheStore),
215
+ Err(err) => Err(EventError::CertCacheStore(err)),
216
+ }
217
+ }));
218
+ Event::Ok(EventOk::DeployedNewCert)
219
+ }
220
+ async fn order(
221
+ config: Arc<AcmeConfig<EC, EA>>,
222
+ resolver: Arc<ResolvesServerCertAcme>,
223
+ key_pair: Vec<u8>,
224
+ ) -> Result<Vec<u8>, OrderError> {
225
+ let directory = Directory::discover(&config.client_config, &config.directory_url).await?;
226
+ let account = Account::create_with_keypair(
227
+ &config.client_config,
228
+ directory,
229
+ &config.contact,
230
+ &key_pair,
231
+ &config.eab,
232
+ )
233
+ .await?;
234
+
235
+ let mut params = CertificateParams::new(config.domains.clone())?;
236
+ params.distinguished_name = DistinguishedName::new();
237
+ let key_pair = rcgen::KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
238
+
239
+ let (order_url, mut order) = account
240
+ .new_order(&config.client_config, config.domains.clone())
241
+ .await?;
242
+ loop {
243
+ match order.status {
244
+ OrderStatus::Pending => {
245
+ let auth_futures = order
246
+ .authorizations
247
+ .iter()
248
+ .map(|url| Self::authorize(&config, &resolver, &account, url));
249
+ try_join_all(auth_futures).await?;
250
+ log::info!("completed all authorizations");
251
+ order = account.order(&config.client_config, &order_url).await?;
252
+ }
253
+ OrderStatus::Processing => {
254
+ for i in 0u64..10 {
255
+ log::info!("order processing");
256
+ after(Duration::from_secs(1u64 << i)).await;
257
+ order = account.order(&config.client_config, &order_url).await?;
258
+ if order.status != OrderStatus::Processing {
259
+ break;
260
+ }
261
+ }
262
+ if order.status == OrderStatus::Processing {
263
+ return Err(OrderError::ProcessingTimeout(order));
264
+ }
265
+ }
266
+ OrderStatus::Ready => {
267
+ log::info!("sending csr");
268
+ let csr = params.serialize_request(&key_pair)?;
269
+ order = account
270
+ .finalize(&config.client_config, order.finalize, csr.der().to_vec())
271
+ .await?
272
+ }
273
+ OrderStatus::Valid { certificate } => {
274
+ log::info!("download certificate");
275
+ let pem = [
276
+ &key_pair.serialize_pem(),
277
+ "\n",
278
+ &account
279
+ .certificate(&config.client_config, certificate)
280
+ .await?,
281
+ ]
282
+ .concat();
283
+ return Ok(pem.into_bytes());
284
+ }
285
+ OrderStatus::Invalid => return Err(OrderError::BadOrder(order)),
286
+ }
287
+ }
288
+ }
289
+ async fn authorize(
290
+ config: &AcmeConfig<EC, EA>,
291
+ resolver: &ResolvesServerCertAcme,
292
+ account: &Account,
293
+ url: &String,
294
+ ) -> Result<(), OrderError> {
295
+ let auth = account.auth(&config.client_config, url).await?;
296
+ let (domain, challenge_url) = match auth.status {
297
+ AuthStatus::Pending => {
298
+ let Identifier::Dns(domain) = auth.identifier;
299
+ log::info!("trigger challenge for {}", &domain);
300
+ let (challenge, auth_key) =
301
+ account.tls_alpn_01(&auth.challenges, domain.clone())?;
302
+ resolver.set_auth_key(domain.clone(), Arc::new(auth_key));
303
+ account
304
+ .challenge(&config.client_config, &challenge.url)
305
+ .await?;
306
+ (domain, challenge.url.clone())
307
+ }
308
+ AuthStatus::Valid => return Ok(()),
309
+ _ => return Err(OrderError::BadAuth(auth)),
310
+ };
311
+ for i in 0u64..5 {
312
+ after(Duration::from_secs(1u64 << i)).await;
313
+ let auth = account.auth(&config.client_config, url).await?;
314
+ match auth.status {
315
+ AuthStatus::Pending => {
316
+ log::info!("authorization for {} still pending", &domain);
317
+ account
318
+ .challenge(&config.client_config, &challenge_url)
319
+ .await?
320
+ }
321
+ AuthStatus::Valid => return Ok(()),
322
+ _ => return Err(OrderError::BadAuth(auth)),
323
+ }
324
+ }
325
+ Err(OrderError::TooManyAttemptsAuth(domain))
326
+ }
327
+ fn poll_next_infinite(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Event<EC, EA>> {
328
+ loop {
329
+ // queued early action
330
+ if let Some(early_action) = &mut self.early_action {
331
+ let result = ready!(early_action.poll_unpin(cx));
332
+ self.early_action.take();
333
+ return Poll::Ready(result);
334
+ }
335
+
336
+ // sleep
337
+ if let Some(timer) = &mut self.wait {
338
+ ready!(timer.poll_unpin(cx));
339
+ self.wait.take();
340
+ }
341
+
342
+ // load from cert cache
343
+ if let Some(load_cert) = &mut self.load_cert {
344
+ let result = ready!(load_cert.poll_unpin(cx));
345
+ self.load_cert.take();
346
+ match result {
347
+ Ok(Some(pem)) => {
348
+ return Poll::Ready(Self::process_cert(self.get_mut(), pem, true));
349
+ }
350
+ Ok(None) => {}
351
+ Err(err) => return Poll::Ready(Err(EventError::CertCacheLoad(err))),
352
+ }
353
+ }
354
+
355
+ // load from account cache
356
+ if let Some(load_account) = &mut self.load_account {
357
+ let result = ready!(load_account.poll_unpin(cx));
358
+ self.load_account.take();
359
+ match result {
360
+ Ok(Some(key_pair)) => self.account_key = Some(key_pair),
361
+ Ok(None) => {}
362
+ Err(err) => return Poll::Ready(Err(EventError::AccountCacheLoad(err))),
363
+ }
364
+ }
365
+
366
+ // execute order
367
+ if let Some(order) = &mut self.order {
368
+ let result = ready!(order.poll_unpin(cx));
369
+ self.order.take();
370
+ match result {
371
+ Ok(pem) => {
372
+ self.backoff_cnt = 0;
373
+ return Poll::Ready(Self::process_cert(self.get_mut(), pem, false));
374
+ }
375
+ Err(err) => {
376
+ // TODO: replace key on some errors or high backoff_cnt?
377
+ self.wait = Some(after(Duration::from_secs(1 << self.backoff_cnt)));
378
+ self.backoff_cnt = (self.backoff_cnt + 1).min(16);
379
+ return Poll::Ready(Err(EventError::Order(err)));
380
+ }
381
+ }
382
+ }
383
+
384
+ // schedule order
385
+ let account_key = match &self.account_key {
386
+ None => {
387
+ let account_key = Account::generate_key_pair();
388
+ self.account_key = Some(account_key.clone());
389
+ let config = self.config.clone();
390
+ let account_key_clone = account_key.clone();
391
+ self.early_action = Some(Box::pin(async move {
392
+ match config
393
+ .cache
394
+ .store_account(
395
+ &config.contact,
396
+ &config.directory_url,
397
+ &account_key_clone,
398
+ )
399
+ .await
400
+ {
401
+ Ok(()) => Ok(EventOk::AccountCacheStore),
402
+ Err(err) => Err(EventError::AccountCacheStore(err)),
403
+ }
404
+ }));
405
+ account_key
406
+ }
407
+ Some(account_key) => account_key.clone(),
408
+ };
409
+ let config = self.config.clone();
410
+ let resolver = self.resolver.clone();
411
+ self.order = Some(Box::pin({
412
+ Self::order(config.clone(), resolver.clone(), account_key)
413
+ }));
414
+ }
415
+ }
416
+ }
417
+
418
+ impl<EC: 'static + Debug, EA: 'static + Debug> Stream for AcmeState<EC, EA> {
419
+ type Item = Event<EC, EA>;
420
+
421
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
422
+ Poll::Ready(Some(ready!(self.poll_next_infinite(cx))))
423
+ }
424
+ }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "itsi-server"
3
- version = "0.1.19"
3
+ version = "0.1.20"
4
4
  edition = "2021"
5
5
  authors = ["Wouter Coppieters <wc@pico.net.nz>"]
6
6
  license = "MIT"
@@ -40,6 +40,7 @@ hyper-util = { version = "0.1.10", features = ["full"] }
40
40
  itsi_error = { path = "../itsi_error" }
41
41
  itsi_rb_helpers = { path = "../itsi_rb_helpers" }
42
42
  itsi_tracing = { path = "../itsi_tracing" }
43
+ itsi_acme = { path = "../itsi_acme" }
43
44
  jsonwebtoken = "9.3.1"
44
45
  magnus = { version = "0.7.1", features = ["bytes", "rb-sys"] }
45
46
  moka = { version = "0.12.10", features = ["sync"] }
@@ -78,9 +79,8 @@ sysinfo = "0.33.1"
78
79
  tempfile = "3.18.0"
79
80
  tokio = { version = "1.44.1", features = ["full"] }
80
81
  tokio-rustls = "0.26.2"
81
- tokio-rustls-acme = "0.6.0"
82
82
  tokio-stream = "0.1.17"
83
- tokio-util = "0.7.13"
83
+ tokio-util = { version = "0.7.14", features = ["compat"] }
84
84
  tracing = "0.1.41"
85
85
  url = "2.5.4"
86
86
  md5 = "0.7.0"
@@ -242,12 +242,12 @@ impl ItsiHttpRequest {
242
242
  .parts
243
243
  .uri
244
244
  .path()
245
- .strip_prefix(&self.script_name)
245
+ .strip_prefix(self.script_name()?)
246
246
  .unwrap_or(self.parts.uri.path()))
247
247
  }
248
248
 
249
249
  pub(crate) fn script_name(&self) -> MagnusResult<&str> {
250
- Ok(&self.script_name)
250
+ Ok(self.script_name.trim_end_matches("/"))
251
251
  }
252
252
 
253
253
  pub(crate) fn query_string(&self) -> MagnusResult<&str> {