[ 
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}
  $ 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 {"statusCode":400,"msgDesc":"Unauthenticated access not 
allowed"}.

  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 therefore 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}
  $ 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 \{{{"statusCode":400,"msgDesc":"Unauthenticated access not 
allowed"}}}.

  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 therefore 
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}
>   $ 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 {"statusCode":400,"msgDesc":"Unauthenticated access not 
> allowed"}.
>   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 therefore 
> 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