kirby zhou created RANGER-3619:
----------------------------------
Summary: REST API should return 403 when authenticated client is
not allowed to access API.
Key: RANGER-3619
URL: https://issues.apache.org/jira/browse/RANGER-3619
Project: Ranger
Issue Type: Bug
Components: Ranger
Affects Versions: 2.2.0, 3.0.0
Reporter: kirby zhou
REST API should return 403-Forbidden when authenticated client is not allowed
to access API to avoid crash Ranger Clients.
Now, some API returns 401-Unauthorized instead of 403-Forbidden when client is
already passed authentication but now allowed to do something.
In general, this will not cause any serious problems. However, there is a flaw
in the SPNEGO protocol implementation of Java HTTPUrlConnection. It causes the
Client to throw an unexpected exception. This will trouble the operators and
developers.
Let me show you how it happens:
For example:
The RangerAdminClient inside KMS want to access API
"/service/secure/policies/download", but the principal is not in the allowlist.
# RangerAdminClient is based on Jersey-Client
# JerseyClient sends a HTTP-request to Ranger Service without authentication
information
# Tomcat/Spring inside Ranger returns 401 with HTTP header
“WWW-Authentication: Neogotiate”
# JerseyClient sends request again with Kerberos/SPNEGO authentication tokens.
# Tomcat/Spring inside Ranger accept the authentication, then call
ServiceRest::getSecureServicePoliciesIfUpdated to reply the API calling.
# ServiceRest::getSecureServicePoliciesIfUpdated checks allowlist of “kms
service”, and refuse client with 401.
# Tomcat/Spring inside Ranger returns 401 with HTTP header
“WWW-Authentication: Neogotiate….” for notifying RangerAdminClient the
authentication is passed.
Now, there is a malformed state. HTTP-status code told client authenticate is
failed, but HTTP header told client authentication is passed.
In the RangerAdminClient side,
# sun.net.www.protocol.http.HttpURLConnection.getInputStream0() see the second
401.
# 'inNegotiate' = true, so it is in the progress of _Negotiate._
# It checks that: if "WWW-Authenticate: Negotiate" exist then disable
negotiate for following code to avoid try {_}Negotiate once again{_}.
# But "WWW-Authenticate: Negotiate xczsd324…" does not the rule above.
# So HttpURLConnection calls AuthenticationInfo.sendHeaders to generate a new
request header.
# Wow, Null exception happens.
# Logs "ERROR RangerAdminRESTClient - Error getting policies; Received NULL
response!!. secureMode=true, user=… (auth:KERBEROS), serviceName=kmsdev"
# Log of KMS: "ERROR RangerAdminRESTClient - Failed to get response, Error is
: java.lang.RuntimeException: java.lang.NullPointerException"
This log makes admin confused.
{code:java}
//ServiceRest::getServicePoliciesIfUpdated
if (isAllowed) {
//...
} else {
httpCode = HttpServletResponse.SC_UNAUTHORIZED;
}
{code}
{code:java}
// sun.net.www.protocol.http.HttpURLConnection.getInputStream0()
// Read comments labeled "Failed Negotiate" for details.
boolean dontUseNegotiate = false;
Iterator<String> iter = responses.multiValueIterator("WWW-Authenticate");
while (iter.hasNext()) {
String value = iter.next().trim();
if (value.equalsIgnoreCase("Negotiate") ||
value.equalsIgnoreCase("Kerberos")) {
if (!inNegotiate) {
inNegotiate = true;
} else {
dontUseNegotiate = true;
doingNTLM2ndStage = false;
serverAuthentication = null;
}
break;
}
}
/**
* Failed Negotiate
*
* In some cases, the Negotiate auth is supported for the
* remote host but the negotiate process still fails (For
* example, if the web page is located on a backend server
* and delegation is needed but fails). The authentication
* process will start again, and we need to detect this
* kind of failure and do proper fallback (say, to NTLM).
*
* In order to achieve this, the inNegotiate flag is set
* when the first negotiate challenge is met (and reset
* if authentication is finished). If a fresh new negotiate
* challenge (no parameter) is found while inNegotiate is
* set, we know there's a failed auth attempt recently.
* Here we'll ignore the header line so that fallback
* can be practiced.
*
* inNegotiateProxy is for proxy authentication.
*/
{code}
--
This message was sent by Atlassian Jira
(v8.20.1#820001)