Giving My App Secrets to the AWS SecretsManager
Tags: rust, aws, rusoto, secretmanager
Published: 2020-07-11 19:57:57

---
title: Giving My App Secrets to the AWS SecretsManager
published: true
date: 2020-07-11 19:10:09 UTC
tags: Rust,aws,rusoto,secretmanager
canonical_url: https://rust.graystorm.com/2020/07/11/giving-my-app-secrets-to-the-aws-secretmanager/
cover_image: https://rust.graystorm.com/wp-content/uploads/2019/06/plant-wood-old-rust-equipment-gear-1278587-pxhere.com_.jpg
series: Designing a Full-Featured WebApp with Rust
---

![Mother telling a child a secret. Is AWS my child in this image??](https://i0.wp.com/rust.graystorm.com/wp-content/uploads/2020/07/person-people-girl-woman-portrait-spring-21589-pxhere.com_.jpg?w=840&ssl=1)<figcaption>Psst… want an API key?</figcaption>

I’m still working around my PinPointShooting app without actually making any app progress. You can go back to the [start](https://rust.graystorm.com/2019/07/13/piecing-together-a-rust-web-application/) if you like or just jump in here. So far, I’ve had my app ids and secrets hidden inside a .env file, but rather than keep them so close to the code, lets move them into my Amazon Web Services (AWS) account. It will cost me an extra $2.40/month to store these 6 secrets (so far) at Amazon ($0.40/month/secret it claims), but it gets me more practice using the rusoto\_core crate and its subsidiaries. So, here is how I went about giving all of my app secrets to the AWS SecretsManager.

## Cargo.toml Changes

First, in Cargo.toml, I already have rusoto\_core and rusoto\_dynamodb for storing my session data. So I only need to add rusoto\_secretsmanager:

```
[dependencies]slog = "2.5.0"
slog-bunyan = "2.1.0"
base64 = "0.10.1"
rand = "0.7.0"
rand_core = "0.5.0"
rust-crypto = "0.2.36"
config = "0.9.3"
serde_json = "1.0.40"
once_cell = "0.2.2"
dotenv = "0.14.1"
rocket-slog = "0.4.0"
sha2 = "0.8.0"
rusoto_core = "0.40.0"
rusoto_dynamodb = "0.40.0"
rusoto_secretsmanager = "0.40.0"
time = "0.1.42"
google-signin = "0.3.0"
```

## Store Those Secrets in AWS

Of course, I had to log into AWS and create these 6 new secrets. In case I need more for other applications in the future, I prefixed each of these with “PPS\_”. So, I created:

| Secret name |
| --- |
| PPS_google_api_client_id |
| PPS_google_api_client_secret |
| PPS_google_maps_api_key |
| PPS_facebook_app_id |
| PPS_facebook_app_secret |
| PPS_database_url |

<figcaption>List of secrets I created in my AWS account</figcaption>

For each, I choose the “Other type of secrets (e.g. API key)” as the type. I stored the pair as “key” and the value I wanted to protect as a secret. This becomes the very simple struct:

```
#[derive(Debug, Deserialize)]
struct AWSSecret {
  key: String,
}
```

So, instead of pulling these into my global CONFIG from the .env file, I want to pull them from AWS instead. This requires changes to the settings.rs file. First I add the requirements at the top:

```
use rusoto_core::Region;
use rusoto_secretsmanager::{GetSecretValueRequest, SecretsManager, SecretsManagerClient};
```

plus I define the AWSSecret struct I mentioned above. Now, right after I pull in all the config settings, just before I try to turn that into a Lazy::New object to initialize the CONFIG, I need to add yet another place to pull settings from. Lets loop through each of the 6 and pull them down from AWS:

## Pull Into Config

```
// now pull secrets from AWS
// note, AWS secrets include PPS_ prefix for this application
for secret in vec![
  "database_urls",
  "google_api_client_id",
  "google_api_client_secret",
  "google_maps_api_key",
  "facebook_app_id",
  "facebook_app_secret",
] {
  config
    .set(secret, get_secret(format!("PPS_{}", secret)).key)
    .unwrap();
}
```

So, for each Config setting stored on AWS, try to get that secret, prepending a “PPS_” in front of the secret name when checking AWS. Now I just write that get_secret() function:

## get_secrets stored at AWS

```
fn get_secret(secret: String) -> AWSSecret {
  let secrets_manager = SecretsManagerClient::new(Region::UsEast1);
  match secrets_manager
    .get_secret_value(GetSecretValueRequest {
      secret_id: secret.clone(),
      ..Default::default()
    })
    .sync() {
      Ok(resp) => serde_json::from_str(&resp.secret_string.unwrap()).unwrap(),
      Err(err) => panic!("Could not retrieve secret {} from AWS: {:?}", secret, err),
    }
}
```

I’ll probably look back on this code in 6 months or a year and cringe, but this works anyway – and dies if it fails to pull a secret, which is what I want. I guess get\_secret could return Some(AWSSecret) or None… but None means something in the app will break – I might as well just panic!

Of course, my AWS credentials are still stored in the .env file. Who protects the secret the protects the secrets!? Anyway, I did update the .env.sample file to reflect the new setup:

```
# configure for your user and rename to .env
ROCKET_ENV=development
AWS_ACCESS_KEY_ID=your_key_here
AWS_SECRET_ACCESS_KEY=your_key_here

# store as AWS secrets: key/value
#PPS_database_url (for example "postgres://user:password@localhost/pinpoint")
#PPS_google_api_client_id
#PPS_google_api_client_secret
#PPS_google_maps_api_key
#PPS_facebook_app_id
#PPS_facebook_app_secret
```

As always, see [PinPointShooting.com](https://www.pinpointshooting.com/) to see just how little it does so far (if it happens to be running), or more exciting: go see the [git repo](https://github.com/jculverhouse/pinpoint_shooting)itself.

The post [Giving My App Secrets to the AWS SecretsManager](https://rust.graystorm.com/2020/07/11/giving-my-app-secrets-to-the-aws-secretmanager/) appeared first on [Learning Rust](https://rust.graystorm.com).