[
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)