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}