[ https://issues.apache.org/jira/browse/RANGER-3619?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17519248#comment-17519248 ]
kirby zhou commented on RANGER-3619: ------------------------------------ Simple test with dual-instance HA done, nothing special happens. > 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: 3.0.0, 2.2.0 > Reporter: kirby zhou > Priority: Major > > 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)