Add kerberos and SSL support for client libraries
Signed-off-by: Sailaja Polavarapu <spolavarapu@cloudera.com>
diff --git a/intg/src/main/java/org/apache/ranger/RangerClient.java b/intg/src/main/java/org/apache/ranger/RangerClient.java
index 29b2ec0..0e2fe56 100644
--- a/intg/src/main/java/org/apache/ranger/RangerClient.java
+++ b/intg/src/main/java/org/apache/ranger/RangerClient.java
@@ -26,16 +26,21 @@
import org.apache.ranger.admin.client.datatype.RESTResponse;
import org.apache.ranger.plugin.util.GrantRevokeRoleRequest;
import org.apache.ranger.plugin.util.RangerRESTClient;
+import org.apache.hadoop.security.SecureClientLogin;
+import javax.security.auth.Subject;
+import java.security.PrivilegedAction;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.io.IOException;
import java.net.URI;
import java.util.*;
public class RangerClient {
private static final Logger LOG = LoggerFactory.getLogger(RangerClient.class);
+ private static final String AUTH_KERBEROS = "kerberos";
// QueryParams
private static final String PARAM_DAYS = "days";
@@ -133,13 +138,33 @@
private final RangerRESTClient restClient;
+ private boolean isSecureMode = false;
+ private Subject sub = null;
+
+ public RangerClient(String configFile) {
+ RangerClientConfig cfg = new RangerClientConfig(configFile);
+ restClient = new RangerRESTClient(cfg.getURL(), cfg.getSslConfigFile(), new Configuration());
+
+ String authenticationType = cfg.getAuthenticationType();
+ String principal = cfg.getPrincipal();
+ String keytab = cfg.getKeytab();
+
+ if (AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab)) {
+ isSecureMode = true;
+ try {
+ sub = SecureClientLogin.loginUserFromKeytab(principal,keytab);
+ } catch (IOException e) {
+ LOG.error(e.getMessage());
+ }
+ } else LOG.error("Authentication credentials missing/invalid");
+ }
public RangerClient(String hostname, String username, String password) {
restClient = new RangerRESTClient(hostname, "", new Configuration());
restClient.setBasicAuthInfo(username, password);
- }
+ }
public RangerClient(RangerRESTClient restClient) {
this.restClient = restClient;
@@ -380,6 +405,36 @@
callAPI(DELETE_POLICY_DELTAS, queryParams, null, null);
}
+ private ClientResponse invokeREST(API api, Map<String, String> params, Object request) throws RangerServiceException {
+ final ClientResponse clientResponse;
+ try {
+ switch (api.getMethod()) {
+ case HttpMethod.POST:
+ clientResponse = restClient.post(api.getPath(), params, request);
+ break;
+
+ case HttpMethod.PUT:
+ clientResponse = restClient.put(api.getPath(), params, request);
+ break;
+
+ case HttpMethod.GET:
+ clientResponse = restClient.get(api.getPath(), params);
+ break;
+
+ case HttpMethod.DELETE:
+ clientResponse = restClient.delete(api.getPath(), params);
+ break;
+
+ default:
+ LOG.error(api.getMethod() + ": unsupported HTTP method");
+
+ clientResponse = null;
+ }
+ } catch (Exception excp) {
+ throw new RangerServiceException(excp);
+ }
+ return clientResponse;
+ }
private <T> T callAPI(API api, Map<String, String> params, Object request, Class<T> responseType) throws RangerServiceException {
T ret = null;
@@ -397,32 +452,16 @@
final ClientResponse clientResponse;
- try {
- switch (api.getMethod()) {
- case HttpMethod.POST:
- clientResponse = restClient.post(api.getPath(), params, request);
- break;
-
- case HttpMethod.PUT:
- clientResponse = restClient.put(api.getPath(), params, request);
- break;
-
- case HttpMethod.GET:
- clientResponse = restClient.get(api.getPath(), params);
- break;
-
- case HttpMethod.DELETE:
- clientResponse = restClient.delete(api.getPath(), params);
- break;
-
- default:
- LOG.error(api.getMethod() + ": unsupported HTTP method");
-
- clientResponse = null;
- }
- } catch (Exception excp) {
- throw new RangerServiceException(excp);
- }
+ if (isSecureMode) {
+ clientResponse = Subject.doAs(sub, (PrivilegedAction<ClientResponse>) () -> {
+ try {
+ return invokeREST(api,params,request);
+ } catch (RangerServiceException e) {
+ LOG.error(e.getMessage());
+ }
+ return null;
+ });
+ } else clientResponse = invokeREST(api,params,request);
if (LOG.isDebugEnabled()) {
LOG.debug("method={}, path={}, contentType={}, accept={}, httpStatus={}", api.getMethod(), api.getNormalizedPath(), api.getConsumes(), api.getProduces(), (clientResponse != null ? clientResponse.getStatus() : "null"));
diff --git a/intg/src/main/java/org/apache/ranger/RangerClientConfig.java b/intg/src/main/java/org/apache/ranger/RangerClientConfig.java
new file mode 100644
index 0000000..68ef0ff
--- /dev/null
+++ b/intg/src/main/java/org/apache/ranger/RangerClientConfig.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+public class RangerClientConfig {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RangerClientConfig.class);
+
+ private static final String RANGER_ADMIN_URL = "ranger.client.url";
+ private static final String AUTH_TYPE = "ranger.client.authentication.type";
+ private static final String CLIENT_KERBEROS_PRINCIPAL = "ranger.client.kerberos.principal";
+ private static final String CLIENT_KERBEROS_KEYTAB = "ranger.client.kerberos.keytab";
+ private static final String CLIENT_SSL_CONFIG_FILE = "ranger.client.ssl.config.filename";
+
+
+ private final Properties props;
+
+ RangerClientConfig(String configFileName){
+ props = readProperties(configFileName);
+ }
+
+ public Properties readProperties(String fileName) {
+ Properties ret = null;
+ InputStream inStr = null;
+ URL fileURL = null;
+ File f = new File(fileName);
+
+ if (f.exists() && f.isFile() && f.canRead()) {
+ try {
+ inStr = new FileInputStream(f);
+ fileURL = f.toURI().toURL();
+ } catch (FileNotFoundException exception) {
+ LOG.error("Error processing input file:" + fileName + " or no privilege for reading file " + fileName, exception);
+ } catch (MalformedURLException malformedException) {
+ LOG.error("Error processing input file:" + fileName + " cannot be converted to URL " + fileName, malformedException);
+ }
+ } else {
+ fileURL = getClass().getResource(fileName);
+
+ if (fileURL == null && !fileName.startsWith("/")) {
+ fileURL = getClass().getResource("/" + fileName);
+ }
+
+ if (fileURL == null) {
+ fileURL = ClassLoader.getSystemClassLoader().getResource(fileName);
+
+ if (fileURL == null && !fileName.startsWith("/")) {
+ fileURL = ClassLoader.getSystemClassLoader().getResource("/" + fileName);
+ }
+ }
+ }
+
+ if (fileURL != null) {
+ try {
+ inStr = fileURL.openStream();
+
+ Properties prop = new Properties();
+
+ prop.load(inStr);
+
+ ret = prop;
+ } catch (Exception excp) {
+ LOG.error("failed to load properties from file '" + fileName + "'", excp);
+ } finally {
+ if (inStr != null) {
+ try {
+ inStr.close();
+ } catch (Exception excp) {
+ // ignore
+ }
+ }
+ }
+ }
+ return ret;
+ }
+ public String getURL() { return props.getProperty(RANGER_ADMIN_URL); }
+
+ public String getPrincipal(){
+ return props.getProperty(CLIENT_KERBEROS_PRINCIPAL);
+ }
+
+ public String getKeytab(){
+ return props.getProperty(CLIENT_KERBEROS_KEYTAB);
+ }
+
+ public String getSslConfigFile(){
+ return props.getProperty(CLIENT_SSL_CONFIG_FILE);
+ }
+
+ public String getAuthenticationType(){
+ return props.getProperty(AUTH_TYPE);
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index adac120..bc887ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -291,6 +291,7 @@
<id>ranger-examples</id>
<modules>
<module>agents-common</module>
+ <module>agents-cred</module>
<module>intg</module>
<module>ranger-examples</module>
</modules>
diff --git a/ranger-examples/distro/src/main/assembly/sample-client.xml b/ranger-examples/distro/src/main/assembly/sample-client.xml
index ea915a6..cde39f1 100644
--- a/ranger-examples/distro/src/main/assembly/sample-client.xml
+++ b/ranger-examples/distro/src/main/assembly/sample-client.xml
@@ -29,6 +29,7 @@
<include>org.apache.ranger:sample-client</include>
<include>org.apache.ranger:ranger-intg</include>
<include>org.apache.ranger:ranger-plugins-common</include>
+ <include>org.apache.ranger:ranger-plugins-cred</include>
</includes>
<binaries>
<outputDirectory>lib</outputDirectory>
@@ -60,6 +61,7 @@
<include>org.codehaus.jackson:jackson-mapper-asl</include>
<include>org.codehaus.jackson:jackson-xc</include>
<include>org.apache.ranger:ranger-plugins-audit</include>
+ <include>org.apache.htrace:htrace-core4</include>
<include>com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version}</include>
<include>net.java.dev.jna:jna:jar:${jna.version}</include>
<include>net.java.dev.jna:jna-platform:jar:${jna-platform.version}</include>
@@ -79,6 +81,15 @@
</includes>
<fileMode>755</fileMode>
</fileSet>
+<!-- only for testing -->
+ <fileSet>
+ <outputDirectory></outputDirectory>
+ <directory>${project.parent.basedir}/sample-client/conf</directory>
+ <includes>
+ <include>*.properties</include>
+ </includes>
+ <fileMode>755</fileMode>
+ </fileSet>
<fileSet>
<directoryMode>755</directoryMode>
<fileMode>644</fileMode>