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

feat: Support CAS server certificate authority to validate instance connections. #2060

Merged
merged 1 commit into from
Sep 18, 2024

Conversation

hessjcg
Copy link
Collaborator

@hessjcg hessjcg commented Aug 26, 2024

For Cloud SQL instances with CAS enabled, the connector will use the certificate chain of trust reported by the
SQL Admin API to validate the instance connection.

@hessjcg hessjcg requested a review from a team as a code owner August 26, 2024 17:59
@hessjcg hessjcg marked this pull request as draft August 26, 2024 21:11
@hessjcg hessjcg marked this pull request as ready for review September 3, 2024 20:00
Copy link
Collaborator

@jackwotherspoon jackwotherspoon left a comment

Choose a reason for hiding this comment

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

I believe we want to update the PSC check now that dnsName can be for CAS-based instances as well as PSC just like Go did: https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/pull/850/files#diff-5097a430f11cfd2c79c874367e2f96cf048d9e47112b01f7650d961d8a677ff8R104

Not sure where exactly in the Java code base this would be, maybe here?

Comment on lines 39 to 45
InstanceMetadata(
Map<IpType, String> ipAddrs,
List<Certificate> instanceCaCertificates,
boolean casManagedCertificate,
String dnsName) {
this.ipAddrs = ipAddrs;
this.instanceCaCertificates = instanceCaCertificates;
this.casManagedCertificate = casManagedCertificate;
this.dnsName = dnsName;
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we really need two constructors for InstanceMetadata ? seems like we should be able to just use one...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. I consolidated this to 1 constructor.

@hessjcg hessjcg force-pushed the cas-support branch 2 times, most recently from 408d64e to 668aa58 Compare September 10, 2024 20:19
@hessjcg
Copy link
Collaborator Author

hessjcg commented Sep 10, 2024

Java has this requirement covered without needing additional code:

I believe we want to update the PSC check now that dnsName can be for CAS-based instances as well as PSC just like Go did: https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/pull/850/files#diff-5097a430f11cfd2c79c874367e2f96cf048d9e47112b01f7650d961d8a677ff8R104

The Java connector calls SSLSocket.connect() at Connector.java:118 with either the instance IP or the PSC domain name. When the instance is a CAS instance, the TLS context will be configured to use the built-in logic to check certificate trust. When SSLSocket.connect() is called with the PSC domain name, the built-in TLS logic will check the certificate's SAN records as expected.

return delegate;
}

// Otherwise, Use a custom trust manager factory that checks the CN against the instance name
InstanceCheckingTrustManagerFactory tmf =
new InstanceCheckingTrustManagerFactory(instanceName, delegate);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like the dnsName is not actually used to verify the server identity? Can we enhance this part and pass in the dnsName to verify it? I think we can use getSubjectAlternativeNames() to get the SAN fields. Thanks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We should not need to explicitly program the TrustManagerFactory to check the subjectAlternateName records, that behavior should be built in. I will add some unit tests with correct and incorrect server certificates to demonstrate this behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 we need to actually check the dnsName against the SAN fields

Copy link
Collaborator Author

@hessjcg hessjcg Sep 13, 2024

Choose a reason for hiding this comment

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

Fixed. I also moved some of the code from this into 2 other PRs for clarity.

  1. chore(refactor): Add CAS related fields to the InstanceMetadata. #2067
  2. (this one) feat: Support CAS server certificate authority to validate instance connections
  3. test: Add integration test for public IP instance with CAS enabled. #2066

@hessjcg hessjcg force-pushed the cas-support branch 2 times, most recently from 0ef54c0 to 689b27f Compare September 13, 2024 19:20
@hessjcg hessjcg changed the base branch from main to cas-support-refactor September 13, 2024 19:24
@hessjcg hessjcg requested a review from feng-zhe September 13, 2024 19:25
@hessjcg hessjcg force-pushed the cas-support branch 2 times, most recently from 26e21b7 to fe76396 Compare September 13, 2024 19:37
@hessjcg hessjcg changed the title feat: Support CAS server certificate authority to validate instance connections feat: Support CAS server certificate authority to validate instance connections. Sep 13, 2024
}
throw new CertificateException(
"Server certificate does not contain expected name '"
+ instanceMetadata.getInstanceName()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use instanceMetadata.getDnsName() instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.


for (List item : sanAsn1Field) {
Integer type = (Integer) item.get(0);
if (type == 0 || type == 2) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a comment here for the 0 and 2? I assume that 0 is the otherName and 2 is the dNSName according to the RFC 5280.

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 are correct. I added a comment to clarify.

Comment on lines 123 to 124
SSLParameters sslParams = new SSLParameters();
socket.setSSLParameters(sslParams);
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you add a comment in the code here explaining this new piece?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed. This was unnecessary.

@@ -367,7 +374,7 @@ private Certificate fetchEphemeralCertificate(
// Parse the certificate from the response.
Certificate ephemeralCertificate;
try {
ephemeralCertificate = createCertificate(response.getEphemeralCert().getCert());
ephemeralCertificate = createCertificate(response.getEphemeralCert().getCert()).get(0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add a comment in the code about which cert .get(0) is gettiing...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 435 to 436
// If this is CAS-managed, then use the HTTPS algorithm to check the subjectAlternateNames
// against the certificate.
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we not also be doing this for PSC? It also has the DNS name as a SAN entry...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, this is wrong. I'm removing it. It won't work because unlike Go we cannot override the hostname passed to Socket.connect().

I built custom hostname verification logic in InstanceCheckingTrustManager.

The built-in HTTPS hostname verification algorithm won't work because the connector cannot override the hostname when Socket.connect(addr) is called with an IP Address.

* checks that the Subject CN field contains the Cloud SQL instance ID.
* does custom hostname checking in accordance with these rules:
*
* <p>If an instanceMetadata.casEnabled Check that the subjectAlternativeNames in the server
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: this sentence seems off to me, reads a bit funny...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. I fixed it.

@hessjcg hessjcg force-pushed the cas-support-refactor branch from a9815d8 to 12de4d4 Compare September 16, 2024 15:59
@hessjcg hessjcg force-pushed the cas-support branch 2 times, most recently from 647e946 to f2fefc0 Compare September 16, 2024 16:10
@hessjcg hessjcg force-pushed the cas-support-refactor branch 2 times, most recently from 87a8a3f to 096f8e7 Compare September 16, 2024 16:11
@hessjcg hessjcg requested a review from a team as a code owner September 16, 2024 16:11
@hessjcg hessjcg force-pushed the cas-support-refactor branch from 096f8e7 to 4ee3acc Compare September 17, 2024 17:54
Copy link
Collaborator

@jackwotherspoon jackwotherspoon left a comment

Choose a reason for hiding this comment

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

LGTM ✅

Base automatically changed from cas-support-refactor to main September 18, 2024 19:34
@hessjcg hessjcg merged commit 4332ffc into main Sep 18, 2024
17 checks passed
@hessjcg hessjcg deleted the cas-support branch September 18, 2024 19:50
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.

3 participants