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

jwt: calculate kid from public key #174

Merged
merged 1 commit into from
Feb 24, 2025
Merged

Conversation

omertuc
Copy link
Member

@omertuc omertuc commented Feb 20, 2025

Background

Recert resigns JWTs found in the cluster. The kid field in the Kubernetes JWT header is a Base64URL encoded hash of the public key used to sign the JWT. The kid used by verifiers (e.g. kube-apiserver) to quickly identify the key used to sign the JWT, instead of brute-forcing.

Issue

See https://issues.redhat.com/browse/OCPBUGS-49972

The current implementation uses the SHA256 hash of the private key to calculate the kid. This is inconsistent with the Kubernetes implementation which uses the public key to calculate the kid.

In recent versions of Kubernetes, a kid mismatch leads to "unauthorized" errors, which eventually leads to a collapse of the cluster.

Solution

This commit changes the kid calculation to use the SHA256 hash of the public key instead of the private key, so that it becomes more consistent with Kubernetes

Backwards compatibility

Seeds processed with older versions of recert that don't contain this fix should still work if "rejuvenated" by newer versions of recert which do contain this fix. This is because the new version will simply overwrite the kid field with the correct value.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a method for generating a "kid" field for JWT headers, enhancing token identification.
    • Added a test module to validate the new JWT key ID functionality.
  • Refactor

    • Streamlined import statements for better code clarity.
    • Improved variable naming for better readability in the resigning logic.
    • Updated error handling messages for clearer feedback.

Copy link

coderabbitai bot commented Feb 20, 2025

Walkthrough

This pull request modifies the JWT verification and signing processes in the crypto utility module. It simplifies import statements and enhances variable naming for clarity in the resign function. The method of obtaining the key ID is updated to use a direct call to private_key.jwt_key_id(), and error messages are improved for better understanding. Additionally, a new method jwt_key_id is introduced in the SigningKey struct, which computes a Base64URL-encoded SHA256 hash of the public key, accompanied by a new test module.

Changes

File(s) Summary
src/cluster_crypto/.../jwt.rs • Simplified import structure by removing self reference.
• Updated variable names in resign for clarity.
• Changed key ID calculation method to use private_key.jwt_key_id().
• Improved error handling messages.
• Updated variable names for writing PEM files.
src/cluster_crypto/.../crypto_utils.rs • Added method jwt_key_id to SigningKey struct for generating a "kid" field.
• Introduced a test module with test_kid function to validate the "kid" calculation.
• Updated imports to include new encoding functionalities and types.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant R as Resign Function
    participant D as DER Encoder
    participant H as SHA256 Hasher

    C->>R: Call resign(jwt, private_key)
    R->>R: Construct SubjectPublicKeyInfo from RSA key
    R->>D: Encode public key info to DER
    D-->>R: Return DER bytes
    R->>H: Compute SHA-256 hash of DER bytes
    H-->>R: Return hash (kid)
    R->>C: Return new signed JWT with updated kid
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

openshift-ci bot commented Feb 20, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: omertuc

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@omertuc omertuc requested review from mresvanis and eranco74 and removed request for tsorya and rauhersu February 20, 2025 15:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/cluster_crypto/crypto_utils/jwt.rs (1)

94-111: Robust rework of KID calculation.

This block correctly constructs and DER-encodes the SubjectPublicKeyInfo before hashing it to compute the KID. It aligns well with Kubernetes’ recommended approach, improving interoperability. A few considerations:

  1. Error handling: Although the code properly handles encoding and hashing errors, consider verifying that public_key_data() never returns invalid data (e.g., empty).
  2. Readability: For clarity, consider extracting the DER-encoding (lines 102–109) into a helper function.
  3. Testing: It would be beneficial to include a test that validates the derived KID matches Kubernetes expectations.

Would you like a helper function or test stub to ensure consistent KID generation?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0c9a4e and 53a95ec.

📒 Files selected for processing (1)
  • src/cluster_crypto/crypto_utils/jwt.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/cluster_crypto/crypto_utils/jwt.rs (2)

7-7: Import looks good.

The introduction of BitString from bcder is necessary for constructing the subject public key info.


9-9: Appropriate new imports.

Adding rfc5280, InMemorySigningKeyPair, KeyAlgorithm, and Sign aligns with the need to structure and sign the public key info.

@omertuc
Copy link
Member Author

omertuc commented Feb 20, 2025

/hold writing a test

@omertuc
Copy link
Member Author

omertuc commented Feb 20, 2025

/unhold wrote the test

# Background

Recert resigns JWTs found in the cluster. The kid field in the
Kubernetes JWT header is a Base64URL encoded hash of the public key used
to sign the JWT. The kid used by verifiers (e.g. kube-apiserver) to
quickly identify the key used to sign the JWT, instead of brute-forcing.

# Issue

Likely to solve issues.redhat.com/browse/OCPBUGS-49972

The current implementation uses the SHA256 hash of the private key to
calculate the kid. This is inconsistent with the Kubernetes
implementation [1] which uses the public key to calculate the kid.

In recent versions of Kubernetes, a kid mismatch leads to "unauthorized"
errors, which eventually leads to a collapse of the cluster.

# Solution

This commit changes the kid calculation to use the SHA256 hash of the
public key instead of the private key, so that it becomes more
consistent with Kubernetes

# Backwards compatibility

Seeds processed with older versions of recert that don't contain this
fix should still work if "rejuvenated" by newer versions of recert which
do contain this fix. This is because the new version will simply
overwrite the `kid` field with the correct value.

# References

[1] https://github.com/openshift/kubernetes/blob/6fdacf04117cef54a0babd0945e8ef87d0f9461d/pkg/serviceaccount/jwt.go#L92-L112

Signed-off-by: Omer Tuchfeld <omer@tuchfeld.dev>
@omertuc
Copy link
Member Author

omertuc commented Feb 20, 2025

/unhold

@omertuc
Copy link
Member Author

omertuc commented Feb 20, 2025

@coderabbitai full review

Copy link

coderabbitai bot commented Feb 20, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/cluster_crypto/crypto_utils/jwt.rs (1)

98-98: Fix typo in error message.

The error message contains a typo: "headern not objecT" should be "header not object".

-        .context("headern not objecT")?
+        .context("header not object")?
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0c9a4e and 70ad55c.

📒 Files selected for processing (2)
  • src/cluster_crypto/crypto_utils.rs (4 hunks)
  • src/cluster_crypto/crypto_utils/jwt.rs (3 hunks)
🔇 Additional comments (3)
src/cluster_crypto/crypto_utils/jwt.rs (1)

83-94: LGTM! Improved key ID calculation.

The code now correctly uses the jwt_key_id method to calculate the key ID from the public key, aligning with Kubernetes implementation.

src/cluster_crypto/crypto_utils.rs (2)

28-46: LGTM! Correct implementation of JWT key ID calculation.

The implementation correctly follows the Kubernetes approach:

  1. Derives public key from private key
  2. Encodes it in PKIX DER format
  3. Calculates SHA256 hash
  4. Base64URL encodes the hash

The code is well-documented with a reference to the original Kubernetes implementation.


305-401: LGTM! Comprehensive test coverage.

The test module provides excellent validation by:

  1. Including a test key (safely obfuscated)
  2. Comparing against reference Go implementation
  3. Documenting the Go code used to generate the expected value

Copy link
Collaborator

@eranco74 eranco74 left a comment

Choose a reason for hiding this comment

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

/lgtm

@omertuc
Copy link
Member Author

omertuc commented Feb 24, 2025

/retest

@omertuc
Copy link
Member Author

omertuc commented Feb 24, 2025

/override ?

Copy link

openshift-ci bot commented Feb 24, 2025

@omertuc: /override requires failed status contexts, check run or a prowjob name to operate on.
The following unknown contexts/checkruns were given:

  • ?

Only the following failed contexts/checkruns were expected:

  • CodeRabbit
  • ci/prow/4.14-images
  • ci/prow/4.15-images
  • ci/prow/4.16-images
  • ci/prow/4.17-images
  • ci/prow/baremetalds-sno-recert-cluster-rename
  • ci/prow/cargo-check
  • ci/prow/cargo-clippy
  • ci/prow/cargo-test
  • ci/prow/e2e-aws-ovn-single-node-recert-parallel
  • ci/prow/e2e-aws-ovn-single-node-recert-serial
  • ci/prow/images
  • pull-ci-rh-ecosystem-edge-recert-main-4.14-images
  • pull-ci-rh-ecosystem-edge-recert-main-4.15-images
  • pull-ci-rh-ecosystem-edge-recert-main-4.16-images
  • pull-ci-rh-ecosystem-edge-recert-main-4.17-images
  • pull-ci-rh-ecosystem-edge-recert-main-baremetalds-sno-recert-cluster-rename
  • pull-ci-rh-ecosystem-edge-recert-main-cargo-check
  • pull-ci-rh-ecosystem-edge-recert-main-cargo-clippy
  • pull-ci-rh-ecosystem-edge-recert-main-cargo-test
  • pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-parallel
  • pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-serial
  • pull-ci-rh-ecosystem-edge-recert-main-images
  • tide

If you are trying to override a checkrun that has a space in it, you must put a double quote on the context.

In response to this:

/override ?

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@omertuc
Copy link
Member Author

omertuc commented Feb 24, 2025

/override pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-serial

@omertuc
Copy link
Member Author

omertuc commented Feb 24, 2025

/override pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-parallel

Copy link

openshift-ci bot commented Feb 24, 2025

@omertuc: Overrode contexts on behalf of omertuc: ci/prow/e2e-aws-ovn-single-node-recert-serial

In response to this:

/override pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-serial

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Copy link

openshift-ci bot commented Feb 24, 2025

@omertuc: Overrode contexts on behalf of omertuc: ci/prow/e2e-aws-ovn-single-node-recert-parallel

In response to this:

/override pull-ci-rh-ecosystem-edge-recert-main-e2e-aws-ovn-single-node-recert-parallel

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@omertuc omertuc merged commit 67a41bb into rh-ecosystem-edge:main Feb 24, 2025
13 of 14 checks passed
@omertuc
Copy link
Member Author

omertuc commented Feb 24, 2025

/cherry-pick release-4.18

@openshift-cherrypick-robot

@omertuc: new pull request created: #175

In response to this:

/cherry-pick release-4.18

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Copy link

openshift-ci bot commented Feb 24, 2025

@omertuc: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-aws-ovn-single-node-recert-parallel 70ad55c link unknown /test e2e-aws-ovn-single-node-recert-parallel
ci/prow/e2e-aws-ovn-single-node-recert-serial 70ad55c link unknown /test e2e-aws-ovn-single-node-recert-serial

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants