Fix HTTP OPTIONS request auth handling#5638
Conversation
|
I merged #5615 too early, so reopening in this PR. This addresses the comments left on the last PR. |
| |`druid.escalator.type`|String|Type of the Escalator that should be used for internal Druid communications. This Escalator must use an authentication scheme that is supported by an Authenticator in `druid.auth.authenticationChain`.|"noop"|no| | ||
| |`druid.auth.authorizers`|JSON List of Strings|List of Authorizer type names |["allowAll"]|no| | ||
| |`druid.auth.unsecuredPaths`| List of Strings|List of paths for which security checks will not be performed. All requests to these paths will be allowed.|[]|no| | ||
| |`druid.auth.allowUnauthenticatedHttpOptions`|Boolean|If true, skip authentication checks for HTTP OPTIONS requests. This is needed for certain use cases, such as supporting CORS pre-flight requests. Note that disabling authentication checks for OPTIONS requests will allow unauthenticated users to determine what Druid endpoints are valid (by checking if the OPTIONS request returns a 200 instead of 404), so the authentication checks should not be disabled unless necessary. |false|no| |
There was a problem hiding this comment.
Will this leak any info beyond what is available in the docs?
Is there any difference in the response of an OPTIONS request for e.g. /druid/coordinator/v1/datasources/{dataSource}/intervals for a real datasource vs a not-real datasource? Or other endpoints like this that include dataSource names in the resource?
There was a problem hiding this comment.
The "does resource that appears in a URL exist?" leakage won't happen, the responses for the OPTIONS requests are the same:
Valid datasource:
curl -i -X OPTIONS http://localhost:8081/druid/coordinator/v1/datasources/wikiticker/intervals
HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 00:45:34 GMT
Content-Type: application/vnd.sun.wadl+xml
Allow: OPTIONS,HEAD,GET
Last-Modified: Fri, 13 Apr 2018 00:21:06 UTC
Vary: Accept
Content-Length: 711
Server: Jetty(9.3.19.v20170502)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><application xmlns="http://wadl.dev.java.net/2009/02"><doc xmlns:jersey="http://jersey.java.net/" jersey:generatedBy="Jersey: 1.19.3 10/24/2016 03:43 PM"/><grammars/><resources base="http://localhost:8081/"><resource path="druid/coordinator/v1/datasources/wikiticker/intervals"><method id="getSegmentDataSourceIntervals" name="GET"><request><param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="simple" style="query" type="xs:string"/><param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="full" style="query" type="xs:string"/></request><response><representation mediaType="application/json"/></response></method></resource></resources></application>
Invalid datasource:
curl -i -X OPTIONS http://localhost:8081/druid/coordinator/v1/datasources/FAKE_DATASOURCES/intervals
HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 00:45:42 GMT
Content-Type: application/vnd.sun.wadl+xml
Allow: OPTIONS,HEAD,GET
Last-Modified: Fri, 13 Apr 2018 00:21:06 UTC
Vary: Accept
Content-Length: 717
Server: Jetty(9.3.19.v20170502)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><application xmlns="http://wadl.dev.java.net/2009/02"><doc xmlns:jersey="http://jersey.java.net/" jersey:generatedBy="Jersey: 1.19.3 10/24/2016 03:43 PM"/><grammars/><resources base="http://localhost:8081/"><resource path="druid/coordinator/v1/datasources/FAKE_DATASOURCES/intervals"><method id="getSegmentDataSourceIntervals" name="GET"><request><param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="simple" style="query" type="xs:string"/><param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="full" style="query" type="xs:string"/></request><response><representation mediaType="application/json"/></response></method></resource></resources></application>
There was a problem hiding this comment.
The unauthenticated OPTIONS requests would leak information about what features are enabled in some cases.
For example, it's possible to determine whether JSON SQL queries are enabled:
Enabled:
curl -i -X OPTIONS http://localhost:8082/druid/v2/sql
HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 01:20:29 GMT
Content-Type: application/vnd.sun.wadl+xml
Allow: OPTIONS,POST
Last-Modified: Fri, 13 Apr 2018 01:16:58 UTC
Vary: Accept
Content-Length: 502
Server: Jetty(9.3.19.v20170502)
Disabled (No POST method, shows the query DELETE method from /druid/v2/{id} instead)
curl -i -X OPTIONS http://localhost:8082/druid/v2/sql
HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 01:21:08 GMT
Content-Type: application/vnd.sun.wadl+xml
Allow: OPTIONS,DELETE
Last-Modified: Fri, 13 Apr 2018 01:21:07 UTC
Vary: Accept
Content-Length: 444
Server: Jetty(9.3.19.v20170502)
There was a problem hiding this comment.
Other examples would be extensions that expose some endpoints when enabled, e.g. druid-basic-security:
With extension enabled:
curl -i -X OPTIONS http://localhost:8081/druid-ext/basic-security/authorization/
HTTP/1.1 200 OK
Date: Fri, 13 Apr 2018 01:15:49 GMT
Content-Type: application/vnd.sun.wadl+xml
Allow: OPTIONS
Last-Modified: Fri, 13 Apr 2018 01:13:22 UTC
Vary: Accept
Content-Length: 4440
Server: Jetty(9.3.19.v20170502)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><application xmlns="http://wadl.dev.java.net/2009/02"><doc xmlns:jersey="http://jersey.java.net/" jersey:generatedBy="Jersey: 1.19.3 10/24/2016 03:43 PM"/><grammars/><resources base="http://localhost:8081/"><resource path="druid-ext/basic-security/authorization/"><resource path="/db/{authorizerName}/users/{userName}">
...
With extension disabled:
curl -i -X OPTIONS http://localhost:8081/druid-ext/basic-security/authorization/
HTTP/1.1 404 Not Found
Date: Fri, 13 Apr 2018 01:17:30 GMT
Content-Length: 0
Server: Jetty(9.3.19.v20170502)
There was a problem hiding this comment.
In that case I'd suggest wording like this,
If true, skip authentication checks for HTTP OPTIONS requests. This is needed for certain use cases, such as supporting CORS pre-flight requests. Note that disabling authentication checks for OPTIONS requests will allow unauthenticated users to determine what Druid endpoints are valid (by checking if the OPTIONS request returns a 200 instead of 404), so enabling this option may reveal information about server configuration, including information about what extensions are loaded (if those extensions add endpoints).
There was a problem hiding this comment.
Sounds good, updated to use that wording
| // so this filter catches all OPTIONS requests and authorizes them. | ||
| if (HttpMethod.OPTIONS.equals(httpReq.getMethod())) { | ||
| if (allowUnauthenticatedHttpOptions) { | ||
| httpReq.setAttribute( |
There was a problem hiding this comment.
This will override authentication information if it is present in the request. But ideally we'd retain that if it's present. Is there a way to do that?
There was a problem hiding this comment.
Yes, I moved AllowOptionsResourceFilter so that it runs after all the authenticators, and had it check for existing authentication results
| AuthConfig.DRUID_AUTHENTICATION_RESULT, | ||
| new AuthenticationResult(AuthConfig.ALLOW_ALL_NAME, AuthConfig.ALLOW_ALL_NAME, null) | ||
| ); | ||
| } |
There was a problem hiding this comment.
I think it would be be better to else deny-this-request here rather than doing nothing and letting the catch-all filter catch it.
There was a problem hiding this comment.
Added an error response here
| this.authenticatorChain = authenticationChain; | ||
| this.authorizers = authorizers; | ||
| this.unsecuredPaths = unsecuredPaths == null ? Collections.emptyList() : unsecuredPaths; | ||
| this.allowUnauthenticatedHttpOptions = allowUnauthenticatedHttpOptions == null |
There was a problem hiding this comment.
This can be simplified by making the type boolean instead of Boolean. Jackson defaults to filling those in as false.
54707dc to
3ee07aa
Compare
|
@jon-wei are you planning to do the backport? |
* Fix HTTP OPTIONS request auth handling * PR comment * More PR comments * Fix * PR comment
…he#5654) * Fix HTTP OPTIONS request auth handling (apache#5638) * Fix HTTP OPTIONS request auth handling * PR comment * More PR comments * Fix * PR comment * Compile fixes * Compile
* Fix HTTP OPTIONS request auth handling * PR comment * More PR comments * Fix * PR comment
Fixes #5588
This PR sets the AUTHORIZATION_CHECKED flag to true for all HTTP OPTIONS requests. This is handled in a new servlet filter (Druid has no explicit OPTIONS method handlers on its endpoints).
It seems that CORS pre-flight OPTIONS requests do not generally contain credentials headers:
So a
disableHttpOptionsAuthenticationconfig is added to disable authentication checks on OPTIONS requests (authentication is required by default) to support CORS use cases.