lambda_runtime_types/rotate/mod.rs
1//! Provides types for lambdas used for Secret Manager rotation.
2//!
3//! # Usage
4//!
5//! ```no_run
6//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
7//! struct Secret {
8//! user: String,
9//! password: String,
10//! }
11//!
12//! struct Runner;
13//!
14//! #[async_trait::async_trait]
15//! impl<'a> lambda_runtime_types::rotate::RotateRunner<'a, (), Secret> for Runner {
16//! async fn setup(region: &'a str) -> anyhow::Result<()> {
17//! // Setup logging to make sure that errors are printed
18//! Ok(())
19//! }
20//!
21//! async fn create(
22//! shared: &'a (),
23//! secret_cur: lambda_runtime_types::rotate::SecretContainer<Secret>,
24//! smc: &lambda_runtime_types::rotate::Smc,
25//! ) -> anyhow::Result<lambda_runtime_types::rotate::SecretContainer<Secret>> {
26//! // Create a new secret without setting it yet.
27//! // Only called if there is no pending secret available
28//! // (which may happen if rotation fails at any stage)
29//! unimplemented!()
30//! }
31//!
32//! async fn set(
33//! shared: &'a (),
34//! secret_cur: lambda_runtime_types::rotate::SecretContainer<Secret>,
35//! secret_new: lambda_runtime_types::rotate::SecretContainer<Secret>,
36//! ) -> anyhow::Result<()> {
37//! // Set the secret in the service
38//! // Only called if password is not already set, checked by
39//! // calling [`test`] with new password beforehand. The reason
40//! // for that it, that a failure in a later stage means all
41//! // stages are called again with set failing as the old password
42//! // does not work anymore
43//! Ok(())
44//! }
45//!
46//! async fn test(
47//! shared: &'a (),
48//! secret_new: lambda_runtime_types::rotate::SecretContainer<Secret>,
49//! ) -> anyhow::Result<()> {
50//! // Test whether a connection with the given secret works
51//! Ok(())
52//! }
53//!
54//! async fn finish(
55//! shared: &'a (),
56//! secret_cur: lambda_runtime_types::rotate::SecretContainer<Secret>,
57//! secret_new: lambda_runtime_types::rotate::SecretContainer<Secret>,
58//! ) -> anyhow::Result<()> {
59//! // Optional: Perform any work which may be necessary to
60//! // complete rotation
61//! Ok(())
62//! }
63//!
64//! }
65//!
66//! pub fn main() -> anyhow::Result<()> {
67//! lambda_runtime_types::exec_tokio::<_, _, Runner, _>()
68//! }
69//! ```
70//!
71//! For further usage like `Shared` Data, refer to the main [documentation](`crate`)
72
73#[cfg(feature = "rotate_aws_sdk")]
74mod aws_sdk;
75#[cfg(feature = "rotate_rusoto")]
76mod rusoto;
77mod smc;
78
79pub use smc::{SecretContainer, Smc};
80
81/// `Event` which is send by the `SecretManager` to the rotation lambda
82#[cfg_attr(
83 docsrs,
84 doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
85)]
86#[derive(Clone, serde::Deserialize)]
87pub struct Event<Secret> {
88 /// Request Token used for `SecretManager` Operations
89 #[serde(rename = "ClientRequestToken")]
90 pub client_request_token: String,
91 /// Id of the secret to rotate
92 #[serde(rename = "SecretId")]
93 pub secret_id: String,
94 /// Current step of the rotation
95 #[serde(rename = "Step")]
96 pub step: Step,
97 #[doc(hidden)]
98 #[serde(skip)]
99 pub _m: std::marker::PhantomData<Secret>,
100}
101
102impl<Secret> std::fmt::Debug for Event<Secret> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 f.debug_struct("Event")
105 .field("client_request_token", &self.client_request_token)
106 .field("secret_id", &self.secret_id)
107 .field("step", &self.step)
108 .finish()
109 }
110}
111
112/// Available steps for in a Secret Manager rotation
113#[cfg_attr(
114 docsrs,
115 doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
116)]
117#[derive(Debug, Copy, Clone, serde::Deserialize)]
118pub enum Step {
119 /// Secret creation
120 #[serde(rename = "createSecret")]
121 Create,
122 /// Secret configuration in service
123 #[serde(rename = "setSecret")]
124 Set,
125 /// Secret testing in service
126 #[serde(rename = "testSecret")]
127 Test,
128 /// Secret rotation finalization
129 #[serde(rename = "finishSecret")]
130 Finish,
131}
132
133/// Defines a type which is executed every time a lambda
134/// is invoced. This type is made for `SecretManager`
135/// rotation lambdas.
136///
137/// Types:
138/// * `Shared`: Type which is shared between lambda
139/// invocations. Note that lambda will
140/// create multiple environments for
141/// simulations invokations and environments
142/// are only kept alive for a certain time.
143/// It is thus not guaranteed that data
144/// can be reused, but with this types
145/// its possible.
146/// * `Secret`: The structure of the secret stored in
147/// the `SecretManager`. May contain only
148/// necessary fields, as other undefined
149/// fields are internally preserved.
150#[cfg_attr(
151 docsrs,
152 doc(cfg(any(feature = "rotate_rusoto", feature = "rotate_aws_sdk")))
153)]
154#[async_trait::async_trait]
155pub trait RotateRunner<'a, Shared, Secret>
156where
157 Shared: Send + Sync + 'a,
158 Secret: 'static + Send,
159{
160 /// See documentation of [`super::Runner::setup`]
161 async fn setup(region: &'a str) -> anyhow::Result<Shared>;
162
163 /// Create a new secret without setting it yet.
164 /// Only called if there is no pending secret available
165 /// (which may happen if rotation fails at any stage)
166 async fn create(
167 shared: &'a Shared,
168 secret_cur: SecretContainer<Secret>,
169 smc: &Smc,
170 ) -> anyhow::Result<SecretContainer<Secret>>;
171
172 /// Set the secret in the service
173 /// Only called if password is not already set, checked by
174 /// calling [`test`] with new password beforehand. The reason
175 /// for that it, that a failure in a later stage means all
176 /// stages are called again with set failing as the old password
177 /// does not work anymore
178 async fn set(
179 shared: &'a Shared,
180 secret_cur: SecretContainer<Secret>,
181 secret_new: SecretContainer<Secret>,
182 ) -> anyhow::Result<()>;
183
184 /// Test whether a connection with the given secret works
185 async fn test(shared: &'a Shared, secret_new: SecretContainer<Secret>) -> anyhow::Result<()>;
186
187 /// Perform any work which may be necessary to complete rotation
188 async fn finish(
189 _shared: &'a Shared,
190 _secret_cur: SecretContainer<Secret>,
191 _secret_new: SecretContainer<Secret>,
192 ) -> anyhow::Result<()> {
193 Ok(())
194 }
195}
196
197#[async_trait::async_trait]
198impl<'a, Type, Shared, Sec> super::Runner<'a, Shared, Event<Sec>, ()> for Type
199where
200 Shared: Send + Sync + 'a,
201 Sec: 'static + Send + Sync + Clone + serde::de::DeserializeOwned + serde::Serialize,
202 Type: 'static + RotateRunner<'a, Shared, Sec>,
203{
204 async fn setup(region: &'a str) -> anyhow::Result<Shared> {
205 Self::setup(region).await
206 }
207
208 async fn run(
209 shared: &'a Shared,
210 event: crate::LambdaEvent<'a, Event<Sec>>,
211 ) -> anyhow::Result<()> {
212 let smc = Smc::new(event.region).await?;
213 log::info!("{:?}", event.event.step);
214 match event.event.step {
215 Step::Create => {
216 let secret_cur = smc
217 .get_secret_value_current::<Sec>(&event.event.secret_id)
218 .await?;
219 let secret_new = smc
220 .get_secret_value_pending::<Sec>(&event.event.secret_id)
221 .await;
222 if let Ok(secret_new) = secret_new {
223 if secret_new.version_id != secret_cur.version_id {
224 log::info!("Found existing pending value.");
225 return Ok(());
226 }
227 }
228 log::info!("Creating new secret value.");
229 let secret = Self::create(shared, secret_cur.inner, &smc).await?;
230 smc.put_secret_value_pending(
231 &event.event.secret_id,
232 Some(&event.event.client_request_token),
233 &secret,
234 )
235 .await?;
236 Ok(())
237 }
238 Step::Set => {
239 log::info!("Setting secret on remote system.");
240 let secret_new = smc
241 .get_secret_value_pending(&event.event.secret_id)
242 .await?
243 .inner;
244 if Self::test(shared, SecretContainer::clone(&secret_new))
245 .await
246 .is_err()
247 {
248 let secret_cur = smc
249 .get_secret_value_current(&event.event.secret_id)
250 .await?
251 .inner;
252 Self::set(shared, secret_cur, secret_new).await?;
253 } else {
254 log::info!("Password already set in remote system.");
255 }
256 Ok(())
257 }
258 Step::Test => {
259 log::info!("Testing secret on remote system.");
260 let secret = smc
261 .get_secret_value_pending(&event.event.secret_id)
262 .await?
263 .inner;
264 Self::test(shared, secret).await?;
265 Ok(())
266 }
267 Step::Finish => {
268 log::info!("Finishing secret deployment.");
269 let secret_current: smc::Secret<Sec> =
270 smc.get_secret_value_current(&event.event.secret_id).await?;
271 let secret_pending: smc::Secret<Sec> =
272 smc.get_secret_value_pending(&event.event.secret_id).await?;
273 Self::finish(shared, secret_current.inner, secret_pending.inner).await?;
274 smc.set_pending_secret_value_to_current(
275 secret_current.arn,
276 secret_current.version_id,
277 secret_pending.version_id,
278 )
279 .await?;
280 Ok(())
281 }
282 }
283 }
284}