This is an automated email from the ASF dual-hosted git repository. pradeep pushed a commit to branch RANGER-4076_master in repository https://gitbox.apache.org/repos/asf/ranger.git
commit 1cb016ba248b6827e1586aadd7c9b7944c304d00 Author: Pradeep AgrawaL <[email protected]> AuthorDate: Wed Feb 4 14:41:19 2026 +0530 RANGER-5475: add JWT support in RangerRESTClient (#831) --- .../ranger/plugin/util/RangerRESTClient.java | 141 ++++++++++++++++----- .../tagsync/sink/tagadmin/TagAdminRESTSink.java | 2 +- .../process/RangerUgSyncRESTClient.java | 8 +- 3 files changed, 117 insertions(+), 34 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java index 1177f4bc6..742116d9f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java @@ -48,9 +48,11 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; @@ -69,58 +71,63 @@ public class RangerRESTClient { private static final Logger LOG = LoggerFactory.getLogger(RangerRESTClient.class); - public static final String RANGER_PROP_POLICYMGR_URL = "ranger.service.store.rest.url"; - public static final String RANGER_PROP_POLICYMGR_SSLCONFIG_FILENAME = "ranger.service.store.rest.ssl.config.file"; - + public static final String JWT_HEADER_PREFIX = "Bearer "; public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE = "xasecure.policymgr.clientssl.keystore"; - public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type"; public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.keystore.credential.file"; public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_CREDENTIAL_ALIAS = "sslKeyStore"; + public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type"; public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE_DEFAULT = "jks"; - public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE = "xasecure.policymgr.clientssl.truststore"; - public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE = "xasecure.policymgr.clientssl.truststore.type"; public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.truststore.credential.file"; public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS = "sslTrustStore"; + public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE = "xasecure.policymgr.clientssl.truststore.type"; public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks"; - + public static final String RANGER_PROP_POLICYMGR_SSLCONFIG_FILENAME = "ranger.service.store.rest.ssl.config.file"; + public static final String RANGER_PROP_POLICYMGR_URL = "ranger.service.store.rest.url"; + public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); - public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; - private String mUrl; + private final List<String> configuredURLs; private final String mSslConfigFileName; - private String mUsername; - private String mPassword; - private boolean mIsSSL; - private String mKeyStoreURL; + private final String propertyPrefix; + private String jwtAsString; private String mKeyStoreAlias; private String mKeyStoreFile; private String mKeyStoreType; - private String mTrustStoreURL; + private String mKeyStoreURL; + private String mPassword; private String mTrustStoreAlias; private String mTrustStoreFile; private String mTrustStoreType; + private String mTrustStoreURL; + private String mUrl; + private String mUsername; + private boolean mIsSSL; + private int lastKnownActiveUrlIndex; private int mRestClientConnTimeOutMs; private int mRestClientReadTimeOutMs; private int maxRetryAttempts; private int retryIntervalMs; - private int lastKnownActiveUrlIndex; - - private final List<String> configuredURLs; - private volatile Client client; private volatile Client cookieAuthClient; public RangerRESTClient(String url, String sslConfigFileName, Configuration config) { - mUrl = url; - mSslConfigFileName = sslConfigFileName; - configuredURLs = StringUtil.getURLs(mUrl); + this(url, sslConfigFileName, config, getPropertyPrefix(config)); + } + + public RangerRESTClient(String url, String sslConfigFileName, Configuration config, String propertyPrefix) { + mUrl = url; + mSslConfigFileName = sslConfigFileName; + configuredURLs = StringUtil.getURLs(mUrl); + this.propertyPrefix = propertyPrefix; + if (StringUtil.isEmpty(url)) { throw new IllegalArgumentException("Ranger URL is null or empty. Likely caused by incorrect configuration"); } else { setLastKnownActiveUrlIndex((new Random()).nextInt(getConfiguredURLs().size())); } + init(config); } @@ -177,6 +184,14 @@ public void setBasicAuthInfo(String username, String password) { mPassword = password; } + public String getJwtAsString() { + return jwtAsString; + } + + public void setJwtAsString(String jwtAsString) { + this.jwtAsString = jwtAsString; + } + public WebTarget getResource(String relativeUrl) { return getClient().target(getUrl() + relativeUrl); } @@ -227,7 +242,7 @@ public Client getCookieAuthClient() { private Client buildClient(boolean isBasicAuth) { RangerJersey2ClientBuilder.SafeClientBuilder clientBuilder; - ClientConfig config = new ClientConfig(); + ClientConfig clientConfig = new ClientConfig(); if (mIsSSL) { try { @@ -254,17 +269,25 @@ public boolean verify(String urlHostName, SSLSession session) { } // Apply centralized anti-MOXy configuration using the utility - RangerJersey2ClientBuilder.applyAntiMoxyConfiguration(config); + RangerJersey2ClientBuilder.applyAntiMoxyConfiguration(clientConfig); // Set timeouts - config.property(ClientProperties.CONNECT_TIMEOUT, mRestClientConnTimeOutMs); - config.property(ClientProperties.READ_TIMEOUT, mRestClientReadTimeOutMs); + clientConfig.property(ClientProperties.CONNECT_TIMEOUT, mRestClientConnTimeOutMs); + clientConfig.property(ClientProperties.READ_TIMEOUT, mRestClientReadTimeOutMs); // Validate that MOXy prevention is properly configured - RangerJersey2ClientBuilder.validateAntiMoxyConfiguration(config); + RangerJersey2ClientBuilder.validateAntiMoxyConfiguration(clientConfig); - if (isBasicAuth && StringUtils.isNotEmpty(mUsername) && StringUtils.isNotEmpty(mPassword)) { - config.register(new javax.ws.rs.client.ClientRequestFilter() { + if (StringUtils.isNotBlank(jwtAsString)) { + LOG.info("Registering JWT auth header in REST client"); + clientConfig.register(new javax.ws.rs.client.ClientRequestFilter() { + @Override + public void filter(javax.ws.rs.client.ClientRequestContext requestContext) { + requestContext.getHeaders().add("Authorization", JWT_HEADER_PREFIX + jwtAsString); + } + }); + } else if (isBasicAuth && StringUtils.isNotEmpty(mUsername) && StringUtils.isNotEmpty(mPassword)) { + clientConfig.register(new javax.ws.rs.client.ClientRequestFilter() { @Override public void filter(javax.ws.rs.client.ClientRequestContext requestContext) { String authHeader = "Basic " + java.util.Base64.getEncoder().encodeToString((mUsername + ":" + mPassword).getBytes()); @@ -273,7 +296,7 @@ public void filter(javax.ws.rs.client.ClientRequestContext requestContext) { }); } - Client client = clientBuilder.withConfig(config).build(); + Client client = clientBuilder.withConfig(clientConfig).build(); return client; } @@ -322,6 +345,7 @@ private void init(Configuration config) { pluginPropertyPrefix = "ranger.plugin"; } + jwtAsString = fetchJWT(propertyPrefix, config); String username = config.get(pluginPropertyPrefix + ".policy.rest.client.username"); String password = config.get(pluginPropertyPrefix + ".policy.rest.client.password"); @@ -467,14 +491,18 @@ protected SSLContext getSSLContext(KeyManager[] kmList, TrustManager[] tmList) { try { String algo = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algo); + tmf.init((KeyStore) null); + tmList = tmf.getTrustManagers(); } catch (NoSuchAlgorithmException | KeyStoreException | IllegalStateException e) { LOG.error("Unable to get the default SSL TrustStore for the JVM", e); tmList = null; } } + Validate.notNull(tmList, "TrustManager is not specified"); + try { SSLContext sslContext = SSLContext.getInstance(RANGER_SSL_CONTEXT_ALGO_TYPE); @@ -483,6 +511,7 @@ protected SSLContext getSSLContext(KeyManager[] kmList, TrustManager[] tmList) { return sslContext; } catch (NoSuchAlgorithmException e) { LOG.error("SSL algorithm is not available in the environment", e); + throw new IllegalStateException("SSL algorithm is not available in the environment: " + e.getMessage(), e); } catch (KeyManagementException e) { LOG.error("Unable to initials the SSLContext", e); @@ -688,4 +717,58 @@ protected void setKeyStoreType(String mKeyStoreType) { protected void setTrustStoreType(String mTrustStoreType) { this.mTrustStoreType = mTrustStoreType; } + + private static String getPropertyPrefix(Configuration config) { + return (config instanceof RangerPluginConfig) ? ((RangerPluginConfig) config).getPropertyPrefix() : "ranger.plugin"; + } + + private String fetchJWT(String propertyPrefix, Configuration config) { + final String jwtSrc = config.get(propertyPrefix + ".policy.rest.client.jwt.source"); + + if (StringUtils.isNotEmpty(jwtSrc)) { + switch (jwtSrc) { + case "env": + String jwtEnvVar = config.get(propertyPrefix + ".policy.rest.client.jwt.env"); + if (StringUtils.isNotEmpty(jwtEnvVar)) { + String jwt = System.getenv(jwtEnvVar); + if (StringUtils.isNotBlank(jwt)) { + return jwt; + } + } + break; + case "file": + String jwtFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.file"); + if (StringUtils.isNotEmpty(jwtFilePath)) { + File jwtFile = new File(jwtFilePath); + if (jwtFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(jwtFile))) { + String line = null; + while ((line = reader.readLine()) != null) { + if (StringUtils.isNotBlank(line) && !line.startsWith("#")) { + return line; + } + } + } catch (IOException e) { + LOG.error("Failed to read JWT from file: {}", jwtFilePath, e); + } + } + } + break; + case "cred": + String credFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.file"); + String credAlias = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.alias"); + if (StringUtils.isNotEmpty(credFilePath) && StringUtils.isNotEmpty(credAlias)) { + String jwt = RangerCredentialProvider.getInstance().getCredentialString(credFilePath, credAlias); + if (StringUtils.isNotBlank(jwt)) { + return jwt; + } + } + break; + } + } else { + LOG.info("JWT source not configured, proceeding without JWT"); + } + + return null; + } } diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java b/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java index e232e55de..3862b0621 100644 --- a/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java +++ b/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java @@ -87,7 +87,7 @@ public boolean initialize(Properties properties) { LOG.debug("isKerberized={}", isKerberized); if (StringUtils.isNotBlank(restUrl)) { - tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance()); + tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance(), "ranger.tagsync"); if (!isKerberized) { tagRESTClient.setBasicAuthInfo(userName, password); diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java index a6270239b..86818bd43 100644 --- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java +++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java @@ -33,12 +33,12 @@ public class RangerUgSyncRESTClient extends RangerRESTClient { public RangerUgSyncRESTClient(String policyMgrBaseUrls, String ugKeyStoreFile, String ugKeyStoreFilepwd, String ugKeyStoreType, String ugTrustStoreFile, String ugTrustStoreFilepwd, String ugTrustStoreType, String authenticationType, String principal, String keytab, String polMgrUsername, String polMgrPassword) { - super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig()); - - String authKerberos = "kerberos"; + super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig(), "ranger.usersync"); UserGroupSyncConfig userGroupSyncConfig = UserGroupSyncConfig.getInstance(); - if (!(authenticationType != null && authKerberos.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab))) { + boolean isKerberized = "kerberos".equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab); + + if (!isKerberized) { setBasicAuthInfo(polMgrUsername, polMgrPassword); }
