Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wrap secrets in custom types to prevent them from leaking #925

Merged
merged 8 commits into from
Oct 25, 2023

Conversation

AlphaKeks
Copy link
Contributor

Description of change

This aims to close #890. I looked into secrecy and tried to integrate it into the code base and found some odd trait bound issues that wouldn't allow using them in structs that implement Serialize or Deserialize. The crate provides a serde feature that should enable exactly this, but this still would lead to weird issues of standard library types not implementing traits from the secrecy crate. This file seems to be the issue. They require the wrapped type to implement SerializableSecret which they never implement for standard library types anywhere?

Anyway, instead I decided to replicate the functionality of the crate myself by creating a wrapper type that makes sure to not expose the inner value unless by calling the expose function explicitly. The underlying data will also be zeroed out in memory when the type is dropped to ensure it won't leak afterwards.

I have looked through the code base searching for places where this type could be useful and made some changes. If you know other places where secrets are being used that I missed, please let me know.

@AlphaKeks AlphaKeks self-assigned this May 19, 2023
@iulianbarbu iulianbarbu added the M label May 19, 2023
Copy link
Contributor

@iulianbarbu iulianbarbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What have you thought about common/src/lib.rs::DatabaseReadyInfo (mainly its password field)? I think we usually need it when db resource information is requested, but we should defend against logging it accidentally.

common/src/secrets.rs Outdated Show resolved Hide resolved
@AlphaKeks
Copy link
Contributor Author

I wrapped the password in Secret, must've overlooked that the first time, thanks.
I am unsure what address_private field is, should we wrap that in Secret too?

@iulianbarbu
Copy link
Contributor

I am unsure what address_private field is, should we wrap that in Secret too?

I don't think so. I think it's the address that can be used internally by the backend. It might save some external network calls when compared to accessing the DB through the public URL.

Copy link
Contributor

@oddgrd oddgrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @AlphaKeks, thanks for this! I left a couple of small comments, and clippy also has a suggestion in the CI failure


#[test]
fn secret_struct() {
#[allow(dead_code)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I didn't have tests for it as I figured it's unnecessary (since everything we could test is basically already enforced by the compiler), but there was a point about having them to prevent regressions in the future. I wasn't entirely sure what to do with that, so if you think we should remove them, I will remove them again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry! I just meant the allow(dead_code) annotation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah. The compiler was complaining about the struct being unused.

common/src/lib.rs Show resolved Hide resolved
@AlphaKeks
Copy link
Contributor Author

A user in our discord brought up that using SharedDB will print out the database connection string when running or deploying a project, which in my opinion shouldn't be the default. What do you guys think about adding a CLI flag to both run and deploy to only show secrets like connections strings on demand?

@iulianbarbu
Copy link
Contributor

iulianbarbu commented Jun 9, 2023

A user in our discord brought up that using SharedDB will print out the database connection string when running or deploying a project, which in my opinion shouldn't be the default. What do you guys think about adding a CLI flag to both run and deploy to only show secrets like connections strings on demand?

I would include the secrets exposing flagging for the DB connections as well. At the same time, maybe a global flag will do better? It's gonna be accounted for both secrets and db connection strings. What do you think?

@AlphaKeks
Copy link
Contributor Author

maybe a global flag will do better?

Are you talking about a flag for cargo-shuttle itself (not subcommands)? I think since this will only apply to 3 of the 15 or so subcommands, I think it wouldn't be a good fit for a global flag.

I will start implementing the functionality for run and deploy and if you have any objections, I can make it a global flag afterwards.

@@ -11,6 +11,7 @@ async-trait = "0.1.56"
dunce = "1.0.3"
fs_extra = "1.3.0"
serde = { version = "1.0.148", features = ["derive"] }
shuttle-common = { path = "../../common", version = "0.18.0", default-features = false }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this as a static-folder dep?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests were failing because of a type mismatch on this function.

Comment on lines 124 to 126
Secrets {
#[arg(long)]
/// Reveal the values of the secrets
show: bool,
},
Copy link
Contributor

@iulianbarbu iulianbarbu Jun 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We considered lately that we would prefer not to show secrets at all. It's best to remain hidden after deploying, having the user deploy again if they want to change them. The idea is simple: if the user wants to display the previous secrets used for the deployment, shouldn't use Shuttle for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I will remove the flags.

@jonaro00 jonaro00 removed E-M labels Aug 17, 2023
@jonaro00
Copy link
Member

@AlphaKeks Do you want more feedback or help with finishing this? I can take a look if needed.

@AlphaKeks
Copy link
Contributor Author

Oh yeah, kinda forgot about this, my bad! Will take a look tomorrow and get this up to date, then ask for review. I'm pretty sure this was basically finished.

@iulianbarbu
Copy link
Contributor

Oh yeah, kinda forgot about this, my bad! Will take a look tomorrow and get this up to date, then ask for review. I'm pretty sure this was basically finished.

We've been discussing polishing this a bit before merging but it is almost there. Please take a look at my last review.

@Kazy
Copy link
Contributor

Kazy commented Oct 19, 2023

Hey @AlphaKeks, I hope you're doing alright :)

Sorry for the delay and back and forth with the review, we're still interested in getting this merged, if you're also still interested in working on it and getting this through. After discussing it with the team, there are still a few changes we would like to make:

  1. Don't hide the connection string, only the password in it. It could be replaced by ******* (a fixed number).
  2. Add a --show-secrets option but only to the resource list command, that would reveal the password in the connection string.
  3. Rebase on top of main.

If you don't want to or don't have the time to continue working on it that's perfectly fine, just let us know and we'll take over. Thank you very much !

@AlphaKeks
Copy link
Contributor Author

Not really interested anymore, sorry.

@Kazy
Copy link
Contributor

Kazy commented Oct 23, 2023

No worries, thank you for your work !

@Kazy Kazy assigned Kazy and unassigned AlphaKeks Oct 23, 2023
@Kazy Kazy force-pushed the feat/secrets branch 4 times, most recently from 5e66e58 to afba827 Compare October 23, 2023 09:38
Copy link
Contributor

@iulianbarbu iulianbarbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just that we'll need to investigate why tests are failing.

common/src/lib.rs Outdated Show resolved Hide resolved
@jonaro00 jonaro00 changed the title Feature: wrap secrets in custom types to prevent them from leaking feat: wrap secrets in custom types to prevent them from leaking Oct 23, 2023
@Kazy Kazy force-pushed the feat/secrets branch 6 times, most recently from c26649f to 8ca577e Compare October 23, 2023 14:23
Copy link
Contributor

@iulianbarbu iulianbarbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we can not remove the .collect() call for the into_iter() that easily. LGTM now, just that we need to fix the conflicts.

@iulianbarbu iulianbarbu merged commit bf6161c into shuttle-hq:main Oct 25, 2023
34 checks passed
@Kazy Kazy deleted the feat/secrets branch October 25, 2023 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: Ensure we can’t log secrets
5 participants