Skip to content

Commit 4e3e03c

Browse files
committed
feat: add auto-rotation scheduler (cron) for key lifecycle management
Add a background thread that periodically checks which keys are due for rotation and automatically rotates them. The scheduler supports: - Symmetric keys via KMIP ReKey - Asymmetric keys (RSA/EC/PQC) via new CreateKeyPair + cross-links - CoverCrypt keys via ReKeyKeyPair (in-place rotation) - Certificates via Certify (re-issuance) New components: - find_due_for_rotation() in ObjectsStore trait + SQLite/PostgreSQL/MySQL impls - is_due_for_rotation() helper in locate_query.rs - auto_rotate.rs: rotation logic per object type - spawn_auto_rotation_cron() in cron.rs - --auto-rotation-check-interval-secs CLI flag (default: 0 = disabled) The rotation policy (rotate_interval, rotate_name, rotate_offset) is transferred to the new key so the cycle continues. Old keys get rotate_interval=0 and ReplacementObjectLink to the new key.
1 parent 865771d commit 4e3e03c

17 files changed

Lines changed: 914 additions & 2 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Features
2+
3+
- Add background auto-rotation scheduler (cron thread) that periodically checks for keys due for rotation and rotates them automatically
4+
- Add `--auto-rotation-check-interval-secs` server CLI flag (default: 0 = disabled)
5+
- Add `find_due_for_rotation` database method (SQLite, PostgreSQL, MySQL) to find Active objects past their rotation interval
6+
- Implement `auto_rotate_key` logic supporting SymmetricKey (ReKey), PrivateKey/PublicKey (CreateKeyPair or CoverCrypt ReKeyKeyPair), and Certificate (Certify) rotation
7+
- Transfer rotation policy to new keys and set `ReplacementObjectLink`/`ReplacedObjectLink` cross-links on rotation

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crate/interfaces/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ doctest = false
2020
async-trait = { workspace = true }
2121
cosmian_kmip = { path = "../kmip", version = "5.22.0" }
2222
cosmian_logger = { workspace = true }
23-
num-bigint-dig = { workspace = true, features = ["std", "rand", "serde", "zeroize"] }
23+
num-bigint-dig = { workspace = true, features = [
24+
"std",
25+
"rand",
26+
"serde",
27+
"zeroize",
28+
] }
2429
serde_json = { workspace = true }
2530
thiserror = { workspace = true }
31+
time = { workspace = true }
2632
zeroize = { workspace = true, default-features = true }

crate/interfaces/src/stores/objects_store.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use cosmian_kmip::{
55
kmip_0::kmip_types::State,
66
kmip_2_1::{kmip_attributes::Attributes, kmip_objects::Object},
77
};
8+
use time::OffsetDateTime;
89

910
use crate::{InterfaceResult, ObjectWithMetadata};
1011

@@ -117,4 +118,16 @@ pub trait ObjectsStore {
117118
) -> InterfaceResult<Vec<(String, State, Attributes)>> {
118119
Ok(vec![])
119120
}
121+
122+
/// Return UIDs of all Active objects that have a `rotate_interval > 0` and whose
123+
/// next rotation instant is ≤ `now`.
124+
///
125+
/// The next rotation instant is computed as:
126+
/// - `rotate_date + rotate_interval` (if `rotate_date` is set), or
127+
/// - `initial_date + rotate_interval + rotate_offset` (if `rotate_date` is None)
128+
///
129+
/// The default implementation returns an empty list; backends should override.
130+
async fn find_due_for_rotation(&self, _now: OffsetDateTime) -> InterfaceResult<Vec<String>> {
131+
Ok(vec![])
132+
}
120133
}

crate/server/src/config/command_line/clap_config.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl Default for ClapConfig {
7070
aws_xks_config: AwsXksConfig::default(),
7171
kmip_policy: KmipPolicyConfig::default(),
7272
azure_ekm_config: AzureEkmConfig::default(),
73+
auto_rotation_check_interval_secs: 0,
7374
}
7475
}
7576
}
@@ -213,6 +214,11 @@ pub struct ClapConfig {
213214
#[clap(flatten)]
214215
#[serde(rename = "kmip")]
215216
pub kmip_policy: KmipPolicyConfig,
217+
218+
/// Interval in seconds between background auto-rotation checks.
219+
/// Set to 0 (default) to disable the auto-rotation background task.
220+
#[clap(long, default_value = "0", verbatim_doc_comment)]
221+
pub auto_rotation_check_interval_secs: u64,
216222
}
217223

218224
impl ClapConfig {
@@ -651,6 +657,10 @@ impl fmt::Debug for ClapConfig {
651657
x.field("aws_xks_enable", &self.aws_xks_config.aws_xks_enable)
652658
};
653659
let x = x.field("kmip", &self.kmip_policy);
660+
let x = x.field(
661+
"auto_rotation_check_interval_secs",
662+
&self.auto_rotation_check_interval_secs,
663+
);
654664

655665
x.finish()
656666
}

crate/server/src/config/params/server_params.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ pub struct ServerParams {
164164
/// Client-supplied `MaximumItems` is clamped to this value; when absent the cap is
165165
/// applied automatically. Prevents unbounded DB queries and large response payloads.
166166
pub max_locate_items: u32,
167+
168+
/// Interval in seconds between background auto-rotation checks.
169+
/// 0 means disabled.
170+
pub auto_rotation_check_interval_secs: u64,
167171
}
168172

169173
/// Represents the server parameters.
@@ -414,6 +418,7 @@ impl ServerParams {
414418
rate_limit_per_second: conf.http.rate_limit_per_second,
415419
cors_allowed_origins: conf.http.cors_allowed_origins.unwrap_or_default(),
416420
max_locate_items: 1000,
421+
auto_rotation_check_interval_secs: conf.auto_rotation_check_interval_secs,
417422
};
418423

419424
debug!("{res:#?}");
@@ -637,6 +642,10 @@ impl fmt::Debug for ServerParams {
637642
debug_struct.field("rate_limit_per_second", &self.rate_limit_per_second);
638643
debug_struct.field("cors_allowed_origins", &self.cors_allowed_origins);
639644
debug_struct.field("max_locate_items", &self.max_locate_items);
645+
debug_struct.field(
646+
"auto_rotation_check_interval_secs",
647+
&self.auto_rotation_check_interval_secs,
648+
);
640649

641650
debug_struct.finish()
642651
}

0 commit comments

Comments
 (0)