Skip to content

Commit 698255e

Browse files
committed
Fix HTTPS setup #786
Try order state: valid #786 Log states #786 log challenges #786 #768 refresh! #768 loop certs Cleanup, move 1 sec delay to loop #768
1 parent 8797baf commit 698255e

File tree

2 files changed

+60
-41
lines changed

2 files changed

+60
-41
lines changed

server/src/appstate.rs

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ pub struct AppState {
3131
pub fn init(config: Config) -> AtomicServerResult<AppState> {
3232
tracing::info!("Initializing AppState");
3333

34+
// We warn over here because tracing needs to be initialized first.
35+
if config.opts.slow_mode {
36+
tracing::warn!("Slow mode is enabled. This will introduce random delays in the server, to simulate a slow connection.");
37+
}
38+
if config.opts.development {
39+
tracing::warn!("Development mode is enabled. This will use staging environments for services like LetsEncrypt.");
40+
}
41+
3442
// Check if atomic-server is already running somewhere, and try to stop it. It's not a problem if things go wrong here, so errors are simply logged.
3543
if cfg!(feature = "process-management") {
3644
#[cfg(feature = "process-management")]

server/src/https.rs

+52-41
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
//! checks if certificates are not outdated,
44
//! persists files on disk.
55
6+
use crate::errors::AtomicServerResult;
7+
use actix_web::{dev::ServerHandle, App, HttpServer};
68
use std::{
79
fs::{self, File},
810
io::BufReader,
911
path::PathBuf,
1012
};
13+
use tracing::{info, warn};
1114

12-
use crate::errors::AtomicServerResult;
1315
/// Create RUSTLS server config from certificates in config dir
1416
pub fn get_https_config(
1517
config: &crate::config::Config,
@@ -18,7 +20,6 @@ pub fn get_https_config(
1820
let https_config = rustls::ServerConfig::builder()
1921
.with_safe_defaults()
2022
.with_no_client_auth();
21-
// rustls::NoClientAuth::new()
2223
let cert_file =
2324
&mut BufReader::new(File::open(config.cert_path.clone()).expect("No HTTPS TLS key found."));
2425
let key_file =
@@ -65,7 +66,7 @@ fn set_certs_created_at_file(config: &crate::config::Config) {
6566
/// Will be true if there are no certs yet.
6667
pub fn should_renew_certs_check(config: &crate::config::Config) -> AtomicServerResult<bool> {
6768
if std::fs::File::open(&config.cert_path).is_err() {
68-
warn!(
69+
info!(
6970
"No HTTPS certificates found in {:?}, requesting new ones...",
7071
&config.https_path
7172
);
@@ -87,17 +88,12 @@ pub fn should_renew_certs_check(config: &crate::config::Config) -> AtomicServerR
8788
Ok(expired)
8889
}
8990

90-
use actix_web::{dev::ServerHandle, App, HttpServer};
91-
use instant_acme::{KeyAuthorization, OrderStatus};
92-
use tracing::{info, log::warn};
93-
94-
use std::sync::mpsc;
95-
96-
/// Starts an HTTP Actix server for HTTPS certificate initialization
91+
/// Starts an HTTP Actix server for HTTPS certificate initialization.
92+
/// Hosts `.well-known/acme-challenge` folder and the challenge file.
9793
async fn cert_init_server(
9894
config: &crate::config::Config,
9995
challenge: &instant_acme::Challenge,
100-
key_auth: &KeyAuthorization,
96+
key_auth: &instant_acme::KeyAuthorization,
10197
) -> AtomicServerResult<ServerHandle> {
10298
let address = format!("{}:{}", config.opts.ip, config.opts.port);
10399
warn!("Server temporarily running in HTTP mode at {}, running Let's Encrypt Certificate initialization...", address);
@@ -117,10 +113,10 @@ async fn cert_init_server(
117113
challenge_path.push("acme-challenge");
118114
fs::create_dir_all(&challenge_path)?;
119115
challenge_path.push(&challenge.token);
120-
// let challenge_file_content = format!("{}.{}", challenge.token, key_auth.as_str());
121116
fs::write(challenge_path, key_auth.as_str())?;
122117

123-
let (tx, rx) = mpsc::channel();
118+
// Channel is used to send the server handle back to the main thread, so we can stop it later
119+
let (tx, rx) = std::sync::mpsc::channel();
124120

125121
std::thread::spawn(move || {
126122
actix_web::rt::System::new().block_on(async move {
@@ -153,20 +149,20 @@ async fn cert_init_server(
153149
&config.opts.domain, &challenge.token
154150
);
155151

156-
// wait for a few secs
157152
std::thread::sleep(std::time::Duration::from_secs(2));
158153
info!("Testing availability of {}", &well_known_url);
159154

160155
let agent = ureq::builder()
161156
.timeout(std::time::Duration::from_secs(2))
162157
.build();
163-
let resp = agent
164-
.get(&well_known_url)
165-
// .get("https://docs.certifytheweb.com/docs/http-validation/")
166-
.call()
167-
.map_err(|e| format!("Unable to Test local server. {}", e))?;
158+
let resp = agent.get(&well_known_url).call().map_err(|e| {
159+
format!(
160+
"Unable to test local server. Is it available at the right address? {}",
161+
e
162+
)
163+
})?;
168164
if resp.status() != 200 {
169-
warn!("Unable to Test local server. Status: {}", resp.status());
165+
warn!("Unable to test local server. Status: {}", resp.status());
170166
} else {
171167
info!("Server for HTTP initialization running correctly");
172168
}
@@ -175,6 +171,8 @@ async fn cert_init_server(
175171

176172
/// Sends a request to LetsEncrypt to create a certificate
177173
pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<()> {
174+
use instant_acme::OrderStatus;
175+
178176
let challenge_type = if config.opts.https_dns {
179177
info!("Using DNS-01 challenge");
180178
instant_acme::ChallengeType::Dns01
@@ -188,6 +186,9 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
188186
// using `Account::from_credentials()`.
189187

190188
let lets_encrypt_url = if config.opts.development {
189+
warn!(
190+
"Using LetsEncrypt staging server, not production. This is for testing purposes only and will not provide a working certificate."
191+
);
191192
instant_acme::LetsEncrypt::Staging.url()
192193
} else {
193194
instant_acme::LetsEncrypt::Production.url()
@@ -253,7 +254,7 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
253254
.challenges
254255
.iter()
255256
.find(|c| c.r#type == challenge_type)
256-
.ok_or("no Dns01 challenge found")?;
257+
.ok_or(format!("no {:?} challenge found", challenge_type))?;
257258

258259
let instant_acme::Identifier::Dns(identifier) = &authz.identifier;
259260

@@ -278,7 +279,8 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
278279
}
279280

280281
// Let the server know we're ready to accept the challenges.
281-
for (_, url) in &challenges {
282+
for (a, url) in &challenges {
283+
info!("Setting challenge ready for {} at {}", a, url);
282284
order.set_challenge_ready(url).await.unwrap();
283285
}
284286

@@ -287,17 +289,16 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
287289
let mut delay = std::time::Duration::from_millis(250);
288290
let url = authorizations.get(0).expect("Authorizations is empty");
289291
let state = loop {
290-
actix::clock::sleep(delay).await;
291292
let state = order.state();
292-
if let instant_acme::OrderStatus::Ready | instant_acme::OrderStatus::Invalid = state.status
293-
{
294-
info!("order state: {:#?}", state);
293+
info!("Order state: {:#?}", state);
294+
if let OrderStatus::Ready | OrderStatus::Invalid | OrderStatus::Valid = state.status {
295295
break state;
296296
}
297+
order.refresh().await.unwrap();
297298

298299
delay *= 2;
299300
tries += 1;
300-
match tries < 8 {
301+
match tries < 10 {
301302
true => info!("order is not ready, waiting {delay:?}"),
302303
false => {
303304
return Err(format!(
@@ -306,6 +307,7 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
306307
.into());
307308
}
308309
}
310+
actix::clock::sleep(delay).await;
309311
};
310312

311313
if state.status == OrderStatus::Invalid {
@@ -319,26 +321,35 @@ pub async fn request_cert(config: &crate::config::Config) -> AtomicServerResult<
319321

320322
// If the order is ready, we can provision the certificate.
321323
// Use the rcgen library to create a Certificate Signing Request.
322-
323-
let mut params = rcgen::CertificateParams::new(names.clone());
324+
let mut params = rcgen::CertificateParams::new(names);
324325
params.distinguished_name = rcgen::DistinguishedName::new();
325326
let cert = rcgen::Certificate::from_params(params).map_err(|e| e.to_string())?;
326327
let csr = cert.serialize_request_der().map_err(|e| e.to_string())?;
327328

328329
// Finalize the order and print certificate chain, private key and account credentials.
329-
330330
order.finalize(&csr).await.map_err(|e| e.to_string())?;
331-
let cert_chain_pem = order
332-
.certificate()
333-
.await
334-
.map_err(|e| format!("Error getting certificate {}", e))?
335-
.expect("No cert found");
336-
info!("certficate chain:\n\n{}", cert_chain_pem);
337-
info!("private key:\n\n{}", cert.serialize_private_key_pem());
338-
// info!(
339-
// "account credentials:\n\n{}",
340-
// serde_json::to_string_pretty(&account.credentials()).map_err(|e| e.to_string())?
341-
// );
331+
332+
let mut tries = 1u8;
333+
334+
let cert_chain_pem = loop {
335+
match order.certificate().await {
336+
Ok(Some(cert_chain_pem)) => {
337+
info!("Certificate ready!");
338+
break cert_chain_pem;
339+
}
340+
Ok(None) => {
341+
if tries > 10 {
342+
return Err("Giving up: certificate is still not ready".into());
343+
}
344+
tries += 1;
345+
info!("Certificate not ready yet...");
346+
std::thread::sleep(std::time::Duration::from_secs(1));
347+
continue;
348+
}
349+
Err(e) => return Err(format!("Error getting certificate {}", e).into()),
350+
}
351+
};
352+
342353
write_certs(config, cert_chain_pem, cert)?;
343354

344355
if let Some(hnd) = handle {

0 commit comments

Comments
 (0)