Publish revoked keys
This commit is contained in:
parent
ad6eed4f02
commit
372a49262d
2 changed files with 76 additions and 0 deletions
17
src/db.rs
17
src/db.rs
|
@ -23,6 +23,12 @@ pub const INSERT_KEY: &str = "INSERT INTO key_db (
|
|||
$7,
|
||||
$8
|
||||
)";
|
||||
pub const SELECT_EXPIRED_KEYS: &str = "SELECT selector, sdid, algorithm, private_key
|
||||
FROM key_db
|
||||
WHERE
|
||||
revocation <= unixepoch()
|
||||
AND published IS FALSE
|
||||
ORDER BY revocation";
|
||||
pub const SELECT_LATEST_KEY: &str = "SELECT not_after
|
||||
FROM key_db
|
||||
WHERE
|
||||
|
@ -31,6 +37,17 @@ WHERE
|
|||
AND published IS FALSE
|
||||
ORDER BY not_after DESC
|
||||
LIMIT 1";
|
||||
pub const SELECT_NEAREST_KEY_PUBLICATION: &str = "SELECT revocation
|
||||
FROM key_db
|
||||
WHERE published IS FALSE
|
||||
ORDER BY revocation
|
||||
LIMIT 1";
|
||||
pub const UPDATE_PUBLISHED_KEY: &str = "UPDATE key_db
|
||||
SET published = TRUE
|
||||
WHERE
|
||||
selector = $1
|
||||
AND sdid = $2
|
||||
AND algorithm = $3";
|
||||
|
||||
pub async fn init(cnf: &Config) -> Result<SqlitePool, String> {
|
||||
do_init(cnf).await.map_err(|e| e.to_string())
|
||||
|
|
59
src/key.rs
59
src/key.rs
|
@ -2,6 +2,9 @@ use crate::config::Config;
|
|||
use crate::Algorithm;
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use sqlx::SqlitePool;
|
||||
use std::path::Path;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io::{AsyncWriteExt, BufWriter};
|
||||
use tokio::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -16,11 +19,67 @@ pub async fn key_rotation(db: &SqlitePool, cnf: &Config) -> Duration {
|
|||
durations.push(d);
|
||||
}
|
||||
}
|
||||
if let Some(path) = cnf.revocation_list() {
|
||||
match publish_expired_keys(db, path).await {
|
||||
Ok(d) => durations.push(d),
|
||||
Err(err) => log::error!("{err}"),
|
||||
};
|
||||
}
|
||||
durations.push(Duration::from_secs(crate::KEY_CHECK_MIN_DELAY));
|
||||
durations.sort();
|
||||
durations[durations.len() - 1]
|
||||
}
|
||||
|
||||
async fn publish_expired_keys(db: &SqlitePool, file_path: &Path) -> Result<Duration, String> {
|
||||
let res: Vec<(String, String, String, String)> = sqlx::query_as(crate::db::SELECT_EXPIRED_KEYS)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
if !res.is_empty() {
|
||||
let rev_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(file_path)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut buff = BufWriter::new(rev_file);
|
||||
for (selector, sdid, algorithm, private_key) in res {
|
||||
buff.write_all(algorithm.as_bytes()).await.unwrap();
|
||||
buff.write_all(b" ").await.unwrap();
|
||||
buff.write_all(private_key.as_bytes()).await.unwrap();
|
||||
buff.write_all(b" ").await.unwrap();
|
||||
buff.write_all(selector.as_bytes()).await.unwrap();
|
||||
buff.write_all(b"._domainkey.").await.unwrap();
|
||||
buff.write_all(sdid.as_bytes()).await.unwrap();
|
||||
buff.write_all(b"\n").await.unwrap();
|
||||
buff.flush().await.unwrap();
|
||||
sqlx::query(crate::db::UPDATE_PUBLISHED_KEY)
|
||||
.bind(&selector)
|
||||
.bind(&sdid)
|
||||
.bind(&algorithm)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
log::info!(
|
||||
"{algorithm} private key for {selector}._domainkey.{sdid} has been published"
|
||||
);
|
||||
}
|
||||
}
|
||||
let res: Option<(i64,)> = sqlx::query_as(crate::db::SELECT_NEAREST_KEY_PUBLICATION)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
match res {
|
||||
Some((next_pub_ts,)) => {
|
||||
let now_ts = OffsetDateTime::now_utc().unix_timestamp();
|
||||
let delta = 0.max(next_pub_ts - now_ts);
|
||||
Ok(Duration::from_secs(delta as u64))
|
||||
}
|
||||
None => Ok(Duration::from_secs(crate::KEY_CHECK_MIN_DELAY)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn renew_key_if_expired(
|
||||
db: &SqlitePool,
|
||||
cnf: &Config,
|
||||
|
|
Loading…
Reference in a new issue