-
-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathprovision.rs
108 lines (89 loc) · 3.23 KB
/
provision.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use std::io;
use std::time::Duration;
use clap::Parser;
use tokio::time::sleep;
use tracing::info;
use instant_acme::{
Account, AuthorizationStatus, ChallengeType, Identifier, LetsEncrypt, NewAccount, NewOrder,
OrderStatus,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let opts = Options::parse();
// Create a new account. This will generate a fresh ECDSA key for you.
// Alternatively, restore an account from serialized credentials by
// using `Account::from_credentials()`.
let (account, credentials) = Account::create(
&NewAccount {
contact: &[],
terms_of_service_agreed: true,
only_return_existing: false,
},
LetsEncrypt::Staging.url(),
None,
)
.await?;
info!(
"account credentials:\n\n{}",
serde_json::to_string_pretty(&credentials)?
);
// Create the ACME order based on the given domain names.
// Note that this only needs an `&Account`, so the library will let you
// process multiple orders in parallel for a single account.
let identifiers = opts
.names
.iter()
.map(|ident| Identifier::Dns(ident.clone()))
.collect::<Vec<_>>();
let mut order = account
.new_order(&NewOrder::new(identifiers.as_slice()))
.await?;
let state = order.state();
info!("order state: {:#?}", state);
assert!(matches!(state.status, OrderStatus::Pending));
// Pick the desired challenge type and prepare the response.
let mut authorizations = order.authorizations();
while let Some(result) = authorizations.next().await {
let mut authz = result?;
match authz.status {
AuthorizationStatus::Pending => {}
AuthorizationStatus::Valid => continue,
_ => todo!(),
}
// We'll use the DNS challenges for this example, but you could
// pick something else to use here.
let mut challenge = authz
.challenge(ChallengeType::Dns01)
.ok_or_else(|| anyhow::anyhow!("no dns01 challenge found"))?;
println!("Please set the following DNS record then press the Return key:");
println!(
"_acme-challenge.{} IN TXT {}",
challenge.identifier(),
challenge.key_authorization().dns_value()
);
io::stdin().read_line(&mut String::new())?;
challenge.set_ready().await?;
}
// Exponentially back off until the order becomes ready or invalid.
let status = order.poll(5, Duration::from_millis(250)).await?;
if status != OrderStatus::Ready {
return Err(anyhow::anyhow!("unexpected order status: {status:?}"));
}
// Finalize the order and print certificate chain, private key and account credentials.
let private_key_pem = order.finalize().await?;
let cert_chain_pem = loop {
match order.certificate().await? {
Some(cert_chain_pem) => break cert_chain_pem,
None => sleep(Duration::from_secs(1)).await,
}
};
info!("certificate chain:\n\n{cert_chain_pem}");
info!("private key:\n\n{private_key_pem}");
Ok(())
}
#[derive(Parser)]
struct Options {
#[clap(long)]
names: Vec<String>,
}