[ 
https://issues.apache.org/jira/browse/RANGER-5677?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Alex Cruise updated RANGER-5677:
--------------------------------
    Description: 
h2. Symptom

A GET to a plugin download endpoint with valid Basic auth credentials is 
rejected with HTTP 400 {{{}Unauthenticated access not allowed{}}}, while the 
identical request with a trailing slash on the path succeeds:
{code:java}
  $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
      
"http://localhost:6080/service/plugins/policies/download/dev_hive?lastKnownVersion=-1&pluginId=test@cluster-hive";
  400

  $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
      
"http://localhost:6080/service/plugins/policies/download/dev_hive/?lastKnownVersion=-1&pluginId=test@cluster-hive";
  200
  {code}

The 400 body is

{code:json}
{"statusCode":400,"msgDesc":"Unauthenticated access not allowed"}
{code}

Reproduced on current master (3.0.0-SNAPSHOT, dev-support docker environment, 
default configuration). The same asymmetry exists on these endpoints:
||endpoint||no trailing slash||trailing slash||
|/service/plugins/policies/download/\{name}|400|200|
|/service/roles/download/\{name}|400|200|
|/service/tags/download/\{name}|400|200|
|/service/xusers/download/\{name}|400|200|
|/service/plugins/secure/policies/download/ \{name}|200|200|

The {{/secure/}} variants are unaffected because they are not in the bypass 
list described below.
h2. Root cause

{{security-applicationContext.xml}} excludes the plugin endpoints from Spring 
Security entirely:
{code:xml}
  <security:http pattern="/service/gds/download/*" security="none"/>
  <security:http pattern="/service/plugins/policies/download/*" 
security="none"/>
  <security:http pattern="/service/plugins/services/grant/*" security="none"/>
  <security:http pattern="/service/plugins/services/revoke/*" security="none"/>
  <security:http pattern="/service/tags/download/*" security="none"/>
  <security:http pattern="/service/roles/download/*" security="none"/>
  <security:http pattern="/service/xusers/download/*" security="none"/>
  {code}
With {{security="none"}} the entire filter chain is skipped, so an 
Authorization header on a matching request is never processed; the request 
always reaches the REST layer with no user session. 
{{RangerBizUtil.failUnauthenticatedDownloadIfNotAllowed()}} then rejects it 
whenever {{ranger.admin.allow.unauthenticated.download.access}} is {{false}} 
(the default, inherited from {{ranger.admin.allow.unauthenticated.access}} 
which also defaults to {{{}false{}}}).

The trailing slash changes the outcome only because the Ant pattern 
{{/service/plugins/policies/download/*}} does not match 
{{/service/plugins/policies/download/dev_hive/}}. The slashed form falls 
through to the main authenticated filter chain, where Basic auth is processed, 
a session is established, and the request succeeds.

So the observable behavior matrix on a default-configured admin is:
||request||credentials sent||result||
|no slash|yes|400, credentials silently ignored (filter bypass), treated as 
anonymous|
|no slash|no|400, anonymous, unauthenticated download disallowed|
|trailing slash|yes|200, misses the bypass pattern, auth is processed|
|trailing slash|no|401, misses the bypass pattern, challenged by the 
authenticated chain|
h2. Why this is a problem
 * Sending valid credentials to the canonical plugin URL yields a rejection, 
while an undocumented trailing-slash variant of the same URL succeeds. This is 
surprising, hard to diagnose (the 400 suggests a request problem, not an 
auth-routing problem), and effectively undocumented behavior.
 * Any client that authenticates to the non-Kerberos download endpoints with 
Basic auth cannot download policies/tags/roles/userstore at all against an 
admin where unauthenticated download access is disabled — even though it is 
presenting exactly the credentials that would satisfy the check.
 * Whether an endpoint enforces or bypasses security should not depend on 
trailing-slash matching subtleties of the Ant matcher. Note the fix direction 
matters: making the security="none" patterns slash-insensitive would widen the 
anonymous bypass and break the credentialed case in both URL forms; making them 
never match would break credential-less plugins. Neither normalization is 
correct.

h2. Proposed fix

Stop using {{security="none"}} for these service endpoints. Instead, route them 
through a filter chain that processes authentication when credentials are 
present (http-basic / kerberos filters) but permits anonymous access at the web 
tier (e.g. {{ permitAll}} instead of {{isAuthenticated()}}), leaving the 
existing application-level checks 
[{{failUnauthenticatedDownloadIfNotAllowed()}} / 
{{failUnauthenticatedIfNotAllowed()}} ] as the enforcement point, exactly as 
they behave for anonymous callers today.

This preserves behavior for credential-less plugins (anonymous still reaches 
the REST layer and is governed by 
{{ranger.admin.allow.unauthenticated.download.access}}), makes credentialed 
requests work as expected, and removes the trailing-slash sensitivity entirely.

h2. Environment

Reproduced against apache/ranger master (3.0.0-SNAPSHOT) using the dev-support 
docker setup, default configuration, default admin credentials.

  was:
h2. Symptom

A GET to a plugin download endpoint with valid Basic auth credentials is 
rejected with HTTP 400 {{{}Unauthenticated access not allowed{}}}, while the 
identical request with a trailing slash on the path succeeds:
{code:java}
  $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
      
"http://localhost:6080/service/plugins/policies/download/dev_hive?lastKnownVersion=-1&pluginId=test@cluster-hive";
  400

  $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
      
"http://localhost:6080/service/plugins/policies/download/dev_hive/?lastKnownVersion=-1&pluginId=test@cluster-hive";
  200
  {code}

The 400 body is

{code:json}
{"statusCode":400,"msgDesc":"Unauthenticated access not allowed"}
{code}

Reproduced on current master (3.0.0-SNAPSHOT, dev-support docker environment, 
default configuration). The same asymmetry exists on these endpoints:
||endpoint||no trailing slash||trailing slash||
|/service/plugins/policies/download/\{name}|400|200|
|/service/roles/download/\{name}|400|200|
|/service/tags/download/\{name}|400|200|
|/service/xusers/download/\{name}|400|200|
|/service/plugins/secure/policies/download/ \{name}|200|200|

The {{/secure/}} variants are unaffected because they are not in the bypass 
list described below.
h2. Root cause

{{security-applicationContext.xml}} excludes the plugin endpoints from Spring 
Security entirely:
{code:xml}
  <security:http pattern="/service/gds/download/*" security="none"/>
  <security:http pattern="/service/plugins/policies/download/*" 
security="none"/>
  <security:http pattern="/service/plugins/services/grant/*" security="none"/>
  <security:http pattern="/service/plugins/services/revoke/*" security="none"/>
  <security:http pattern="/service/tags/download/*" security="none"/>
  <security:http pattern="/service/roles/download/*" security="none"/>
  <security:http pattern="/service/xusers/download/*" security="none"/>
  {code}
With {{security="none"}} the entire filter chain is skipped, so an 
Authorization header on a matching request is never processed; the request 
always reaches the REST layer with no user session. 
{{RangerBizUtil.failUnauthenticatedDownloadIfNotAllowed()}} then rejects it 
whenever {{ranger.admin.allow.unauthenticated.download.access}} is {{false}} 
(the default, inherited from {{ranger.admin.allow.unauthenticated.access}} 
which also defaults to {{{}false{}}}).

The trailing slash changes the outcome only because the Ant pattern 
{{/service/plugins/policies/download/*}} does not match 
{{/service/plugins/policies/download/dev_hive/}}. The slashed form falls 
through to the main authenticated filter chain, where Basic auth is processed, 
a session is established, and the request succeeds.

So the observable behavior matrix on a default-configured admin is:
||request||credentials sent||result||
|no slash|yes|400, credentials silently ignored (filter bypass), treated as 
anonymous|
|no slash|no|400, anonymous, unauthenticated download disallowed|
|trailing slash|yes|200, misses the bypass pattern, auth is processed|
|trailing slash|no|401, misses the bypass pattern, challenged by the 
authenticated chain|
h2. Why this is a problem
 * Sending valid credentials to the canonical plugin URL yields a rejection, 
while an undocumented trailing-slash variant of the same URL succeeds. This is 
surprising, hard to diagnose (the 400 suggests a request problem, not an 
auth-routing problem), and effectively undocumented behavior.
 * Any client that authenticates to the non-Kerberos download endpoints with 
Basic auth cannot download policies/tags/roles/userstore at all against an 
admin where unauthenticated download access is disabled — even though it is 
presenting exactly the credentials that would satisfy the check.
 * Whether an endpoint enforces or bypasses security should not depend on 
trailing-slash matching subtleties of the Ant matcher. Note the fix direction 
matters: making the security="none" patterns slash-insensitive would widen the 
anonymous bypass and break the credentialed case in both URL forms; making them 
never match would break credential-less plugins. Neither normalization is 
correct.

h2. Proposed fix

Stop using {{security="none" }} for these service endpoints. Instead, route 
them through a filter chain that processes authentication when credentials are 
present (http-basic / kerberos filters) but permits anonymous access at the web 
tier (e.g. {{ permitAll}} instead of {{isAuthenticated()}}), leaving the 
existing application-level checks 
[{{failUnauthenticatedDownloadIfNotAllowed()}} / 
{{failUnauthenticatedIfNotAllowed()}} ] as the enforcement point, exactly as 
they behave for anonymous callers today.

This preserves behavior for credential-less plugins (anonymous still reaches 
the REST layer and is governed by 
{{ranger.admin.allow.unauthenticated.download.access}}), makes credentialed 
requests work as expected, and removes the trailing-slash sensitivity entirely.

h2. Environment

Reproduced against apache/ranger master (3.0.0-SNAPSHOT) using the dev-support 
docker setup, default configuration, default admin credentials.


> Plugin REST endpoints silently ignore supplied credentials due to 
> security="none" filter bypass; authentication outcome depends on a trailing 
> slash
> ---------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: RANGER-5677
>                 URL: https://issues.apache.org/jira/browse/RANGER-5677
>             Project: Ranger
>          Issue Type: Bug
>          Components: admin
>    Affects Versions: 3.0.0
>            Reporter: Alex Cruise
>            Priority: Major
>
> h2. Symptom
> A GET to a plugin download endpoint with valid Basic auth credentials is 
> rejected with HTTP 400 {{{}Unauthenticated access not allowed{}}}, while the 
> identical request with a trailing slash on the path succeeds:
> {code:java}
>   $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
>       
> "http://localhost:6080/service/plugins/policies/download/dev_hive?lastKnownVersion=-1&pluginId=test@cluster-hive";
>   400
>   $ curl -s -o /dev/null -w "%{http_code}\n" -u admin:rangerR0cks! \
>       
> "http://localhost:6080/service/plugins/policies/download/dev_hive/?lastKnownVersion=-1&pluginId=test@cluster-hive";
>   200
>   {code}
> The 400 body is
> {code:json}
> {"statusCode":400,"msgDesc":"Unauthenticated access not allowed"}
> {code}
> Reproduced on current master (3.0.0-SNAPSHOT, dev-support docker environment, 
> default configuration). The same asymmetry exists on these endpoints:
> ||endpoint||no trailing slash||trailing slash||
> |/service/plugins/policies/download/\{name}|400|200|
> |/service/roles/download/\{name}|400|200|
> |/service/tags/download/\{name}|400|200|
> |/service/xusers/download/\{name}|400|200|
> |/service/plugins/secure/policies/download/ \{name}|200|200|
> The {{/secure/}} variants are unaffected because they are not in the bypass 
> list described below.
> h2. Root cause
> {{security-applicationContext.xml}} excludes the plugin endpoints from Spring 
> Security entirely:
> {code:xml}
>   <security:http pattern="/service/gds/download/*" security="none"/>
>   <security:http pattern="/service/plugins/policies/download/*" 
> security="none"/>
>   <security:http pattern="/service/plugins/services/grant/*" security="none"/>
>   <security:http pattern="/service/plugins/services/revoke/*" 
> security="none"/>
>   <security:http pattern="/service/tags/download/*" security="none"/>
>   <security:http pattern="/service/roles/download/*" security="none"/>
>   <security:http pattern="/service/xusers/download/*" security="none"/>
>   {code}
> With {{security="none"}} the entire filter chain is skipped, so an 
> Authorization header on a matching request is never processed; the request 
> always reaches the REST layer with no user session. 
> {{RangerBizUtil.failUnauthenticatedDownloadIfNotAllowed()}} then rejects it 
> whenever {{ranger.admin.allow.unauthenticated.download.access}} is {{false}} 
> (the default, inherited from {{ranger.admin.allow.unauthenticated.access}} 
> which also defaults to {{{}false{}}}).
> The trailing slash changes the outcome only because the Ant pattern 
> {{/service/plugins/policies/download/*}} does not match 
> {{/service/plugins/policies/download/dev_hive/}}. The slashed form falls 
> through to the main authenticated filter chain, where Basic auth is 
> processed, a session is established, and the request succeeds.
> So the observable behavior matrix on a default-configured admin is:
> ||request||credentials sent||result||
> |no slash|yes|400, credentials silently ignored (filter bypass), treated as 
> anonymous|
> |no slash|no|400, anonymous, unauthenticated download disallowed|
> |trailing slash|yes|200, misses the bypass pattern, auth is processed|
> |trailing slash|no|401, misses the bypass pattern, challenged by the 
> authenticated chain|
> h2. Why this is a problem
>  * Sending valid credentials to the canonical plugin URL yields a rejection, 
> while an undocumented trailing-slash variant of the same URL succeeds. This 
> is surprising, hard to diagnose (the 400 suggests a request problem, not an 
> auth-routing problem), and effectively undocumented behavior.
>  * Any client that authenticates to the non-Kerberos download endpoints with 
> Basic auth cannot download policies/tags/roles/userstore at all against an 
> admin where unauthenticated download access is disabled — even though it is 
> presenting exactly the credentials that would satisfy the check.
>  * Whether an endpoint enforces or bypasses security should not depend on 
> trailing-slash matching subtleties of the Ant matcher. Note the fix direction 
> matters: making the security="none" patterns slash-insensitive would widen 
> the anonymous bypass and break the credentialed case in both URL forms; 
> making them never match would break credential-less plugins. Neither 
> normalization is correct.
> h2. Proposed fix
> Stop using {{security="none"}} for these service endpoints. Instead, route 
> them through a filter chain that processes authentication when credentials 
> are present (http-basic / kerberos filters) but permits anonymous access at 
> the web tier (e.g. {{ permitAll}} instead of {{isAuthenticated()}}), leaving 
> the existing application-level checks 
> [{{failUnauthenticatedDownloadIfNotAllowed()}} / 
> {{failUnauthenticatedIfNotAllowed()}} ] as the enforcement point, exactly as 
> they behave for anonymous callers today.
> This preserves behavior for credential-less plugins (anonymous still reaches 
> the REST layer and is governed by 
> {{ranger.admin.allow.unauthenticated.download.access}}), makes credentialed 
> requests work as expected, and removes the trailing-slash sensitivity 
> entirely.
> h2. Environment
> Reproduced against apache/ranger master (3.0.0-SNAPSHOT) using the 
> dev-support docker setup, default configuration, default admin credentials.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to