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

Add support for hashing time.Time within slices, which unbreaks auditing #6767

Merged
merged 19 commits into from
Jul 2, 2019

Conversation

ncabatoff
Copy link
Collaborator

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.

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.
@@ -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) {
Copy link
Member

@jefferai jefferai May 21, 2019

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.

Copy link
Collaborator Author

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.

Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

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.

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)
Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds reasonable.

// 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")
Copy link
Member

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?

Copy link
Collaborator Author

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.

Copy link
Member

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{}

Copy link
Collaborator Author

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.

ncabatoff and others added 8 commits May 21, 2019 15:23
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.
@ncabatoff
Copy link
Collaborator Author

@jefferai Could you have a look at the revised hashMap in hashstructure.go please? I had to duplicate some of what hashwalker does because I couldn't find a good way to make it handle OptMarshaler, and because hashwalker's approach of in-place modification precludes it working with primitives as direct inputs.

} else if strutil.StrListContains(nonHMACDataKeys, k) {
newData[k] = v
} else if t, ok := v.(time.Time); ok {
newData[k] = t.Format(time.RFC3339Nano)
Copy link
Member

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.

@ncabatoff
Copy link
Collaborator Author

Compared audit log with master build for vault kv put action and verified that the same fields get HMAC'd after these changes. This PR is ready to be reviewed again.

Copy link
Member

@jefferai jefferai left a 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

audit/hashstructure.go Outdated Show resolved Hide resolved
@ncabatoff ncabatoff added this to the 1.2 milestone Jul 2, 2019
@ncabatoff ncabatoff added beta and removed beta labels Jul 2, 2019
@ncabatoff ncabatoff merged commit 056e90b into master Jul 2, 2019
@ncabatoff ncabatoff deleted the fix-auditing-counter-requests branch July 23, 2019 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants