Skip to content

feat(grpc): add certificate rotation support for mTLS flow - #1

Draft
agrawalradhika-cell wants to merge 1 commit into
mainfrom
grpc-support
Draft

feat(grpc): add certificate rotation support for mTLS flow#1
agrawalradhika-cell wants to merge 1 commit into
mainfrom
grpc-support

Conversation

@agrawalradhika-cell

Copy link
Copy Markdown
Owner

This change introduces support for X.509 certificate rotation in gRPC connections by utilizing the certificate_configuration_fetcher parameter in grpcio.

Fixes: b/494318856\nSee also: go/cert-rotation-in-pythonsdk-for-x509

This change introduces support for X.509 certificate rotation in gRPC connections by utilizing the certificate_configuration_fetcher parameter in grpcio.

Fixes: b/494318856\nSee also: go/cert-rotation-in-pythonsdk-for-x509
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the gRPC transport layer to support X.509 certificate rotation for mTLS connections. By integrating gRPC's certificate_configuration_fetcher mechanism, the system can dynamically refresh client certificates and private keys without requiring connection re-establishment, improving security posture and operational efficiency for long-lived mTLS connections. The changes are designed to gracefully fall back to existing certificate handling if the rotation feature is not available in the gRPCio version.

Highlights

  • Certificate Rotation Support: Introduced a new helper function _get_ssl_channel_credentials to centralize the logic for creating SSL channel credentials, supporting certificate rotation via certificate_configuration_fetcher when available in grpcio.
  • Integration with mTLS Flow: Updated the secure_authorized_channel function and the _ssl_credentials property to leverage the new _get_ssl_channel_credentials function, enabling dynamic mTLS certificate rotation.
  • Robust Fallback Mechanism: The new certificate handling logic includes a graceful fallback to the traditional certificate_chain and private_key parameters if the certificate_configuration_fetcher is not supported by the installed grpcio version or if a TypeError occurs during its use.
  • Comprehensive Unit Testing: Added new unit tests for _get_ssl_channel_credentials covering various scenarios, including successful fetcher usage, fallback behavior, and cases where SslCertificateConfiguration is not available, ensuring the reliability of the new feature.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for X.509 certificate rotation for mTLS in gRPC connections. The implementation adds a new helper function _get_ssl_channel_credentials to handle creating credentials with rotation support if the installed grpcio version allows it, while providing a fallback for older versions. The changes are well-structured and include comprehensive tests for the new functionality. I have one suggestion to improve the exception handling to ensure consistency between the certificate rotation and fallback paths.

Comment on lines 354 to 363
try:
_, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
self._ssl_credentials = grpc.ssl_channel_credentials(
certificate_chain=cert, private_key=key
)

def client_cert_callback():
_, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
return cert, key

self._ssl_credentials = _get_ssl_channel_credentials(client_cert_callback)
except exceptions.ClientCertError as caught_exc:
new_exc = exceptions.MutualTLSChannelError(caught_exc)
raise new_exc from caught_exc

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The current try...except block only catches ClientCertError during the initial channel creation, specifically when the fallback mechanism in _get_ssl_channel_credentials is used. If certificate rotation is active, the client_cert_callback is called later by gRPC, and any ClientCertError raised at that point will not be caught and converted to a MutualTLSChannelError.

Moving the exception handling inside the client_cert_callback ensures consistent error handling for both scenarios (with and without certificate rotation).

            def client_cert_callback():
                try:
                    _, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
                    return cert, key
                except exceptions.ClientCertError as caught_exc:
                    new_exc = exceptions.MutualTLSChannelError(caught_exc)
                    raise new_exc from caught_exc

            self._ssl_credentials = _get_ssl_channel_credentials(client_cert_callback)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for X.509 certificate rotation in gRPC by using the certificate_configuration_fetcher feature of grpcio. The implementation correctly feature-detects and provides a fallback for older versions of grpcio. The changes are well-tested. I found one potential issue in the implementation of the client_cert_callback where it doesn't correctly handle the case where client credentials can't be loaded, which could lead to unhandled exceptions. I've suggested a fix for this.

Comment on lines +357 to +358
_, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
return cert, key

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The client_cert_callback is ignoring the first element of the tuple returned by _mtls_helper.get_client_ssl_credentials(), which is a boolean indicating whether credentials were successfully obtained. If get_client_ssl_credentials() returns (False, None, None, None), this callback will return (None, None), which will likely cause an unhandled error deep inside grpcio when it tries to use these None values. The callback should check this boolean and raise a google.auth.exceptions.ClientCertError if credentials are not found. This will ensure that errors are handled gracefully within this library.

Suggested change
_, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
return cert, key
has_creds, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
if not has_creds:
raise exceptions.ClientCertError(
"Failed to load client credentials for mTLS."
)
return cert, key

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.

1 participant