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

Enhance sys/raw to read and write values that cannot be encoded in json #13537

Merged
merged 12 commits into from
Jan 20, 2022

Conversation

shwuandwing
Copy link
Contributor

This enhances sys/raw so it can return / write values that cannot be encoded in json (like gzip data for core/mounts, or protobuf in KV v2 mounts)

Addresses / fixes #12746, #13536

@vercel vercel bot temporarily deployed to Preview – vault-storybook December 31, 2021 02:59 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook December 31, 2021 03:02 Inactive
@ncabatoff
Copy link
Collaborator

Hi @shwuandwing,

I'm excited to see you working on this, as this limitation is something I'd love to see addressed.

I don't think your current solution actually solves #12746; I would expect to still see the same error as was reported in that issue after your changes, since we're still looking at the error from compressutil.Decompress.

What do you think about adding an optional compressed boolean option? handleRawRead would maintain its current behaviour when the option isn't provided or when compressed=true; if compressed=false is given, it would not attempt to call Decompress. handleRawWrite would compress the provided value before storing it if compressed=true was provided. I prefer that we not rely on the user to do the compression of the value they want written, since that seems error prone - I'm sure plenty of users would try to compress the data without using our compressutil.

This doesn't obviate the need for base64. I don't think it's strictly required - I just did some tests with uncompressed binary storage entries and I was able to read/write the value just fine. But it's not ideal to have binary unencoded data go to the user's terminal. From that perspective, your solution of returning both the original value plus a value_base64 isn't ideal, in that it still allows for that to happen. How about instead we just retain the single value (no value_base64), and add an encoding=base64 option for both reads and writes?

@vercel vercel bot temporarily deployed to Preview – vault-storybook January 9, 2022 18:30 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 9, 2022 18:39 Inactive
vault/logical_raw.go Show resolved Hide resolved
vault/logical_raw.go Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 10, 2022 18:58 Inactive
@shwuandwing shwuandwing requested a review from ncabatoff January 10, 2022 19:18
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 10, 2022 19:32 Inactive

encoding := data.Get("encoding").(string)
if encoding != "" && encoding != "base64" {
return logical.ErrorResponse("invalid encoding '%s'", encoding), logical.ErrInvalidRequest
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is fine, but FYI more commonly we omit the second return (error) value when we're using logical.ErrorResponse. logical.ErrInvalidRequest translates to http.StatusBadRequest, which is the default for when we return an error with a non-nil response, as is the case for logical.ErrorResponse.

vault/logical_raw.go Show resolved Hide resolved
vault/logical_raw.go Outdated Show resolved Hide resolved
}
}

if compressed {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the user doesn't provide compressed=true, we're not making use of the information we have in the existing storage entry, and we'll go ahead and replace an existing compressed entry with an uncompressed one. Can you reorder things so that if this is an update (instead of a create), we use the information in the existing storage entry to determine whether and how to compress? And in that case maybe we should return an error if they did specify those options and they don't match the existing storage entry?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just clarifying. To differentiate between update vs create, backends generally need to implement "ExistenceCheck" -- https://github.com/hashicorp/vault/blob/main/sdk/framework/path.go#L92.

Are you saying you want this diff to implement "ExistenceCheck" for sys/raw and use that in "handleRawWrite", or skip ExistenceCheck and have "handleRawWrite" do something like

entry, err := b.barrier.Get(ctx, path)
_, compressionType, _, _ := compressutil.DecompressWithCanary(entry.Value)
if compressionType != "" && !compressed {
return err // this is compressed data but compression was not specified
}

One problem with this approach is what if users want to replace an existing compressed entry with an uncompressed one (that's actually what happens today if you use sys/raw to update any json-compressed entries like core/audit or core/mounts).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I implemented an existence check -- and updates now will use the existing compression type (or no compression) -- and check + error out if the new compression type is different the existing compression type.

I did make one small exception, users can force an entry to be non-compressed even if it was compressed before. This ensures a users can workaround unknown issues with compressutil .

sdk/helper/compressutil/compress.go Show resolved Hide resolved
…sion and the type compression from the existing value.
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 19, 2022 08:56 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 19, 2022 09:00 Inactive
@shwuandwing shwuandwing requested a review from ncabatoff January 19, 2022 18:22
}

if compressionType != test.compressionConfig.Type {
t.Fatalf("bad compressiontType value;\nexpected: %q\nactional: %q", test.compressionConfig.Type, compressionType)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
t.Fatalf("bad compressiontType value;\nexpected: %q\nactional: %q", test.compressionConfig.Type, compressionType)
t.Fatalf("bad compressionType value;\nexpected: %q\nactual: %q", test.compressionConfig.Type, compressionType)

vault/logical_raw.go Outdated Show resolved Hide resolved
if err != nil {
t.Fatalf("err: %v", err)
}
if !strings.HasPrefix(resp.Data["value"].(string), "{\"type\":\"mounts\"") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if !strings.HasPrefix(resp.Data["value"].(string), "{\"type\":\"mounts\"") {
if !strings.HasPrefix(resp.Data["value"].(string), `{"type":"mounts"`) {

@ncabatoff
Copy link
Collaborator

Looking really good, ty for the comprehensive tests.

@vercel vercel bot temporarily deployed to Preview – vault-storybook January 19, 2022 23:57 Inactive
@vercel vercel bot temporarily deployed to Preview – vault-storybook January 20, 2022 00:17 Inactive
@shwuandwing shwuandwing requested a review from ncabatoff January 20, 2022 00:19
@ncabatoff ncabatoff merged commit 94d921f into hashicorp:main Jan 20, 2022
@ncabatoff
Copy link
Collaborator

Nice work, thanks @shwuandwing !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Compression Handler Falsely Assumes All Raw Data is JSON
3 participants