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(cli): Initial secrets implementation #3345
base: master
Are you sure you want to change the base?
Conversation
c106710
to
aca302c
Compare
What if you also add a |
That is an option. It changes the behavior of kopia, but it is not likely that users are crating/connecting new repos every day, so it probably wouldn't break user's workflows. The one case I am concerned with is whether this can/should be extended for |
Oh I should clarify that Examples:
|
Yeah, I realized that is what you meant after I thought about it some more. I implemented exactly that, and converted |
f4c5989
to
43881fc
Compare
this is looking great already, may I suggest separating non-functional changes (code movement and renames) into separate PR that can be trivially merged so that this one becomes smaller and easier to review? |
471a07a
to
9557981
Compare
@jkowalski I implemented the changes based on your code review. I will start cleaning up the broken tests and implementing tests for the new functionality next. |
One thing I noticed is that because we call Edit: I guess one option is to default to a less-expensive key-derivation algorithm for secrets. It would still be better than the plain-text we have now |
I believe I have all functionality implemented now, and covered all storage options. Still need feedback on if the performance degradation is ok, and if not how to remedy it. All tests pass on my machine, but I don't have the ability to run the other architectures, so need to wait for CI runs |
1b42d11
to
97299c1
Compare
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #3345 +/- ##
==========================================
- Coverage 75.82% 75.81% -0.01%
==========================================
Files 463 468 +5
Lines 37100 37587 +487
==========================================
+ Hits 28132 28498 +366
- Misses 7036 7122 +86
- Partials 1932 1967 +35
☔ View full report in Codecov by Sentry. |
@@ -24,8 +24,8 @@ func (c *storageS3Flags) Setup(svc StorageProviderServices, cmd *kingpin.CmdClau | |||
cmd.Flag("endpoint", "Endpoint to use").Default("s3.amazonaws.com").StringVar(&c.s3options.Endpoint) | |||
cmd.Flag("region", "S3 Region").Default("").StringVar(&c.s3options.Region) | |||
cmd.Flag("access-key", "Access key ID (overrides AWS_ACCESS_KEY_ID environment variable)").Required().Envar(svc.EnvName("AWS_ACCESS_KEY_ID")).StringVar(&c.s3options.AccessKeyID) | |||
cmd.Flag("secret-access-key", "Secret access key (overrides AWS_SECRET_ACCESS_KEY environment variable)").Required().Envar(svc.EnvName("AWS_SECRET_ACCESS_KEY")).StringVar(&c.s3options.SecretAccessKey) | |||
cmd.Flag("session-token", "Session token (overrides AWS_SESSION_TOKEN environment variable)").Envar(svc.EnvName("AWS_SESSION_TOKEN")).StringVar(&c.s3options.SessionToken) | |||
secretVarWithEnv(cmd.Flag("secret-access-key", "Secret access key (overrides AWS_SECRET_ACCESS_KEY environment variable)"), svc.EnvName("AWS_SECRET_ACCESS_KEY"), &c.s3options.SecretAccessKey) |
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 is losing the Required()
aspect of some flag which will fail at startup if a value is not provided.
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.
Yes, I added the check below instead. I don't see a good way to support the required field along with using a custom flag. I can try to rethink how the env variable case is handled, as this is the primary reason required doesn't work.
Edit: looks like i did for the token but missed the access key.
I'm not sure I understand the need to have yet another encryption scheme for embedding encryopted literals in a config file when we have external secret storage systems for fetching those dynamically. I was hoping we can simplify the solution by separating 2 concerns - one being access to all these runtime providers of secrets and the other - the problem of storing localconfig unencrypted.
Wen can then have something like:
This would check if the string starts with any of supported providers and invioke the appropriate provider to get the value, or return the literal value if no prefix matches.
|
That's the kind of feedback that I'd have preferred before doing a full implementation. I don't have the time to rework this completly right now. I'll just leave it until I have time to come back to it. |
Yeah, sorry about that. I've been traveling the whole last week and did not have a chance to look into that properly. I think 85% of this PR is still exactly what we need, we just need to simplify the secrets package and perhaps remove a few things. |
This is an implementation of a secrets framework to allow various ways to specify and store secrets. It is eventually meant to implement #3312 when complete.
The implementation creates a Secret object which stores any secret. it can be prefixed with various options to allow reading the secret from an env variable, cmdline, vault, etc. Or the secret can be passed in directly. If a secret is provided directly on the cmdline, it will be encrypted using a signing key and will be stored in the config file. If the secret is provided indirectly (cmdline, vault, env-variable) it is not stored.
The signing key is a random 32-byte string, and is stored in the config file after encrypting it with a key derived from the repo password. This makes it easy to change the repo password without needing to update any stored secrets.
Currently the code is using fernet for the symmetric-key encryption because it was easy to get working, but likely this should reuse the same algorithms used for file encryption.The code has been updated to share the Aes encryption code between the repo and the secrets.The signing key is currently stored in a context, as I needed it to be globally available with the initial implementation. However, I think I have mostly eliminated that need, so I am going to try to eliminate the Context stuff.I have eliminated the need for the context, though there is still some code reformatting needed after the fact.
I am using reflection to walk the local-config struct to find Secrets. This provides a clean interface to the rest of the code in that variables do not need to be individually decoded after the password is available, but I'm not sure it is the ideal solution.
I am posting to see if I can get any feedback on whether this is a reasonable solution, or if something else is desired.
At the moment, only sftp is supported.
Usage:
This implementation does mean that there are some illegal password strings (anything starting with one of the keywords followed by a colon). There may be alternatives if this is undesirable.
To Do:
Implement Rclone--password
and--server-password
cmdline argument