v0.25.5
This commit is contained in:
1 parent
1dea7f55c8
commit
c819f0a335
151 files changed
+7610
-4264
No files matched your search
+12
-3
@@ -72,8 +72,17 @@ impl Manager {
|
||||
id: format!("{:032x}", rand::random::<u128>()),
|
||||
certificate_id: cert.id.clone(),
|
||||
status: "running",
|
||||
kind: if dry_run { "renewal-dry-run" } else { "renewal" },
|
||||
log_directory: certbot_log_directory(&directory, &issuer.provider, &issuer.environment, &cert.id),
|
||||
kind: if dry_run {
|
||||
"renewal-dry-run"
|
||||
} else {
|
||||
"renewal"
|
||||
},
|
||||
log_directory: certbot_log_directory(
|
||||
&directory,
|
||||
&issuer.provider,
|
||||
&issuer.environment,
|
||||
&cert.id,
|
||||
),
|
||||
provider: issuer.provider.clone(),
|
||||
domains,
|
||||
environment: issuer.environment.clone(),
|
||||
@@ -109,7 +118,7 @@ impl Manager {
|
||||
.await
|
||||
.map_err(|_| ApiError(StatusCode::CONFLICT, "acme.secret_missing".into()))?;
|
||||
request["credential_ini_b64"] = Value::String(
|
||||
base64::engine::general_purpose::STANDARD.encode(credential.as_slice())
|
||||
base64::engine::general_purpose::STANDARD.encode(credential.as_slice()),
|
||||
);
|
||||
}
|
||||
let raw = Zeroizing::new(request.to_string());
|
||||
|
||||
+124
-29
@@ -29,8 +29,17 @@ pub struct Job {
|
||||
pub certificate: Option<Certificate>,
|
||||
}
|
||||
|
||||
pub(crate) fn certbot_log_directory(directory: &std::path::Path, provider: &str, environment: &str, certificate_id: &str) -> String {
|
||||
let suffix = if provider == "rfc2136" { String::new() } else { format!("-{provider}") };
|
||||
pub(crate) fn certbot_log_directory(
|
||||
directory: &std::path::Path,
|
||||
provider: &str,
|
||||
environment: &str,
|
||||
certificate_id: &str,
|
||||
) -> String {
|
||||
let suffix = if provider == "rfc2136" {
|
||||
String::new()
|
||||
} else {
|
||||
format!("-{provider}")
|
||||
};
|
||||
directory
|
||||
.join("acme")
|
||||
.join(format!("{environment}-{certificate_id}{suffix}"))
|
||||
@@ -38,26 +47,57 @@ pub(crate) fn certbot_log_directory(directory: &std::path::Path, provider: &str,
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
}
|
||||
fn provider_credential_bytes(request: &Value, provider: &str) -> Result<Zeroizing<Vec<u8>>, ApiError> {
|
||||
fn provider_credential_bytes(
|
||||
request: &Value,
|
||||
provider: &str,
|
||||
) -> Result<Zeroizing<Vec<u8>>, ApiError> {
|
||||
fn credential<'a>(request: &'a Value, name: &str) -> Result<&'a str, ApiError> {
|
||||
let invalid = || ApiError(StatusCode::BAD_REQUEST, "acme.invalid_api_credential".into());
|
||||
let value = request.get(name).and_then(Value::as_str).ok_or_else(invalid)?;
|
||||
let invalid = || {
|
||||
ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_api_credential".into(),
|
||||
)
|
||||
};
|
||||
let value = request
|
||||
.get(name)
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(invalid)?;
|
||||
let valid = (8..=4096).contains(&value.len())
|
||||
&& value.bytes().all(|byte| {
|
||||
byte.is_ascii_alphanumeric()
|
||||
|| matches!(byte, b'_' | b'.' | b'/' | b'+' | b'-' | b'=')
|
||||
});
|
||||
if valid { Ok(value) } else { Err(invalid()) }
|
||||
if valid {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(invalid())
|
||||
}
|
||||
}
|
||||
let bytes = match provider {
|
||||
"cloudflare" => format!("dns_cloudflare_api_token = {}\n", credential(request, "api_token")?).into_bytes(),
|
||||
"cloudflare" => format!(
|
||||
"dns_cloudflare_api_token = {}\n",
|
||||
credential(request, "api_token")?
|
||||
)
|
||||
.into_bytes(),
|
||||
"ovh" => {
|
||||
if request.get("endpoint").is_some_and(|value| !value.is_string()) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_ovh_endpoint".into()));
|
||||
if request
|
||||
.get("endpoint")
|
||||
.is_some_and(|value| !value.is_string())
|
||||
{
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_ovh_endpoint".into(),
|
||||
));
|
||||
}
|
||||
let endpoint = request.get("endpoint").and_then(Value::as_str).unwrap_or("ovh-eu");
|
||||
let endpoint = request
|
||||
.get("endpoint")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("ovh-eu");
|
||||
if !matches!(endpoint, "ovh-eu" | "ovh-ca") {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_ovh_endpoint".into()));
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_ovh_endpoint".into(),
|
||||
));
|
||||
}
|
||||
format!(
|
||||
"dns_ovh_endpoint = {}\ndns_ovh_application_key = {}\ndns_ovh_application_secret = {}\ndns_ovh_consumer_key = {}\n",
|
||||
@@ -73,15 +113,32 @@ fn provider_credential_bytes(request: &Value, provider: &str) -> Result<Zeroizin
|
||||
.get("dns_server")
|
||||
.and_then(Value::as_str)
|
||||
.and_then(|value| value.parse::<std::net::IpAddr>().ok())
|
||||
.ok_or_else(|| ApiError(StatusCode::BAD_REQUEST, "acme.invalid_dns_server".into()))?;
|
||||
if request.get("dns_port").is_some_and(|value| value.as_u64().is_none()) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_limits".into()));
|
||||
.ok_or_else(|| {
|
||||
ApiError(StatusCode::BAD_REQUEST, "acme.invalid_dns_server".into())
|
||||
})?;
|
||||
if request
|
||||
.get("dns_port")
|
||||
.is_some_and(|value| value.as_u64().is_none())
|
||||
{
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_limits".into(),
|
||||
));
|
||||
}
|
||||
let dns_port = request.get("dns_port").and_then(Value::as_u64).unwrap_or(53);
|
||||
let dns_port = request
|
||||
.get("dns_port")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or(53);
|
||||
if !(1..=65535).contains(&dns_port) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_limits".into()));
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_limits".into(),
|
||||
));
|
||||
}
|
||||
let raw_name = request.get("key_name").and_then(Value::as_str).unwrap_or("");
|
||||
let raw_name = request
|
||||
.get("key_name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("");
|
||||
let key_name = raw_name.to_ascii_lowercase();
|
||||
let key_name = key_name.trim_end_matches('.');
|
||||
let valid_name = !key_name.is_empty()
|
||||
@@ -95,43 +152,81 @@ fn provider_credential_bytes(request: &Value, provider: &str) -> Result<Zeroizin
|
||||
})
|
||||
});
|
||||
if !valid_name {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_domain".into()));
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_domain".into(),
|
||||
));
|
||||
}
|
||||
if request.get("key_algorithm").is_some_and(|value| !value.is_string()) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_key_algorithm".into()));
|
||||
if request
|
||||
.get("key_algorithm")
|
||||
.is_some_and(|value| !value.is_string())
|
||||
{
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_key_algorithm".into(),
|
||||
));
|
||||
}
|
||||
let key_algorithm = request
|
||||
.get("key_algorithm")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("HMAC-SHA256");
|
||||
if !matches!(key_algorithm, "HMAC-SHA256" | "HMAC-SHA384" | "HMAC-SHA512") {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_key_algorithm".into()));
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_key_algorithm".into(),
|
||||
));
|
||||
}
|
||||
let key_secret = request.get("key_secret").and_then(Value::as_str).ok_or_else(|| ApiError(StatusCode::BAD_REQUEST, "acme.invalid_api_credential".into()))?;
|
||||
let key_secret = request
|
||||
.get("key_secret")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| {
|
||||
ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_api_credential".into(),
|
||||
)
|
||||
})?;
|
||||
let decoded = Zeroizing::new(
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(key_secret)
|
||||
.map_err(|_| ApiError(StatusCode::BAD_REQUEST, "acme.invalid_key_secret".into()))?,
|
||||
.map_err(|_| {
|
||||
ApiError(StatusCode::BAD_REQUEST, "acme.invalid_key_secret".into())
|
||||
})?,
|
||||
);
|
||||
if key_secret.len() > 1024 || !(16..=512).contains(&decoded.len()) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_key_secret".into()));
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_key_secret".into(),
|
||||
));
|
||||
}
|
||||
if request.get("sign_query").is_some_and(|value| !value.is_boolean()) {
|
||||
return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_sign_query".into()));
|
||||
if request
|
||||
.get("sign_query")
|
||||
.is_some_and(|value| !value.is_boolean())
|
||||
{
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_sign_query".into(),
|
||||
));
|
||||
}
|
||||
let sign_query = request.get("sign_query").and_then(Value::as_bool).unwrap_or(false);
|
||||
let sign_query = request
|
||||
.get("sign_query")
|
||||
.and_then(Value::as_bool)
|
||||
.unwrap_or(false);
|
||||
format!(
|
||||
"dns_rfc2136_server = {dns_server}\ndns_rfc2136_port = {dns_port}\ndns_rfc2136_name = {key_name}.\ndns_rfc2136_secret = {key_secret}\ndns_rfc2136_algorithm = {key_algorithm}\ndns_rfc2136_sign_query = {sign_query}\n"
|
||||
)
|
||||
.into_bytes()
|
||||
}
|
||||
"http01" => Vec::new(),
|
||||
_ => return Err(ApiError(StatusCode::BAD_REQUEST, "acme.invalid_provider".into())),
|
||||
_ => {
|
||||
return Err(ApiError(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"acme.invalid_provider".into(),
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(Zeroizing::new(bytes))
|
||||
}
|
||||
|
||||
|
||||
pub struct Manager {
|
||||
slot: Arc<Semaphore>,
|
||||
job: Mutex<Option<Job>>,
|
||||
|
||||
Reference in new issue
Block a user