Repository navigation
fix(workloadapi): handle rejected JWT bundle updates in JWT sources - #482
Conversation
maxlambrecht
left a comment
There was a problem hiding this comment.
The parser fix is correct, but #479 remains unresolved through DefaultJwtSource and CachedJwtSource: their onError() callbacks throw WatcherException, which escapes onNext() and permanently cancels the stream. Could you expand this PR so rejected updates preserve existing bundles and subsequent valid updates are applied? I’d prefer addressing this here since it’s part of the reported failure, but a follow-up PR would also work.
… parse
JwtBundle.parse documents JwtBundleException for bundles that cannot be
parsed. If the JWKS contained a key whose type is neither EC nor RSA
(for example an OKP/Ed25519 or an "oct" key),
JwtSignatureAlgorithm.Family.parse threw an IllegalArgumentException that
escaped unwrapped. JwtBundle.load already catches IllegalArgumentException
for the same input, so the two entry points disagreed.
The leak was most harmful on the Workload API watch path.
StreamObservers' JWT bundles observer only catches JwtBundleException
from GrpcConversionUtils.toJwtBundleSet, so the IllegalArgumentException
was thrown out of StreamObserver.onNext. gRPC reacts by cancelling the
call ("CANCELLED: Failed to read message."). CANCELLED is treated as
non-retryable, so the watch ended permanently and later bundle updates
(rotations) were never delivered. With the exception wrapped, the
observer reports "Error processing JWT bundles update" to the watcher
and keeps the stream open for the next update.
Catch IllegalArgumentException in JwtBundle.parse as load does, and
mention unsupported key types in the Javadoc. Tests cover JwtBundle.parse
and GrpcConversionUtils.toJwtBundleSet with a JWKS that contains an OKP
key.
Signed-off-by: NopeCharon <115794254+NopeCharon@users.noreply.github.com>
DefaultJwtSource and CachedJwtSource threw a WatcherException from the JWT bundles watcher's onError. When the error came from a rejected update, the exception escaped StreamObserver.onNext and gRPC cancelled the call, so the watch ended and later bundle rotations were never applied. Only treat errors before the first update as initialization failures: the watcher records the error and init throws it, which also makes newSource fail instead of returning a source without bundles when the first update is rejected asynchronously. After initialization, errors are logged and the current bundles are kept, so the stream stays open and the next valid update replaces them. Signed-off-by: NopeCharon <115794254+NopeCharon@users.noreply.github.com>
f8042e5 to
d4133b0
Compare
|
@maxlambrecht Expanded it here in d4133b0: both sources now only fail on errors before the first update, and after that they log the error and keep the current bundles, so the stream stays open and the next valid update is applied. Added a test that streams an unsupported key update between two valid ones through the fake Workload API. |
maxlambrecht
left a comment
There was a problem hiding this comment.
Thanks, this looks good!
What
JwtBundle.parsenow catches theIllegalArgumentExceptionraised for unsupported JWKS key types (anything other than EC/RSA) and wraps it inJwtBundleException, asJwtBundle.loadalready does. The Javadoc now lists unsupported key types under@throws.Fixes #479
Why
The unwrapped exception broke the documented contract of
parse. On the Workload API watch path it was also thrown out ofStreamObserver.onNext, because the JWT bundles observer only catchesJwtBundleException. gRPC then cancelled the call withCANCELLED: Failed to read message., which is non-retryable, so the watch ended permanently and later bundle updates were never delivered.How tested
JwtBundleTest.testParse_InvalidKeyType_ThrowsJwtBundleExceptionandGrpcConversionUtilsTest.test_toJwtBundleSet_unsupportedKeyType, both using the existingjwks_invalid_keytype.jsonfixture. Both fail without this change.watchJwtBundles. Before: the watcher only receivedCancelling JWT Bundles watch. After: the watcher receivedError processing JWT bundles updatefollowed by the valid update../gradlew :java-spiffe-core:buildon JDK 17.