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

Encrypt and upload secrets. #4706

Merged
merged 1 commit into from
Mar 13, 2018
Merged

Encrypt and upload secrets. #4706

merged 1 commit into from
Mar 13, 2018

Conversation

elliott-davis
Copy link
Contributor

@elliott-davis elliott-davis commented Mar 5, 2018


Blocked by: habitat-sh/builder#232

Signed-off-by: Elliott Davis elliott@excellent.io

@thesentinels
Copy link
Contributor

Thanks for the pull request! Here is what will happen next:

  1. Your PR will be reviewed by the maintainers
  2. If everything looks good, one of them will approve it, and your PR will be merged.

Thank you for contributing!


if res.status != StatusCode::Created {
return Err(err_from_response(res));
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Is that ; doing something here?

/// # Failures
///
/// * Remote Builder is not available
pub fn create_origin_secret(&self, origin: &str, token: &str, key: &str, secret: &str) -> Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

How does one update a secret? Presumably by calling this function again? I'm wondering if there's utility in having there be any distinction in the interface about whether there was already a value for the provided key (such as a Result<bool> return) or requiring something like an overwrite: bool parameter. I don't actually know; just throwing that idea out there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right now there will be a unique constraint on the key name per origin. Users will have to delete and recreate a key if the want to reuse it. This behavior should be consistent with Travis CI

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds good. Where is the the depot/origins/{}/secret implementation? Also, might be worth a note in the help.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

be taken from the HAB_BLDR_URL environment variable if defined. (default: \
https://bldr.habitat.sh)")
(@arg AUTH_TOKEN: -z --auth +takes_value "Authentication token for Builder")
(@arg ORIGIN: +takes_value
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it might be a bit more usable to make ORIGIN an option (requiring -o, --origin)

hab origin secret delete foo bar

is less obvious to me than

hab origin secret delete --origin bar foo

And generally, I think making the purpose of arguments that will often be omitted explicit reduces cognitive load. This goes for all the hab origin secret subcommands

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with the idea of having origin be a flag but in an effort to remain consistent with the existing UX I left it as a positional argument. I think a larger refactor is necessary to make origin a flag everywhere to keep everything consistent

Copy link
Contributor

Choose a reason for hiding this comment

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

I looked at the rest of the UX when there are multiple positional parameters, we do make origin an option:

    hab bldr job promote [FLAGS] [OPTIONS] <GROUP_ID> <CHANNEL>
…
OPTIONS:
…
    -o, --origin <ORIGIN>      Limit the promotable packages to the specified origin

When the origin is the only possible positional argument, it's fine, but in this case I think it's the UX consistent behavior to make it an option requiring a flag.

Copy link
Contributor

@ryankeairns ryankeairns Mar 7, 2018

Choose a reason for hiding this comment

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

My .02 is that I also prefer the clarity of using the -o or --origin flag... that also helps with not having to concern yourself with the order of the arguments - its explicit that the thing following the flag is in fact the origin name, in other words.

)
(@subcommand delete =>
(about: "Delete a secret for your origin")
(@arg KEY_NAME: +required +takes_value
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add aliases to be consistent with the other commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so. Personally I'd like to remove the other aliases as well. I find they often lead to typo bugs and provide an inconsistent user experience.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm on-board with removing all the aliases, but I think we should do that as a separate change rather than introducing just one new command that doesn't follow the pattern and leads to an inconsistent user experience. Not a big deal; do what feels right to you.

(@subcommand secret =>
(about: "Commands related to secret management")
(@setting ArgRequiredElseHelp)
(@subcommand generate =>
Copy link
Contributor

Choose a reason for hiding this comment

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

The name generate implies to me that I don't have to come up with the value. To me add is the more natural counterpart to delete.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I talked with @ryankeairns about this and he wanted the UX to be consistent with origin key generate. Previously I had origin secret create to follow the CRUD pattern.

Copy link
Contributor

Choose a reason for hiding this comment

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

I 💯 on-board with consistency, but in this case I'd say it's a different operation and so should have a different name to make that clear. Not a hill I'm looking to die on or anything, it just struck me as unexpected. I'm interested what @ryankeairns thinks.

Copy link
Contributor

@ryankeairns ryankeairns Mar 7, 2018

Choose a reason for hiding this comment

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

Just so I'm following... generate generates a new key; are we also creating/generating a new secret? or is that not exactly what's going on?

Copy link
Contributor

Choose a reason for hiding this comment

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

@elliott-davis can correct me if I'm wrong, but what we're doing here is asking builder to store an encrypted key/value pair that we are supplying. Builder isn't generating a value for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we're asking builder to encrypt and store a value for us. Technically we're encrypting it locally and asking builder to store it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't want to beat this to death (too late!), but perhaps upload is the verb then.


ui.status(Status::Encrypting, format!("value for key {}.", key))?;
let encrypted_secret_bytes = encryption_key.encrypt(secret.as_bytes(), None)?;
let encrypted_secret_string = String::from_utf8_lossy(&encrypted_secret_bytes);
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is arbitrary bytes, I worry about storing them in a str, which guarantees utf8 validity. Can we switch to either base64 encoding or using a Vec<u8> in create_origin_secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can switch from_utf8_lossy to from_utf8 to get a utf8 guarantee? What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 🎉

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that you're going to get errors at the rate which a random series of bytes do not make valid UTF-8, right? I don't know what that frequency is off the top of my head, but if there a reason why you wouldn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure I follow the random series of bytes bit. We are taking a user supplied string, converting it to bytes, sending it to encrypt which returns a base64 encoded series of bytes, then we convert that back into a string for a json payload. Does that match up with what you were thinking?

Copy link
Contributor

Choose a reason for hiding this comment

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

For posterity, the confusion was mine. I did not realize that BoxKeyPair::encrypt returned already base64-encoded data. I assumed since it's return type was Result<Vec<u8>> that it was raw bytes. This is pretty misleading, so I filed an issue: habitat-sh/core#15

@elliott-davis elliott-davis force-pushed the elliott/encrypt branch 3 times, most recently from a343f33 to 7d08248 Compare March 8, 2018 17:22
@elliott-davis
Copy link
Contributor Author

→ Determining secrets for core.
for
elliott@elliott-MacBookPro:/src/habitat$ target/debug/hab origin secret upload -u http://localhost:9636/v1 --origin core foo bar
☛ Encrypting value for key foo.
✓ Encrypted foo=[REDACTED].
↑ Uploading secret for key foo.
✓ Uploaded secret for foo.
elliott@elliott-MacBookPro:/src/habitat$ target/debug/hab origin secret list -u http://localhost:9636/v1 --origin core
→ Determining secrets for core.
foo
for
elliott@elliott-MacBookPro:/src/habitat$ target/debug/hab origin secret delete -u http://localhost:9636/v1 --origin core foo
☒ Deleting secret foo.
✓ Deleted secret foo.

@elliott-davis elliott-davis force-pushed the elliott/encrypt branch 3 times, most recently from 29d8083 to a74d9d7 Compare March 8, 2018 18:36
@baumanj
Copy link
Contributor

baumanj commented Mar 8, 2018

All the changes look look great! I'm still a unclear on #4706 (comment). Your thoughts?

@elliott-davis elliott-davis changed the title WIP: Encrypt and upload secrets. Todo: Delete, list Encrypt and upload secrets. Todo: Delete, list Mar 8, 2018
@elliott-davis elliott-davis changed the title Encrypt and upload secrets. Todo: Delete, list Encrypt and upload secrets. Mar 8, 2018
@elliott-davis elliott-davis force-pushed the elliott/encrypt branch 3 times, most recently from a928c0e to 1460bc9 Compare March 9, 2018 15:47
.into_iter()
.map(|m| m.into())
.map(|s: originsrv::OriginSecret| s.get_name().to_string())
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

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

I love this kind of chaining. So good.

.map_err(Error::DepotClient)?;

ui.status(Status::Deleted, format!("channel {}.", channel))?;
ui.status(Status::Deleted, format!("secret key {}.", key))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I'm a little confused. Why was components/hab/src/command/bldr/channel/destroy.rs changed to destroy origin secrets instead? Wouldn't it be better to leave the channel stuff alone and put the destroying secrets stuff into its own file?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I had the same thought!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:whoa: that's definitely a mistake.

ui.status(Status::Deleted, format!("secret {}.", key))?;

Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Right, here's the file I was thinking would be present for deleting secrets. I'm not sure what's happening in the channels file I commented on above.

))
}
};
ui.status(Status::Encrypted, format!("{}=[REDACTED].", key))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

The [REDACTED] is awesome.

@raskchanky
Copy link
Contributor

I left a few comments, but on the whole this looks terrific!

Copy link
Collaborator

@fnichol fnichol left a comment

Choose a reason for hiding this comment

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

Based on the name of the commit message, I'm assuming that this is approaching completion, but possibly not quite done?

Looking pretty good!

I'm starting to wonder about our timelines for releasing the whole feature (i.e. client through to server). If we're going to have our client code ready but not yet ready to service the calls, it might be worth considering feature toggling these subcommands so we can dark launch the code until we're ready to release every together. It still doesn't prevent anyone from using this new functionality (I'm guessing primarily developers on Builder at the moment), simply setting an environment variable.

(@subcommand upload =>
(about: "Create and upload a secret for your origin")
(@arg KEY_NAME: +required +takes_value
"The name of the variable key to be injected into the studio.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would a user provide the key prefix or not here? Maybe an example in the usage like we do in a few other places to clarify?

(about: "Create and upload a secret for your origin")
(@arg KEY_NAME: +required +takes_value
"The name of the variable key to be injected into the studio.")
(@arg SECRET: +required +takes_value
Copy link
Collaborator

Choose a reason for hiding this comment

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

Potential future idea (which might be terrible): I wonder what this would look like if you optionally allowed for stdin of the secret value so that you could pipe it in like command-that-gets-secret | hab origin secret AWS_SECRET. That's just freethinking though…

.map_err(Error::DepotClient)?;

ui.status(Status::Deleted, format!("channel {}.", channel))?;
ui.status(Status::Deleted, format!("secret key {}.", key))?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I had the same thought!

format!("Downloading public encryption origin key for {}", origin),
)?;
ui.begin(format!(
"Downloading public encryption origin key for {}",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm guessing this is simply rustfmt reformatting, but now that I read this message, it doesn't really make sense: origin keys aren't about encrypting anything, they're all about signing and verifying things (namely packages). I wonder if taking out the word "encryption" in the origin keys messaging might help to distinguish these 2 concepts in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is handling the case specific to origin encryption keys (they were a new concept introduced in a previous PR)

@elliott-davis elliott-davis force-pushed the elliott/encrypt branch 2 times, most recently from 1f6280b to 2a48e3f Compare March 9, 2018 19:49
Signed-off-by: Elliott Davis <elliott@excellent.io>
@raskchanky
Copy link
Contributor

@raskchanky
Copy link
Contributor

@thesentinels approve

@thesentinels
Copy link
Contributor

🤘 I am testing your branch against master before merging it. We do this to ensure that the master branch is never failing tests.

@thesentinels
Copy link
Contributor

:neckbeard: Travis CI has started testing this PR.

@thesentinels
Copy link
Contributor

💖 Travis CI reports this PR passed.

It always makes me feel nice when humans approve of one anothers work. I'm merging this PR now.

I just want you and the contributor to answer me one question:

gif-keyboard-3280869874741411265

@thesentinels thesentinels merged commit 6ce27f6 into master Mar 13, 2018
@thesentinels thesentinels deleted the elliott/encrypt branch March 13, 2018 21:51
@christophermaier christophermaier added Focus: CLI Related to the Habitat CLI (core/hab) component Type: Feature Issues that describe a new desired feature and removed A-cli labels Jul 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Focus: CLI Related to the Habitat CLI (core/hab) component Type: Feature Issues that describe a new desired feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants