-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add support for mTLS authentication in Arrow Flight client #25179
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
base: master
Are you sure you want to change the base?
Conversation
38df200
to
f2aff7e
Compare
https://github.com/prestodb/presto/actions/runs/15201290385/job/42755765576?pr=25179 @steveburnett I'm not able to figure out why the presto-docs check is failing. Can you help here? Edit : Waiting for fix to be merged here. |
f2aff7e
to
16c6399
Compare
Fix has been merged, and the test is now passing - see #25188 for an example. Rebase your PR to re-run the CI tests and |
7bdfc96
to
bb2cf1d
Compare
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.
Thank you for the fix @elbinpallimalilibm. I have added few comments, please check.
return flightClientSSLCertificate; | ||
} | ||
|
||
@Config("arrow-flight.client-ssl-certificate") |
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 it will be good to add a comment here saying this is needed for mTLS auth.
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.
Added comments.
Optional<InputStream> clientCertificate = Optional.empty(); | ||
Optional<InputStream> clientKey = Optional.empty(); | ||
if (config.getFlightClientSSLCertificate() != null && config.getFlightClientSSLKey() != null) { | ||
clientCertificate = Optional.of(newInputStream(Paths.get(config.getFlightClientSSLCertificate()))); |
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.
trying to understand it a bit better, what happens if the certificate and key are invalid? Lets use a try-catch block here? And maybe add a test case covering this scenario?
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 the cert is invalid, executing a query will give the user an error that the cert is invalid. Added a test case that covers this scenario.
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.
Lets add a try-catch here as well and modify the error message in the test case accordingly. Thank you for adding test case for this though.
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.
The invalid cert exception is thrown only at line 84 FlightClient flightClient = flightClientBuilder.build();
and we might get exception due to other reasons as well from the build
method. So adding a try...catch here will not help in modifying the error message.
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.
Ok, I actually foresee improperly configured client cert/key as a very probable source of error, and hence wanted to cover the scenario with a proper user facing message. Anyways I leave the final decision to you.
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.
Refactored the code to catch errors due to invalid cert or key file. Rethrowing a Presto Exception with a custom message for those scenarios.
@@ -52,7 +52,9 @@ Property Name Description | |||
========================================== ============================================================== | |||
``arrow-flight.server`` Endpoint of the Flight server | |||
``arrow-flight.server.port`` Flight server port | |||
``arrow-flight.server-ssl-certificate`` Pass ssl certificate | |||
``arrow-flight.server-ssl-certificate`` Path to SSL certificate of Flight server | |||
``arrow-flight.client-ssl-certificate`` Path to SSL certificate that Flight client should use |
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: lets modify these to include "in case of mTLS authentication" to make it more clear?
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.
Updated the docs.
} | ||
|
||
@BeforeClass | ||
public void setup() |
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.
The 2 test classes have a lot of duplicate code. Please see if we can use inheritance to avoid this redundant code?
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.
Refactored the test classes.
bb2cf1d
to
60eaa3b
Compare
117c43a
to
4a84c3b
Compare
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.
LGTM! (docs)
Pull branch, local doc build, looks good. Thanks!
4a84c3b
to
83a005f
Compare
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.
Thank you for addressing earlier comments. I have few more minor comments, once addressed, I will approve.
} | ||
|
||
private static DistributedQueryRunner createQueryRunner( | ||
public static DistributedQueryRunner createQueryRunner( |
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.
why are we modifying the access-modifier?
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 need to create query runner with pre-defined catalog properties from the new test classes. That's why the method was changed to public from private.
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.
Does protected or other modifier work instead?
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.
Changed to protected
Optional<InputStream> clientCertificate = Optional.empty(); | ||
Optional<InputStream> clientKey = Optional.empty(); | ||
if (config.getFlightClientSSLCertificate() != null && config.getFlightClientSSLKey() != null) { | ||
clientCertificate = Optional.of(newInputStream(Paths.get(config.getFlightClientSSLCertificate()))); |
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.
Lets add a try-catch here as well and modify the error message in the test case accordingly. Thank you for adding test case for this though.
} | ||
|
||
@BeforeClass | ||
public void setup() |
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.
Does this method need to be public? How about making it package private? Similarly for other methods apart from createQueryRunner
, lets make them as restricted as possible.
} | ||
|
||
@Override | ||
protected Map<String, String> getCatalogProperties() |
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.
The access modifiers here will change based on my above comment.
83a005f
to
1021d94
Compare
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.
Couple more comments, rest looks good
} | ||
|
||
private static DistributedQueryRunner createQueryRunner( | ||
public static DistributedQueryRunner createQueryRunner( |
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.
Does protected or other modifier work instead?
|
||
abstract Map<String, String> getCatalogProperties(); | ||
|
||
protected int getServerPort() |
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.
Lets change this to package private as well?
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.
Changed
3fe0e0e
to
4bbfaa5
Compare
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.
Thank you for patiently addressing all comments. Few refactorings needed, rest looks good
} | ||
catch (Exception e) { | ||
throw new ArrowException(ARROW_FLIGHT_CLIENT_ERROR, "Error creating flight client: " + e.getMessage(), e); | ||
Optional<Throwable> cause = Optional.ofNullable(e.getCause()); | ||
if (cause.filter(c -> c instanceof InvalidKeyException).isPresent()) { |
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 (cause.filter(c -> c instanceof InvalidKeyException).isPresent()) { | |
if (e instanceOf InvalidKeyException) { |
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.
can be simplified like 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.
e
will be instance of IllegalArgumentException
. Inner exception e.getCause
if not null, will be an instance of InvalidKeyException
if (cause.filter(c -> c instanceof InvalidKeyException).isPresent()) { | ||
throw new ArrowException(ARROW_FLIGHT_INVALID_KEY_ERROR, "Error creating flight client, invalid key file: " + e.getMessage(), e); | ||
} | ||
else if (cause.filter(c -> c instanceof CertificateException).isPresent()) { |
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.
ditto
clientCertificate.get().close(); | ||
} | ||
catch (IOException e) { | ||
logger.error("Error closing input stream", e); |
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.
the error messages here are all identical, can be modified in different blocks.
clientCertificate.get().close(); | ||
} | ||
catch (IOException e) { | ||
logger.error("Error closing input stream", e); |
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.
logger.error("Error closing input stream", e); | |
logger.error("Error closing input stream for clientCertificate", e); |
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.
Updated
clientKey.get().close(); | ||
} | ||
catch (IOException e) { | ||
logger.error("Error closing input stream", e); |
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.
ditto
4bbfaa5
to
dd0d61a
Compare
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.
Thank you for the proactive responses. LGTM!
@prestodb/committers this should be good for final pass |
Description
Add support for mTLS authentication in Arrow Flight client
Motivation and Context
If the Flight server has mTLS authentication enabled, then the Flight client should be able to use client certificate and key.
Impact
Test Plan
Added positive and negative test cases against an mTLS enabled Flight server.
Contributor checklist
Release Notes
Please follow release notes guidelines and fill in the release notes below.