-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Conversation
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 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 |
…her to compress/decompress, whether value is base64 encoded
|
||
encoding := data.Get("encoding").(string) | ||
if encoding != "" && encoding != "base64" { | ||
return logical.ErrorResponse("invalid encoding '%s'", encoding), logical.ErrInvalidRequest |
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 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
Outdated
} | ||
} | ||
|
||
if compressed { |
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 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?
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 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).
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.
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 .
…sion and the type compression from the existing value.
} | ||
|
||
if compressionType != test.compressionConfig.Type { | ||
t.Fatalf("bad compressiontType value;\nexpected: %q\nactional: %q", test.compressionConfig.Type, compressionType) |
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.
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_system_test.go
Outdated
if err != nil { | ||
t.Fatalf("err: %v", err) | ||
} | ||
if !strings.HasPrefix(resp.Data["value"].(string), "{\"type\":\"mounts\"") { |
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 !strings.HasPrefix(resp.Data["value"].(string), "{\"type\":\"mounts\"") { | |
if !strings.HasPrefix(resp.Data["value"].(string), `{"type":"mounts"`) { |
Looking really good, ty for the comprehensive tests. |
Nice work, thanks @shwuandwing ! |
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