-
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
Add support for hashing time.Time within slices, which unbreaks auditing #6767
Conversation
of requests returning the request counters. Modified HashStructure signature to make clear that hashWalker is really intended to work on map[string]interface{}. It may be capable of doing more than that, but it really doesn't handle arbitrary data. Also removed some dead code, and got rid of the hashWalker.csData field since it's the same as the last element of csKey.
audit/hashstructure.go
Outdated
@@ -108,18 +107,18 @@ func Hash(salter *salt.Salt, raw interface{}, nonHMACDataKeys []string) error { | |||
// the structure. Only _values_ are hashed: keys of objects are not. | |||
// | |||
// For the HashCallback, see the built-in HashCallbacks below. | |||
func HashStructure(s interface{}, cb HashCallback, ignoredKeys []string) (interface{}, error) { | |||
s, err := copystructure.Copy(s) | |||
func HashStructure(s map[string]interface{}, cb HashCallback, ignoredKeys []string) (interface{}, error) { |
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.
Please revert this change. The fact that the only things we pass in right now are map[string]interface{} doesn't change the fact that both copystructure nor reflectwalk are able to work just fine on structs, and we may well want to pass in full structs at some future point.
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.
copystructure and reflectwalk work fine on structs, yes. That doesn't hold true for hashWalker.
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.
Currently. It doesn't really bother me either way, it can be changed back, just seems like an unnecessary change.
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, totally unnecessary. I felt that without this I'd have to do more work to fill in the gaps, but if you prefer I can leave the signature alone and add a big disclaimer instead.
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.
Either way. Nobody is expecting you to code in support for non-maps. Keep this or leave a comment.
audit/hashstructure.go
Outdated
func HashStructure(s interface{}, cb HashCallback, ignoredKeys []string) (interface{}, error) { | ||
s, err := copystructure.Copy(s) | ||
func HashStructure(s map[string]interface{}, cb HashCallback, ignoredKeys []string) (interface{}, error) { | ||
scopy, err := copystructure.Copy(s) |
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.
What's the reason for this copy? It assumes that you'd never want to modify the actual value coming in. Plus, we are already providing a copy in the first place.
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 copy was already there. I wondered myself why we're copying twice. The reason I introduced a new variable here was a side-effect of the signature change.
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.
Sure, it's already there -- the question is, what's the reason for it :-)
It seems like either we should figure out if we're copying in all cases already and remove this, or remove the places where we're already copying.
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.
Sounds good. I'm thinking I'll remove the copying from HashStructure and FormatRequest/FormatResponse, instead doing it in Hash. Rationale: HashStructure is lower level, maybe someday a caller will come along that doesn't need/want copying and this provides that option. Meanwhile FormatRequest and FormatResponse are already doing a lot, and this detail feels like it should be Hash's responsibility.
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.
Sounds reasonable.
audit/hashstructure.go
Outdated
// Second to last element of w.loc is location that contains this struct. | ||
switch w.loc[len(w.loc)-2] { | ||
case reflectwalk.MapKey: | ||
return errors.New("time.Time value in a map key cannot be hashed for audits") |
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 assume the behavior generally is we only hash values, not keys. In which case throwing an error here seems overkill...why not just not hash it but not throw an error and then when it's marshaled it'll become a string key 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.
Sure, I was trying to maintain the existing behaviour except where it had to change to fix the error, but I can let it through. But then the question may arise "why are you only hashing some time.Time values and not others?" Suppose for example someday someone puts a birthdate (PII) in a map key, that could be a problem.
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.
Suppose for example someday someone puts a birthdate (PII) in a map key, that could be a problem.
Sure -- but my understanding is if it had been preconverted to a string for the key, we'd let it through, because we only hash values. If I'm wrong and we hash keys already, then I guess we should hash it.
Sure, I was trying to maintain the existing behaviour
So do we already throw errors for other types that appear in keys? I am assuming not since much of what comes in is a map[string]interface{}
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.
You're right, we don't hash keys already. I'm perhaps being over-paranoid about failing to hash something we should.
So do we already throw errors for other types that appear in keys? I am assuming not since much of what comes in is a map[string]interface{}
No, I simply meant that right now we already return an error if there's a time in a map key, so it seemed reasonable to maintain that. I'm okay with letting it through unhashed instead, will try that.
Move all the copying/hashing logic from FormatRequest/FormatResponse into the new Hash* funcs. Be a bit more selective about where we use copystructure. HashStructure now modifies in place instead of copying.
time.Time, ignore them, i.e. pass them through unhashed.
audit backends. If they do, they're responsible for setting it up.
part of the merge I had to resolve an impedance mismatch between the new notion of allowing OptMarshaler on a per-data-key basis, and the existing expectation that hashWalker will be invoked on the top-level data map. # Conflicts: # audit/format.go
It turns out that hashWalker can't handle be made to work with raw primitive values, because there's no reflect mechanism to write them in place. The reason we were trying to do that is because we now need to handle the top-level data map ourselves, in order to handle the case where any top-level fields are OptMarshalers. The good news is that we now do a bit less copying as a result. The bad news is that there's some duplication between hashWalker and hashMap.
@jefferai Could you have a look at the revised |
audit/hashstructure.go
Outdated
} else if strutil.StrListContains(nonHMACDataKeys, k) { | ||
newData[k] = v | ||
} else if t, ok := v.(time.Time); ok { | ||
newData[k] = t.Format(time.RFC3339Nano) |
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.
You don't need to call this specifically as marshaling JSON will automatically format time.Time as RFC3339Nano.
It might be better to do this in a type switch where the StrListContains case is moved down into default with what else is in the else
block. Either way works though.
…github.com-ncabatoff/hashicorp/vault into fix-auditing-counter-requests
Compared audit log with master build for |
# Conflicts: # go.sum
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 it's looking pretty good, but would like some other reviews
…-counter-requests
of requests returning the request counters. Modified HashStructure
signature to make clear that hashWalker is really intended to work on
map[string]interface{}. It may be capable of doing more than that,
but it really doesn't handle arbitrary data.
Also removed some dead code, and got rid of the hashWalker.csData field
since it's the same as the last element of csKey.