-
Notifications
You must be signed in to change notification settings - Fork 73
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
Implement spiffebundle.Bundle type #89
Implement spiffebundle.Bundle type #89
Conversation
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
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.
Thanks, @amartinezfayo !
My biggest concern here is false sharing as X.509 root slices and JWT key maps are passed around. We should come up with consistent behavior that 1) minimizes the number of copies, but 2) prevents false sharing where the method feels like it should produce an independent copy.
I don't think we have to go so far as duplicating the x.509 certificate and JWT keys, just the containers they are in.
v2/bundle/spiffebundle/bundle.go
Outdated
if err := bundle.AddJWTKey(key.KeyID, key.Key); err != nil { | ||
return nil, spiffebundleErr.New("error adding key %d of JWKS: %v", i, errs.Unwrap(err)) | ||
} | ||
case "": |
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 this case can be removed. From the spec:
Clients encountering unknown use values MUST ignore the entire JWK element.
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.
It was not clear to me what was the right thing to do here, since the spec also says "The use parameter MUST be set". I'm ok removing this.
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.
Hmm. That's a good point. @evan2645, what do you think? Spec seems to say "fail if not set, and ignore if set to an unknown 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.
I'm sorry that the spec has caused this confusion. As part of this exchange, suggestions on how to clarify the text (and/or a PR/issue in https://github.com/spiffe/spiffe) would be useful.
Yes, use
MUST be set... but requiring it to be set should not by itself suggest a particular behavior if a validator finds that this is not the case. All SPIFFE bundle producers MUST set the use
claim. SPIFFE bundle validators MUST ignore JWK entries with unknown use claims (including an empty or missing use
claim).
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 we have logging available, this would be a good place to do it.
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.
Is that something allowed by the spec? JWKS RFC only says that the keys array must exist, not that it must have at least one key...
Fair point. It does seem like a pretty strange condition, though...
The jwtbundle.Bundle type assumes that the use is jwt-svid, it does not require any SPIFFE specific parameters when parsing a JWKS document and does not set the use` when marshalling. That's the current behavior in this branch.
Yes I think that is the correct behavior. We should do whatever the JWKS RFC says when parsing jwt-svid bundle. IIRC the RFC says to ignore unknown values. One open question is, in this case, jwt-svid
use value is non unknown, so should be be technically able to load a jwt-svid bundle given a spiffe bundle? Seems like a nicety. But I don't think we should not allow a JWKS bundle to be loaded as a SPIFFE bundle
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.
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.
@amartinezfayo. I think what we've settled with is to ignore keys with missing or unrecognized use
values, i.e. don't enforce conformance as a validator, just do the safe thing (i.e. ignore keys that aren't safe to use).
We don't have a good story for library-level logging for this kind of thing.
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.
Thanks for the clarification @azdagron.
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.
@evan2645 I filed spiffe/spiffe#121 to consider a clarification from the spec perspective.
v2/bundle/spiffebundle/bundle.go
Outdated
x509Roots: x509Bundle.X509Roots(), | ||
} | ||
} | ||
return &Bundle{} |
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.
Unless there is a valid, ergonomic case for receiving a nil bundle, I'd advocate that we let it panic. Either way returning a valid Bundle pointer here seems like the wrong thing to do since it will not have a trust domain and will be unusable. If we decide not to panic, we should return nil
.
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.
Makes sense, I'll change this to panic.
v2/bundle/spiffebundle/bundle.go
Outdated
jwtKeys: jwtBundle.JWTKeys(), | ||
} | ||
} | ||
return &Bundle{} |
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.
Same comment here regarding panicking on a nil bundle...
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.
Makes sense, I'll change this to panic.
v2/bundle/spiffebundle/bundle.go
Outdated
b.mtx.RLock() | ||
defer b.mtx.RUnlock() | ||
|
||
return b.x509Roots |
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.
We should probably copy the slice so we don't accidentally introduce false sharing.
v2/bundle/spiffebundle/bundle.go
Outdated
b.mtx.RLock() | ||
defer b.mtx.RUnlock() | ||
|
||
return b.jwtKeys |
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.
We should probably copy the map so we don't accidentally introduce false sharing.
Raw: []byte("CERT 2"), | ||
} | ||
testCases = []testCase{ | ||
testCase{ |
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.
nit: testCase
type here can be deduced by the compiler and should be omitted
bundleBytesMarshal, err := bundle.Marshal() | ||
require.NoError(t, err) | ||
|
||
// Prase the marshaled bundle |
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.
// Prase the marshaled bundle | |
// Parse the marshaled bundle |
v2/internal/x509util/util.go
Outdated
} | ||
|
||
// CertsEqual checks if two X.509 certificates are equal by comparing its raw bytes | ||
func CertsEqual(a, b *x509.Certificate) bool { |
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'm embarrassed I didn't remember this during the PR for the x509 bundle. This function is provided by the stdlib (x509.Certificate.Equal) and has the same behavior.
v2/bundle/spiffebundle/bundle.go
Outdated
trustDomain spiffeid.TrustDomain | ||
|
||
mtx sync.RWMutex | ||
refreshHint *int64 |
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 things are a little nicer if this is a time.Duration
and the conversion happens during marshalling/unmarshalling instead of inside SetRefreshHint/RefreshHint.
v2/bundle/spiffebundle/bundle.go
Outdated
SequenceNumber uint64 `json:"spiffe_sequence,omitempty"` | ||
RefreshHint int64 `json:"spiffe_refresh_hint,omitempty"` |
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.
These should be pointers so we have explicit behavior on set v.s. unset values. For example, as written, somebody couldn't set a sequence number of zero and have it show up in the marshaled bundle.
SequenceNumber uint64 `json:"spiffe_sequence,omitempty"` | |
RefreshHint int64 `json:"spiffe_refresh_hint,omitempty"` | |
SequenceNumber *uint64 `json:"spiffe_sequence,omitempty"` | |
RefreshHint *int64 `json:"spiffe_refresh_hint,omitempty"` |
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
v2/bundle/spiffebundle/bundle.go
Outdated
func FromX509Bundle(x509Bundle *x509bundle.Bundle) *Bundle { | ||
if x509Bundle != nil { | ||
return &Bundle{ | ||
trustDomain: x509Bundle.TrustDomain(), | ||
x509Roots: x509Bundle.X509Roots(), | ||
} | ||
} | ||
return &Bundle{} | ||
panic(spiffebundleErr.New("no X.509 bundle")) |
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 was thinking more along the lines just trying to use the bundle pointer and letting the runtime panic, like:
func FromX509Bundle(x509Bundle *x509bundle.Bundle) *Bundle {
return &Bundle{
trustDomain: x509bundle.TrustDomain(),
x509Bundle.X509Roots(),
}
}
Doing the explicit nil check with the panic seems like overkill.
v2/bundle/spiffebundle/bundle.go
Outdated
func FromJWTBundle(jwtBundle *jwtbundle.Bundle) *Bundle { | ||
if jwtBundle != nil { | ||
return &Bundle{ | ||
trustDomain: jwtBundle.TrustDomain(), | ||
jwtKeys: jwtBundle.JWTKeys(), | ||
} | ||
} | ||
return &Bundle{} | ||
panic(spiffebundleErr.New("no JWT bundle")) |
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.
same here
v2/bundle/spiffebundle/set.go
Outdated
@@ -1,49 +1,4 @@ | |||
package spiffebundle |
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.
Hmm, is the removal of the set scaffolding accidental?
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.
Oops!
v2/bundle/spiffebundle/bundle.go
Outdated
b.mtx.RLock() | ||
defer b.mtx.RUnlock() | ||
|
||
return copyX509Roots(b.x509Roots) |
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.
copyX509Roots
and copyJWTKey
probably belong in a common package under internal/
... we're going to want to update jwtbundle
and x509bundle
to use them in the respective JWTKeys
and X509Roots
functions.
v2/bundle/spiffebundle/bundle.go
Outdated
if err := bundle.AddJWTKey(key.KeyID, key.Key); err != nil { | ||
return nil, spiffebundleErr.New("error adding key %d of JWKS: %v", i, errs.Unwrap(err)) | ||
} | ||
case "": |
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.
@amartinezfayo. I think what we've settled with is to ignore keys with missing or unrecognized use
values, i.e. don't enforce conformance as a validator, just do the safe thing (i.e. ignore keys that aren't safe to use).
We don't have a good story for library-level logging for this kind of thing.
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
* Implement spiffebundle.Bundle type
* Implement spiffebundle.Bundle type
* Implement spiffebundle.Bundle type
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Fixes: #59