Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions pdp/conf.dist/ranger-pdp-site.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,6 @@
<description>PEM-encoded public key for verifying JWT signatures.</description>
</property>

<property>
<name>ranger.pdp.authn.jwt.cookie.name</name>
<value>hadoop-jwt</value>
<description>Cookie name from which a JWT bearer token may be read.</description>
</property>

<property>
<name>ranger.pdp.authn.jwt.audiences</name>
<value/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ private void addAuthFilter(Context ctx) {
authFilterDef.addInitParameter(RangerPdpConstants.PROP_AUTHN_JWT_ENABLED, Boolean.toString(config.isJwtAuthnEnabled()));
authFilterDef.addInitParameter(RangerPdpConstants.PROP_AUTHN_JWT_PROVIDER_URL, config.getJwtProviderUrl());
authFilterDef.addInitParameter(RangerPdpConstants.PROP_AUTHN_JWT_PUBLIC_KEY, config.getJwtPublicKey());
authFilterDef.addInitParameter(RangerPdpConstants.PROP_AUTHN_JWT_COOKIE_NAME, config.getJwtCookieName());
authFilterDef.addInitParameter(RangerPdpConstants.PROP_AUTHN_JWT_AUDIENCES, config.getJwtAudiences());

// Kerberos / SPNEGO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,6 @@ public String getJwtPublicKey() {
return get(RangerPdpConstants.PROP_AUTHN_JWT_PUBLIC_KEY, "");
}

public String getJwtCookieName() {
return get(RangerPdpConstants.PROP_AUTHN_JWT_COOKIE_NAME, "hadoop-jwt");
}

public String getJwtAudiences() {
return get(RangerPdpConstants.PROP_AUTHN_JWT_AUDIENCES, "");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ private RangerPdpConstants() {
public static final String PROP_AUTHN_JWT_ENABLED = PROP_AUTHN_JWT_PREFIX + "enabled";
public static final String PROP_AUTHN_JWT_PROVIDER_URL = PROP_AUTHN_JWT_PREFIX + "provider.url";
public static final String PROP_AUTHN_JWT_PUBLIC_KEY = PROP_AUTHN_JWT_PREFIX + "public.key";
public static final String PROP_AUTHN_JWT_COOKIE_NAME = PROP_AUTHN_JWT_PREFIX + "cookie.name";
public static final String PROP_AUTHN_JWT_AUDIENCES = PROP_AUTHN_JWT_PREFIX + "audiences";

// Kerberos/SPNEGO auth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
/**
* Authenticates requests using a JWT bearer token.
*
* <p>Checks for the token in the {@code Authorization: Bearer <token>} header first,
* then in the configured JWT cookie. Delegates signature verification and expiry/audience
* <p>Checks for the token in the {@code Authorization: Bearer <token>} header
* Delegates signature verification and expiry/audience
* checks to {@link RangerDefaultJwtAuthHandler} from the {@code ranger-authn} module.
*
* <p>Configuration keys (all prefixed with {@code ranger.pdp.authn.jwt.}):
* <ul>
* <li>{@code provider.url} – JWKS endpoint URL (optional if public key is set)
* <li>{@code public.key} – PEM-encoded public key (optional if provider URL is set)
* <li>{@code cookie.name} – JWT cookie name (default: {@code hadoop-jwt})
* <li>{@code audiences} – comma-separated list of accepted audiences (optional)
* </ul>
*/
Expand All @@ -58,7 +57,6 @@ public void init(Properties config) throws Exception {

copyIfPresent(config, RangerPdpConstants.PROP_AUTHN_JWT_PROVIDER_URL, jwtConfig, RangerDefaultJwtAuthHandler.KEY_PROVIDER_URL);
copyIfPresent(config, RangerPdpConstants.PROP_AUTHN_JWT_PUBLIC_KEY, jwtConfig, RangerDefaultJwtAuthHandler.KEY_JWT_PUBLIC_KEY);
copyIfPresent(config, RangerPdpConstants.PROP_AUTHN_JWT_COOKIE_NAME, jwtConfig, RangerDefaultJwtAuthHandler.KEY_JWT_COOKIE_NAME);
copyIfPresent(config, RangerPdpConstants.PROP_AUTHN_JWT_AUDIENCES, jwtConfig, RangerDefaultJwtAuthHandler.KEY_JWT_AUDIENCES);

delegate = new RangerDefaultJwtAuthHandler();
Expand Down
6 changes: 0 additions & 6 deletions pdp/src/main/resources/ranger-pdp-default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,6 @@
<description>PEM-encoded public key for verifying JWT signatures.</description>
</property>

<property>
<name>ranger.pdp.authn.jwt.cookie.name</name>
<value>hadoop-jwt</value>
<description>Cookie name from which a JWT bearer token may be read.</description>
</property>

<property>
<name>ranger.pdp.authn.jwt.audiences</name>
<value/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
*/
package org.apache.ranger.authz.handler;

import org.apache.hadoop.security.authentication.server.AuthenticationToken;

public class RangerAuth {
public static enum AUTH_TYPE {
JWT_JWKS("JWT-JWKS");
Expand All @@ -35,8 +33,8 @@ private AUTH_TYPE(String authType) {
private AUTH_TYPE type;
private boolean isAuthenticated;

public RangerAuth(final AuthenticationToken authenticationToken, AUTH_TYPE type) {
this.userName = authenticationToken.getName();
public RangerAuth(final String userName, AUTH_TYPE type) {
this.userName = userName;
this.isAuthenticated = true;
this.type = type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
package org.apache.ranger.authz.handler.jwt;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.security.authentication.server.AuthenticationToken;
import org.apache.ranger.authz.handler.RangerAuth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.nimbusds.jose.proc.JWSKeySelector;
import com.nimbusds.jose.proc.SecurityContext;
Expand All @@ -38,7 +39,7 @@
*
*/
public class RangerDefaultJwtAuthHandler extends RangerJwtAuthHandler {

private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultJwtAuthHandler.class);
protected static final String AUTHORIZATION_HEADER = "Authorization";
protected static final String DO_AS_PARAMETER = "doAs";

Expand All @@ -57,41 +58,55 @@ public ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(JWSKeySelector<
public RangerAuth authenticate(HttpServletRequest httpServletRequest) {
RangerAuth rangerAuth = null;
String jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
String jwtCookieStr = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;
String doAsUser = httpServletRequest.getParameter(DO_AS_PARAMETER);

AuthenticationToken authenticationToken = authenticate(jwtAuthHeaderStr, jwtCookieStr, doAsUser);

if (authenticationToken != null) {
rangerAuth = new RangerAuth(authenticationToken, RangerAuth.AUTH_TYPE.JWT_JWKS);
// authenticate against the JWT first to get the real (token-verified) user
AuthenticationToken authToken = authenticate(jwtAuthHeaderStr);
String realUser = authToken != null ? authToken.getName() : null;

if (realUser != null) {
String effectiveUser = realUser;

if (StringUtils.isNotBlank(doAsUser)) {
LOG.debug("RangerDefaultJwtAuthHandler.authenticate(): doAs=[{}] requested. isProxyEnabled=[{}]", doAsUser, isProxyEnabled());

if (!isProxyEnabled()) {
LOG.warn("doAs [{}] requested but trusted proxy is not enabled. Ignoring doAs, proceeding with real user [{}].",
doAsUser, effectiveUser);
} else {
LOG.debug("RangerDefaultJwtAuthHandler.authenticate(): Calling authorizeProxyUser: realUser=[{}], doAs=[{}], remoteAddr=[{}]",
realUser, doAsUser, httpServletRequest.getRemoteAddr());
// Check: is realUser authorized to impersonate doAsUser
if (!authorizeProxyUser(realUser, doAsUser, httpServletRequest.getRemoteAddr())) {
LOG.warn("RangerDefaultJwtAuthHandler.authenticate(): doAs=[{}] not authorized for realUser=[{}]. Rejecting.", doAsUser, realUser);
return null;
}
//Checks passed → switch to doAs user
effectiveUser = doAsUser.trim();
LOG.info("JWT doAs authorized: effectiveUser=[{}], realUser=[{}]", effectiveUser, realUser);
}
}
rangerAuth = new RangerAuth(effectiveUser, RangerAuth.AUTH_TYPE.JWT_JWKS);
}

return rangerAuth;
}

public static boolean canAuthenticateRequest(final ServletRequest request) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String jwtAuthHeaderStr = getJwtAuthHeader(httpServletRequest);
String jwtCookieStr = StringUtils.isBlank(jwtAuthHeaderStr) ? getJwtCookie(httpServletRequest) : null;

return shouldProceedAuth(jwtAuthHeaderStr, jwtCookieStr);
return shouldProceedAuth(jwtAuthHeaderStr);
}

public static String getJwtAuthHeader(final HttpServletRequest httpServletRequest) {
return httpServletRequest.getHeader(AUTHORIZATION_HEADER);
}

public static String getJwtCookie(final HttpServletRequest httpServletRequest) {
String jwtCookieStr = null;
Cookie[] cookies = httpServletRequest.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
jwtCookieStr = cookie.getName() + "=" + cookie.getValue();
break;
}
}
}
return jwtCookieStr;
protected boolean isProxyEnabled() {
return false;
}

protected boolean authorizeProxyUser(String realUser, String doAsUser, String remoteAddr) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,12 @@ public abstract class RangerJwtAuthHandler implements RangerAuthHandler {
public static final String TYPE = "ranger-jwt"; // Constant that identifies the authentication mechanism.
public static final String KEY_PROVIDER_URL = "jwks.provider-url"; // JWKS provider URL
public static final String KEY_JWT_PUBLIC_KEY = "jwt.public-key"; // JWT token provider public key
public static final String KEY_JWT_COOKIE_NAME = "jwt.cookie-name"; // JWT cookie name
public static final String KEY_JWT_AUDIENCES = "jwt.audiences";
public static final String JWT_AUTHZ_PREFIX = "Bearer ";

protected List<String> audiences = null;
protected JWKSource<SecurityContext> keySource = null;

protected static String cookieName = "hadoop-jwt";

@Override
public void initialize(final Properties config) throws Exception {
if (LOG.isDebugEnabled()) {
Expand All @@ -84,12 +81,6 @@ public void initialize(final Properties config) throws Exception {
throw new Exception("RangerJwtAuthHandler: Mandatory configs ('jwks.provider-url' & 'jwt.public-key') are missing, must provide atleast one.");
}

// setup custom cookie name if configured
String customCookieName = config.getProperty(KEY_JWT_COOKIE_NAME);
if (customCookieName != null) {
cookieName = customCookieName;
}

// setup audiences if configured
String audiencesStr = config.getProperty(KEY_JWT_AUDIENCES);
if (StringUtils.isNotBlank(audiencesStr)) {
Expand All @@ -101,31 +92,24 @@ public void initialize(final Properties config) throws Exception {
}
}

protected AuthenticationToken authenticate(final String jwtAuthHeader, final String jwtCookie, final String doAsUser) {
protected AuthenticationToken authenticate(final String jwtAuthHeader) {
if (LOG.isDebugEnabled()) {
LOG.debug("===>>> RangerJwtAuthHandler.authenticate()");
}

AuthenticationToken token = null;
if (shouldProceedAuth(jwtAuthHeader, jwtCookie)) {
String serializedJWT = getJWT(jwtAuthHeader, jwtCookie);
if (shouldProceedAuth(jwtAuthHeader)) {
String serializedJWT = getJWT(jwtAuthHeader);

if (StringUtils.isNotBlank(serializedJWT)) {
try {
final SignedJWT jwtToken = SignedJWT.parse(serializedJWT);
boolean valid = validateToken(jwtToken);
if (valid) {
String userName;

if (StringUtils.isNotBlank(doAsUser)) {
userName = doAsUser.trim();
} else {
userName = jwtToken.getJWTClaimsSet().getSubject();
}
String userName = jwtToken.getJWTClaimsSet().getSubject();

if (LOG.isDebugEnabled()) {
LOG.debug("RangerJwtAuthHandler.authenticate(): Issuing AuthenticationToken for user: [{}]", userName);
LOG.debug("RangerJwtAuthHandler.authenticate(): Authentication successful for user [{}] and doAs user is [{}]", jwtToken.getJWTClaimsSet().getSubject(), doAsUser);
}
token = new AuthenticationToken(userName, userName, TYPE);
} else {
Expand All @@ -146,22 +130,13 @@ protected AuthenticationToken authenticate(final String jwtAuthHeader, final Str
return token;
}

protected String getJWT(final String jwtAuthHeader, final String jwtCookie) {
protected String getJWT(final String jwtAuthHeader) {
String serializedJWT = null;

// try to fetch from AUTH header
if (StringUtils.isNotBlank(jwtAuthHeader) && jwtAuthHeader.startsWith(JWT_AUTHZ_PREFIX)) {
serializedJWT = jwtAuthHeader.substring(JWT_AUTHZ_PREFIX.length());
}

// if not found in AUTH header, try to fetch from cookie
if (StringUtils.isBlank(serializedJWT) && StringUtils.isNotBlank(jwtCookie)) {
String[] cookie = jwtCookie.split("=");
if (cookieName.equals(cookie[0])) {
serializedJWT = cookie[1];
}
}

return serializedJWT;
}

Expand Down Expand Up @@ -317,7 +292,7 @@ protected boolean validateExpiration(final SignedJWT jwtToken) {
return valid;
}

public static boolean shouldProceedAuth(final String authHeader, final String jwtCookie) {
return (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(JWT_AUTHZ_PREFIX)) || (StringUtils.isNotBlank(jwtCookie) && jwtCookie.startsWith(cookieName));
public static boolean shouldProceedAuth(final String authHeader) {
return (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(JWT_AUTHZ_PREFIX));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.AuthorizationException;
import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.ranger.authz.handler.RangerAuth;
import org.apache.ranger.authz.handler.jwt.RangerDefaultJwtAuthHandler;
import org.apache.ranger.authz.handler.jwt.RangerJwtAuthHandler;
Expand Down Expand Up @@ -72,11 +76,12 @@ public void initialize() {

config.setProperty(RangerJwtAuthHandler.KEY_PROVIDER_URL, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_AUTH_PROVIDER_URL));
config.setProperty(RangerJwtAuthHandler.KEY_JWT_PUBLIC_KEY, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_PUBLIC_KEY, ""));
config.setProperty(RangerJwtAuthHandler.KEY_JWT_COOKIE_NAME,
PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_COOKIE_NAME, RangerSSOAuthenticationFilter.JWT_COOKIE_NAME_DEFAULT));
config.setProperty(RangerJwtAuthHandler.KEY_JWT_AUDIENCES, PropertiesUtil.getProperty(RangerSSOAuthenticationFilter.JWT_AUDIENCES, ""));

super.initialize(config);

Configuration conf = getProxyuserConfiguration();
ProxyUsers.refreshSuperUserGroupsConfiguration(conf, "ranger.proxyuser.");
} catch (Exception e) {
LOG.error("Failed to initialize Ranger Admin JWT Auth Filter.", e);
}
Expand Down Expand Up @@ -121,6 +126,36 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
}

@Override
protected boolean isProxyEnabled() {
return PropertiesUtil.getBooleanProperty("ranger.authentication.allow.trustedproxy", false);
}

@Override
protected boolean authorizeProxyUser(String realUser, String doAsUser, String remoteAddr) {
try {
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(realUser);
ugi = UserGroupInformation.createProxyUser(doAsUser, ugi);
ProxyUsers.authorize(ugi, remoteAddr);
LOG.debug("RangerJwtAuthFilter.authorizeProxyUser(): ProxyUsers.authorize SUCCEEDED for realUser=[{}], doAs=[{}]",
realUser, doAsUser);
return true;
} catch (AuthorizationException ex) {
LOG.warn("JWT ProxyUsers.authorize failed for doAs=[{}], realUser=[{}]: {}", doAsUser, realUser, ex.getMessage());
return false;
}
}

private Configuration getProxyuserConfiguration() {
Configuration conf = new Configuration(false);
PropertiesUtil.getPropertiesMap().forEach((k, v) -> {
if (k.startsWith("ranger.proxyuser.")) {
conf.set(k, v);
}
});
return conf;
}

@Override
public void destroy() {
// Empty method
Expand Down
Loading