-
Notifications
You must be signed in to change notification settings - Fork 315
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
Conversation
Thanks for the pull request! Here is what will happen next:
Thank you for contributing! |
|
||
if res.status != StatusCode::Created { | ||
return Err(err_from_response(res)); | ||
}; |
There was a problem hiding this comment.
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<()> { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
components/hab/src/cli.rs
Outdated
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
components/hab/src/cli.rs
Outdated
) | ||
(@subcommand delete => | ||
(about: "Delete a secret for your origin") | ||
(@arg KEY_NAME: +required +takes_value |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
components/hab/src/cli.rs
Outdated
(@subcommand secret => | ||
(about: "Commands related to secret management") | ||
(@setting ArgRequiredElseHelp) | ||
(@subcommand generate => |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 🎉
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
a343f33
to
7d08248
Compare
|
29d8083
to
a74d9d7
Compare
All the changes look look great! I'm still a unclear on #4706 (comment). Your thoughts? |
a74d9d7
to
b93dab4
Compare
a928c0e
to
1460bc9
Compare
.into_iter() | ||
.map(|m| m.into()) | ||
.map(|s: originsrv::OriginSecret| s.get_name().to_string()) | ||
.collect(); |
There was a problem hiding this comment.
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))?; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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(()) | ||
} |
There was a problem hiding this comment.
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))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The [REDACTED]
is awesome.
I left a few comments, but on the whole this looks terrific! |
There was a problem hiding this 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.
components/hab/src/cli.rs
Outdated
(@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.") |
There was a problem hiding this comment.
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?
components/hab/src/cli.rs
Outdated
(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 |
There was a problem hiding this comment.
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))?; |
There was a problem hiding this comment.
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 {}", |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
1f6280b
to
2a48e3f
Compare
Signed-off-by: Elliott Davis <elliott@excellent.io>
2a48e3f
to
fabefbe
Compare
@thesentinels approve |
🤘 I am testing your branch against master before merging it. We do this to ensure that the master branch is never failing tests. |
Travis CI has started testing this PR. |
💖 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: |
Blocked by: habitat-sh/builder#232
Signed-off-by: Elliott Davis elliott@excellent.io