You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pe...@apache.org on 2018/12/29 08:56:44 UTC
[ranger] branch master updated: RANGER-2170:Ranger supports plugin
to enable, monitor and manage Elasticsearch
This is an automated email from the ASF dual-hosted git repository.
pengjianhua pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 39a53b6 RANGER-2170:Ranger supports plugin to enable, monitor and manage Elasticsearch
39a53b6 is described below
commit 39a53b6c448145c2a68839233281671f754e519b
Author: zhangqiang2 <zh...@zte.com.cn>
AuthorDate: Thu Dec 13 14:46:14 2018 +0800
RANGER-2170:Ranger supports plugin to enable, monitor and manage Elasticsearch
Signed-off-by: peng.jianhua <pe...@zte.com.cn>
---
agents-common/scripts/enable-agent.sh | 12 +-
.../apache/ranger/plugin/client/BaseClient.java | 2 +-
.../plugin/store/EmbeddedServiceDefsUtil.java | 8 +-
.../ranger-servicedef-elasticsearch.json | 196 ++++++++++++++
plugin-elasticsearch/.gitignore | 1 +
.../conf/ranger-elasticsearch-audit-changes.cfg | 65 +++++
.../conf/ranger-elasticsearch-audit.xml | 271 ++++++++++++++++++++
.../conf/ranger-elasticsearch-security-changes.cfg | 28 ++
.../conf/ranger-elasticsearch-security.xml | 83 ++++++
.../conf/ranger-policymgr-ssl-changes.cfg | 21 ++
plugin-elasticsearch/conf/ranger-policymgr-ssl.xml | 49 ++++
plugin-elasticsearch/pom.xml | 69 +++++
plugin-elasticsearch/scripts/install.properties | 140 ++++++++++
.../authorizer/RangerElasticsearchAuthorizer.java | 148 +++++++++++
.../elasticsearch/RangerServiceElasticsearch.java | 88 +++++++
.../elasticsearch/client/ElasticsearchClient.java | 282 +++++++++++++++++++++
.../client/ElasticsearchResourceMgr.java | 102 ++++++++
.../elasticsearch/privilege/IndexPrivilege.java | 55 ++++
.../privilege/IndexPrivilegeUtils.java | 102 ++++++++
pom.xml | 34 +++
ranger-elasticsearch-plugin-shim/.gitignore | 1 +
.../conf/plugin-descriptor.properties | 65 +++++
.../conf/plugin-security.policy | 39 +++
ranger-elasticsearch-plugin-shim/pom.xml | 66 +++++
.../RangerElasticsearchAccessControl.java | 35 +++
.../authorizer/RangerElasticsearchAuthorizer.java | 119 +++++++++
.../plugin/RangerElasticsearchPlugin.java | 122 +++++++++
.../action/filter/RangerSecurityActionFilter.java | 82 ++++++
.../plugin/authc/user/UsernamePasswordToken.java | 103 ++++++++
.../rest/filter/RangerSecurityRestFilter.java | 73 ++++++
.../elasticsearch/plugin/utils/RequestUtils.java | 126 +++++++++
src/main/assembly/admin-web.xml | 12 +
src/main/assembly/plugin-elasticsearch.xml | 197 ++++++++++++++
33 files changed, 2793 insertions(+), 3 deletions(-)
diff --git a/agents-common/scripts/enable-agent.sh b/agents-common/scripts/enable-agent.sh
index ce0dc8c..b4194a7 100755
--- a/agents-common/scripts/enable-agent.sh
+++ b/agents-common/scripts/enable-agent.sh
@@ -210,6 +210,8 @@ elif [ "${HCOMPONENT_NAME}" = "sqoop" ]; then
HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/server/lib
elif [ "${HCOMPONENT_NAME}" = "kylin" ]; then
HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/tomcat/webapps/kylin/WEB-INF/lib
+elif [ "${HCOMPONENT_NAME}" = "elasticsearch" ]; then
+ HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/plugins
fi
HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/conf
@@ -219,7 +221,7 @@ if [ "${HCOMPONENT_NAME}" = "solr" ]; then
install_owner=`ls -ld | cut -f 3 -d " "`
echo "INFO: Creating $HCOMPONENT_CONF_DIR"
mkdir -p $HCOMPONENT_CONF_DIR
- echo "INFO: Changing ownership of $HCOMPONENT_CONF_DIR to $install_owner"
+ echo "INFO: Changing ownership of $HCOMPONENT_CONF_DIR to $install_owner"
chown $install_owner:$install_owner $HCOMPONENT_CONF_DIR
fi
elif [ "${HCOMPONENT_NAME}" = "kafka" ]; then
@@ -230,6 +232,14 @@ elif [ "${HCOMPONENT_NAME}" = "yarn" ]; then
HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/etc/hadoop
elif [ "${HCOMPONENT_NAME}" = "sqoop" ]; then
HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/conf
+elif [ "${HCOMPONENT_NAME}" = "elasticsearch" ]; then
+ HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/config/ranger-elasticsearch-plugin
+ if [ ! -d $HCOMPONENT_CONF_DIR ]; then
+ echo "INFO: Creating $HCOMPONENT_CONF_DIR"
+ mkdir -p $HCOMPONENT_CONF_DIR
+ echo "INFO: Changing ownership of $HCOMPONENT_CONF_DIR to $CFG_OWNER_INF"
+ chown $CFG_OWNER_INF $HCOMPONENT_CONF_DIR
+ fi
fi
HCOMPONENT_ARCHIVE_CONF_DIR=${HCOMPONENT_CONF_DIR}/.archive
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java
index e654f2b..ed002ef 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java
@@ -37,7 +37,7 @@ public abstract class BaseClient {
private static final String DEFAULT_NAME_RULE = "DEFAULT";
- private static final String DEFAULT_ERROR_MESSAGE = " You can still save the repository and start creating "
+ protected static final String DEFAULT_ERROR_MESSAGE = " You can still save the repository and start creating "
+ "policies, but you would not be able to use autocomplete for "
+ "resource names. Check ranger_admin.log for more info.";
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/EmbeddedServiceDefsUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/EmbeddedServiceDefsUtil.java
index 118af1f..110f763 100755
--- a/agents-common/src/main/java/org/apache/ranger/plugin/store/EmbeddedServiceDefsUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/EmbeddedServiceDefsUtil.java
@@ -48,7 +48,7 @@ public class EmbeddedServiceDefsUtil {
// following servicedef list should be reviewed/updated whenever a new embedded service-def is added
- private static final String DEFAULT_BOOTSTRAP_SERVICEDEF_LIST = "tag,hdfs,hbase,hive,kms,knox,storm,yarn,kafka,solr,atlas,nifi,nifi-registry,sqoop,kylin";
+ private static final String DEFAULT_BOOTSTRAP_SERVICEDEF_LIST = "tag,hdfs,hbase,hive,kms,knox,storm,yarn,kafka,solr,atlas,nifi,nifi-registry,sqoop,kylin,elasticsearch";
private static final String PROPERTY_SUPPORTED_SERVICE_DEFS = "ranger.supportedcomponents";
private Set<String> supportedServiceDefs;
public static final String EMBEDDED_SERVICEDEF_TAG_NAME = "tag";
@@ -68,6 +68,7 @@ public class EmbeddedServiceDefsUtil {
public static final String EMBEDDED_SERVICEDEF_SQOOP_NAME = "sqoop";
public static final String EMBEDDED_SERVICEDEF_KYLIN_NAME = "kylin";
public static final String EMBEDDED_SERVICEDEF_ABFS_NAME = "abfs";
+ public static final String EMBEDDED_SERVICEDEF_ELASTICSEARCH_NAME = "elasticsearch";
public static final String PROPERTY_CREATE_EMBEDDED_SERVICE_DEFS = "ranger.service.store.create.embedded.service-defs";
@@ -102,6 +103,7 @@ public class EmbeddedServiceDefsUtil {
private RangerServiceDef sqoopServiceDef;
private RangerServiceDef kylinServiceDef;
private RangerServiceDef abfsServiceDef;
+ private RangerServiceDef elasticsearchServiceDef;
private RangerServiceDef tagServiceDef;
@@ -145,6 +147,7 @@ public class EmbeddedServiceDefsUtil {
sqoopServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_SQOOP_NAME);
kylinServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_KYLIN_NAME);
abfsServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_ABFS_NAME);
+ elasticsearchServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_ELASTICSEARCH_NAME);
// Ensure that tag service def is updated with access types of all service defs
store.updateTagServiceDefForAccessTypes();
@@ -211,6 +214,9 @@ public class EmbeddedServiceDefsUtil {
return getId(kylinServiceDef);
}
+ public long getElasticsearchServiceDefId() {
+ return getId(elasticsearchServiceDef);
+ }
public long getTagServiceDefId() { return getId(tagServiceDef); }
public long getWasbServiceDefId() { return getId(wasbServiceDef); }
diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json
new file mode 100644
index 0000000..b055bf8
--- /dev/null
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json
@@ -0,0 +1,196 @@
+{
+ "id":16,
+ "name": "elasticsearch",
+ "implClass": "org.apache.ranger.services.elasticsearch.RangerServiceElasticsearch",
+ "label": "ELASTICSEARCH",
+ "description": "ELASTICSEARCH",
+ "guid": "c0682ba7-7052-4c9c-a30e-84ccd5d98457",
+ "resources":
+ [
+ {
+ "itemId": 1,
+ "name": "index",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": true,
+ "lookupSupported": true,
+ "recursiveSupported": false,
+ "excludesSupported": false,
+ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": { "wildCard":true, "ignoreCase":true},
+ "validationRegEx":"",
+ "validationMessage": "",
+ "uiHint":"",
+ "label": "Index",
+ "description": "Elasticsearch Index"
+ }
+ ],
+
+ "accessTypes":
+ [
+ {
+ "itemId": 1,
+ "name": "all",
+ "label": "all",
+ "impliedGrants":
+ [
+ "monitor",
+ "manage",
+ "view_index_metadata",
+ "read",
+ "read_cross_cluster",
+ "index",
+ "create",
+ "delete",
+ "write",
+ "delete_index",
+ "create_index",
+ "indices_put",
+ "indices_search_shards",
+ "indices_bulk",
+ "indices_index"
+ ]
+ },
+
+ {
+ "itemId": 2,
+ "name": "monitor",
+ "label": "monitor"
+ },
+
+ {
+ "itemId": 3,
+ "name": "manage",
+ "label": "manage",
+ "impliedGrants":
+ [
+ "monitor"
+ ]
+ },
+
+ {
+ "itemId": 4,
+ "name": "view_index_metadata",
+ "label": "view_index_metadata",
+ "impliedGrants":
+ [
+ "indices_search_shards"
+ ]
+ },
+
+ {
+ "itemId": 5,
+ "name": "read",
+ "label": "read"
+ },
+
+ {
+ "itemId": 6,
+ "name": "read_cross_cluster",
+ "label": "read_cross_cluster",
+ "impliedGrants":
+ [
+ "indices_search_shards"
+ ]
+ },
+
+ {
+ "itemId": 7,
+ "name": "index",
+ "label": "index",
+ "impliedGrants":
+ [
+ "indices_put",
+ "indices_bulk",
+ "indices_index"
+ ]
+ },
+
+ {
+ "itemId": 8,
+ "name": "create",
+ "label": "create",
+ "impliedGrants":
+ [
+ "indices_put",
+ "indices_bulk",
+ "indices_index"
+ ]
+ },
+
+ {
+ "itemId": 9,
+ "name": "delete",
+ "label": "delete",
+ "impliedGrants":
+ [
+ "indices_bulk"
+ ]
+ },
+
+ {
+ "itemId": 10,
+ "name": "write",
+ "label": "write",
+ "impliedGrants":
+ [
+ "indices_put"
+ ]
+ },
+
+ {
+ "itemId": 11,
+ "name": "delete_index",
+ "label": "delete_index"
+ },
+
+ {
+ "itemId": 12,
+ "name": "create_index",
+ "label": "create_index"
+ }
+ ],
+
+ "configs":
+ [
+ {
+ "itemId": 1,
+ "name": "username",
+ "type": "string",
+ "mandatory": true,
+ "validationRegEx":"",
+ "validationMessage": "",
+ "uiHint":"",
+ "label": "Username"
+ },
+
+ {
+ "itemId": 2,
+ "name": "elasticsearch.url",
+ "type": "string",
+ "mandatory": true,
+ "defaultValue": "",
+ "validationRegEx":"",
+ "validationMessage": "",
+ "uiHint":"{\"TextFieldWithIcon\":true, \"info\": \"eg. 'http://<ipaddr>:9200'\"}",
+ "label": "Elasticsearch URL"
+ }
+ ],
+ "options": { "enableDenyAndExceptionsInPolicies": "false" },
+
+ "enums":
+ [
+
+ ],
+
+ "contextEnrichers":
+ [
+
+ ],
+
+ "policyConditions":
+ [
+
+ ]
+}
diff --git a/plugin-elasticsearch/.gitignore b/plugin-elasticsearch/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/plugin-elasticsearch/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/plugin-elasticsearch/conf/ranger-elasticsearch-audit-changes.cfg b/plugin-elasticsearch/conf/ranger-elasticsearch-audit-changes.cfg
new file mode 100644
index 0000000..8071e7b
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-elasticsearch-audit-changes.cfg
@@ -0,0 +1,65 @@
+# 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.
+#xasecure.audit.db.is.enabled %XAAUDIT.DB.IS_ENABLED% mod create-if-not-exists
+#xasecure.audit.jpa.javax.persistence.jdbc.url %XAAUDIT_DB_JDBC_URL% mod create-if-not-exists
+#xasecure.audit.jpa.javax.persistence.jdbc.user %XAAUDIT.DB.USER_NAME% mod create-if-not-exists
+#xasecure.audit.jpa.javax.persistence.jdbc.password crypted mod create-if-not-exists
+#xasecure.audit.credential.provider.file jceks://file%CREDENTIAL_PROVIDER_FILE% mod create-if-not-exists
+#xasecure.audit.jpa.javax.persistence.jdbc.driver %XAAUDIT_DB_JDBC_DRIVER% mod create-if-not-exists
+
+xasecure.audit.hdfs.is.enabled %XAAUDIT.HDFS.IS_ENABLED% mod create-if-not-exists
+xasecure.audit.hdfs.config.destination.directory %XAAUDIT.HDFS.DESTINATION_DIRECTORY% mod create-if-not-exists
+xasecure.audit.hdfs.config.destination.file %XAAUDIT.HDFS.DESTINTATION_FILE% mod create-if-not-exists
+xasecure.audit.hdfs.config.destination.flush.interval.seconds %XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS% mod create-if-not-exists
+xasecure.audit.hdfs.config.destination.rollover.interval.seconds %XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS% mod create-if-not-exists
+xasecure.audit.hdfs.config.destination.open.retry.interval.seconds %XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.buffer.directory %XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.buffer.file %XAAUDIT.HDFS.LOCAL_BUFFER_FILE% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.buffer.flush.interval.seconds %XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.buffer.rollover.interval.seconds %XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.archive.directory %XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY% mod create-if-not-exists
+xasecure.audit.hdfs.config.local.archive.max.file.count %XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT% mod create-if-not-exists
+
+#xasecure.audit.kafka.is.enabled %XAAUDIT.KAFKA.IS_ENABLED% mod create-if-not-exists
+#xasecure.audit.kafka.is.async %XAAUDIT.KAFKA.IS_ASYNC% mod create-if-not-exists
+#xasecure.audit.kafka.async.max.queue.size %XAAUDIT.KAFKA.MAX_QUEUE_SIZE% mod create-if-not-exists
+#xasecure.audit.kafka.async.max.flush.interval.ms %XAAUDIT.KAFKA.MAX_FLUSH_INTERVAL_MS% mod create-if-not-exists
+#xasecure.audit.kafka.broker_list %XAAUDIT.KAFKA.BROKER_LIST% mod create-if-not-exists
+#xasecure.audit.kafka.topic_name %XAAUDIT.KAFKA.TOPIC_NAME% mod create-if-not-exists
+
+xasecure.audit.solr.is.enabled %XAAUDIT.SOLR.IS_ENABLED% mod create-if-not-exists
+xasecure.audit.solr.async.max.queue.size %XAAUDIT.SOLR.MAX_QUEUE_SIZE% mod create-if-not-exists
+xasecure.audit.solr.async.max.flush.interval.ms %XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS% mod create-if-not-exists
+xasecure.audit.solr.solr_url %XAAUDIT.SOLR.SOLR_URL% mod create-if-not-exists
+
+#V3 configuration
+xasecure.audit.destination.solr %XAAUDIT.SOLR.ENABLE% mod create-if-not-exists
+xasecure.audit.destination.solr.urls %XAAUDIT.SOLR.URL% mod create-if-not-exists
+xasecure.audit.destination.solr.user %XAAUDIT.SOLR.USER% mod create-if-not-exists
+xasecure.audit.destination.solr.password %XAAUDIT.SOLR.PASSWORD% mod create-if-not-exists
+xasecure.audit.destination.solr.zookeepers %XAAUDIT.SOLR.ZOOKEEPER% mod create-if-not-exists
+xasecure.audit.destination.solr.batch.filespool.dir %XAAUDIT.SOLR.FILE_SPOOL_DIR% mod create-if-not-exists
+
+xasecure.audit.destination.hdfs %XAAUDIT.HDFS.ENABLE% mod create-if-not-exists
+xasecure.audit.destination.hdfs.batch.filespool.dir %XAAUDIT.HDFS.FILE_SPOOL_DIR% mod create-if-not-exists
+xasecure.audit.destination.hdfs.dir %XAAUDIT.HDFS.HDFS_DIR% mod create-if-not-exists
+
+AZURE.ACCOUNTNAME %XAAUDIT.HDFS.AZURE_ACCOUNTNAME% var
+xasecure.audit.destination.hdfs.config.fs.azure.shellkeyprovider.script %XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER% mod create-if-not-exists
+xasecure.audit.destination.hdfs.config.fs.azure.account.key.%AZURE.ACCOUNTNAME%.blob.core.windows.net %XAAUDIT.HDFS.AZURE_ACCOUNTKEY% mod create-if-not-exists
+xasecure.audit.destination.hdfs.config.fs.azure.account.keyprovider.%AZURE.ACCOUNTNAME%.blob.core.windows.net %XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER% mod create-if-not-exists
+
+#xasecure.audit.destination.file %XAAUDIT.FILE.ENABLE% mod create-if-not-exists
+#xasecure.audit.destination.file.dir %XAAUDIT.FILE.DIR% mod create-if-not-exists
diff --git a/plugin-elasticsearch/conf/ranger-elasticsearch-audit.xml b/plugin-elasticsearch/conf/ranger-elasticsearch-audit.xml
new file mode 100644
index 0000000..b9bdde5
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-elasticsearch-audit.xml
@@ -0,0 +1,271 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
+ <property>
+ <name>xasecure.audit.is.enabled</name>
+ <value>true</value>
+ </property>
+
+
+ <!-- DB audit provider configuration -->
+ <property>
+ <name>xasecure.audit.db.is.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.db.is.async</name>
+ <value>true</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.db.async.max.queue.size</name>
+ <value>10240</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.db.async.max.flush.interval.ms</name>
+ <value>30000</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.db.batch.size</name>
+ <value>100</value>
+ </property>
+
+ <!-- Properties whose name begin with "xasecure.audit.jpa." are used to configure JPA -->
+ <property>
+ <name>xasecure.audit.jpa.javax.persistence.jdbc.url</name>
+ <value>jdbc:mysql://localhost:3306/ranger_audit</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.jpa.javax.persistence.jdbc.user</name>
+ <value>rangerlogger</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.jpa.javax.persistence.jdbc.password</name>
+ <value>none</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.jpa.javax.persistence.jdbc.driver</name>
+ <value>com.mysql.jdbc.Driver</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.credential.provider.file</name>
+ <value>jceks://file/etc/ranger/elasticsearchdev/auditcred.jceks</value>
+ </property>
+
+
+
+ <!-- HDFS audit provider configuration -->
+ <property>
+ <name>xasecure.audit.hdfs.is.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.is.async</name>
+ <value>true</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.async.max.queue.size</name>
+ <value>1048576</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.async.max.flush.interval.ms</name>
+ <value>30000</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.encoding</name>
+ <value></value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.destination.directory</name>
+ <value>hdfs://NAMENODE_HOST:8020/ranger/audit/%app-type%/%time:yyyyMMdd%</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.destination.file</name>
+ <value>%hostname%-audit.log</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.destination.flush.interval.seconds</name>
+ <value>900</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.destination.rollover.interval.seconds</name>
+ <value>86400</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.destination.open.retry.interval.seconds</name>
+ <value>60</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.buffer.directory</name>
+ <value>/var/log/elasticsearch/audit</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.buffer.file</name>
+ <value>%time:yyyyMMdd-HHmm.ss%.log</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.buffer.file.buffer.size.bytes</name>
+ <value>8192</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.buffer.flush.interval.seconds</name>
+ <value>60</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.buffer.rollover.interval.seconds</name>
+ <value>600</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.archive.directory</name>
+ <value>/var/log/elasticsearch/audit/archive</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.hdfs.config.local.archive.max.file.count</name>
+ <value>10</value>
+ </property>
+
+ <!-- Audit to HDFS on Azure Datastore (WASB) requires v3 style settings. Comment the above and uncomment only the
+ following to audit to Azure Blob Datastore via hdfs' WASB schema.
+
+ NOTE: If you specify one audit destination in v3 style then other destinations, if any, must also be specified in v3 style
+ ====
+
+ <property>
+ <name>xasecure.audit.destination.hdfs</name>
+ <value>enabled</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.destination.hdfs.dir</name>
+ <value>wasb://ranger-audit1@youraccount.blob.core.windows.net</value>
+ </property>
+
+ the following 3 correspond to the properties with similar name in core-site.xml, i.e.
+ - fs.azure.account.key.youraccount.blob.core.windows.net => xasecure.audit.destination.hdfs.config.fs.azure.account.key.youraccount.blob.core.windows.net and
+ - fs.azure.account.keyprovider.youraccount.blob.core.windows.net => xasecure.audit.destination.hdfs.config.fs.azure.account.keyprovider.youraccount.blob.core.windows.net,
+ - fs.azure.shellkeyprovider.script => xasecure.audit.destination.hdfs.config.fs.azure.shellkeyprovider.script,
+
+ <property>
+ <name>xasecure.audit.destination.hdfs.config.fs.azure.account.key.youraccount.blob.core.windows.net</name>
+ <value>YOUR ENCRYPTED ACCESS KEY</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.destination.hdfs.config.fs.azure.account.keyprovider.youraccount.blob.core.windows.net</name>
+ <value>org.apache.hadoop.fs.azure.ShellDecryptionKeyProvider</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.destination.hdfs.config.fs.azure.shellkeyprovider.script</name>
+ <value>/usr/lib/python2.7/dist-packages/hdinsight_common/decrypt.sh</value>
+ </property>
+ -->
+
+ <!-- Log4j audit provider configuration -->
+ <property>
+ <name>xasecure.audit.log4j.is.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.log4j.is.async</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.log4j.async.max.queue.size</name>
+ <value>10240</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.log4j.async.max.flush.interval.ms</name>
+ <value>30000</value>
+ </property>
+
+ <!-- Kafka audit provider configuration -->
+ <property>
+ <name>xasecure.audit.kafka.is.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.kafka.async.max.queue.size</name>
+ <value>1</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.kafka.async.max.flush.interval.ms</name>
+ <value>1000</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.kafka.broker_list</name>
+ <value>localhost:9092</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.kafka.topic_name</name>
+ <value>ranger_audits</value>
+ </property>
+
+ <!-- Ranger audit provider configuration -->
+ <property>
+ <name>xasecure.audit.solr.is.enabled</name>
+ <value>false</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.solr.async.max.queue.size</name>
+ <value>1</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.solr.async.max.flush.interval.ms</name>
+ <value>1000</value>
+ </property>
+
+ <property>
+ <name>xasecure.audit.solr.solr_url</name>
+ <value>http://localhost:6083/solr/ranger_audits</value>
+ </property>
+
+</configuration>
diff --git a/plugin-elasticsearch/conf/ranger-elasticsearch-security-changes.cfg b/plugin-elasticsearch/conf/ranger-elasticsearch-security-changes.cfg
new file mode 100644
index 0000000..5b0deae
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-elasticsearch-security-changes.cfg
@@ -0,0 +1,28 @@
+# 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.
+#
+# Change the original policy parameter to work with policy manager based.
+#
+#
+ranger.plugin.elasticsearch.service.name %REPOSITORY_NAME% mod create-if-not-exists
+
+ranger.plugin.elasticsearch.policy.source.impl org.apache.ranger.admin.client.RangerAdminRESTClient mod create-if-not-exists
+
+ranger.plugin.elasticsearch.policy.rest.url %POLICY_MGR_URL% mod create-if-not-exists
+ranger.plugin.elasticsearch.policy.rest.ssl.config.file /etc/hadoop/conf/ranger-policymgr-ssl.xml mod create-if-not-exists
+ranger.plugin.elasticsearch.policy.pollIntervalMs 30000 mod create-if-not-exists
+ranger.plugin.elasticsearch.policy.cache.dir %POLICY_CACHE_FILE_PATH% mod create-if-not-exists
+ranger.plugin.elasticsearch.policy.rest.client.connection.timeoutMs 120000 mod create-if-not-exists
+ranger.plugin.elasticsearch.policy.rest.client.read.timeoutMs 30000 mod create-if-not-exists
\ No newline at end of file
diff --git a/plugin-elasticsearch/conf/ranger-elasticsearch-security.xml b/plugin-elasticsearch/conf/ranger-elasticsearch-security.xml
new file mode 100644
index 0000000..802e573
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-elasticsearch-security.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
+ <property>
+ <name>ranger.plugin.elasticsearch.service.name</name>
+ <value>elasticsearchdev</value>
+ <description>
+ Name of the Ranger service containing policies for this elasticsearch instance
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.source.impl</name>
+ <value>org.apache.ranger.admin.client.RangerAdminRESTClient</value>
+ <description>
+ Class to retrieve policies from the source
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.rest.url</name>
+ <value>http://policymanagerhost:port</value>
+ <description>
+ URL to Ranger Admin
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.rest.ssl.config.file</name>
+ <value>/etc/hadoop/conf/ranger-policymgr-ssl.xml</value>
+ <description>
+ Path to the file containing SSL details to contact Ranger Admin
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.pollIntervalMs</name>
+ <value>30000</value>
+ <description>
+ How often to poll for changes in policies?
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.cache.dir</name>
+ <value>/etc/ranger/hadoopdev/policycache</value>
+ <description>
+ Directory where Ranger policies are cached after successful retrieval from the source
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.rest.client.connection.timeoutMs</name>
+ <value>120000</value>
+ <description>
+ RangerRestClient Connection Timeout in Milli Seconds
+ </description>
+ </property>
+
+ <property>
+ <name>ranger.plugin.elasticsearch.policy.rest.client.read.timeoutMs</name>
+ <value>30000</value>
+ <description>
+ RangerRestClient read Timeout in Milli Seconds
+ </description>
+ </property>
+</configuration>
diff --git a/plugin-elasticsearch/conf/ranger-policymgr-ssl-changes.cfg b/plugin-elasticsearch/conf/ranger-policymgr-ssl-changes.cfg
new file mode 100644
index 0000000..ae347e8
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-policymgr-ssl-changes.cfg
@@ -0,0 +1,21 @@
+# 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.
+#
+# SSL Params
+#
+xasecure.policymgr.clientssl.keystore %SSL_KEYSTORE_FILE_PATH% mod create-if-not-exists
+xasecure.policymgr.clientssl.keystore.credential.file jceks://file%CREDENTIAL_PROVIDER_FILE% mod create-if-not-exists
+xasecure.policymgr.clientssl.truststore %SSL_TRUSTSTORE_FILE_PATH% mod create-if-not-exists
+xasecure.policymgr.clientssl.truststore.credential.file jceks://file%CREDENTIAL_PROVIDER_FILE% mod create-if-not-exists
diff --git a/plugin-elasticsearch/conf/ranger-policymgr-ssl.xml b/plugin-elasticsearch/conf/ranger-policymgr-ssl.xml
new file mode 100644
index 0000000..dcb0de0
--- /dev/null
+++ b/plugin-elasticsearch/conf/ranger-policymgr-ssl.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- The following properties are used for 2-way SSL client server validation -->
+ <property>
+ <name>xasecure.policymgr.clientssl.keystore</name>
+ <value>elasticsearchdev-clientcert.jks</value>
+ <description>
+ Java Keystore files
+ </description>
+ </property>
+ <property>
+ <name>xasecure.policymgr.clientssl.truststore</name>
+ <value>cacerts-xasecure.jks</value>
+ <description>
+ java truststore file
+ </description>
+ </property>
+ <property>
+ <name>xasecure.policymgr.clientssl.keystore.credential.file</name>
+ <value>jceks://file/tmp/keystore-elasticsearchdev-ssl.jceks</value>
+ <description>
+ java keystore credential file
+ </description>
+ </property>
+ <property>
+ <name>xasecure.policymgr.clientssl.truststore.credential.file</name>
+ <value>jceks://file/tmp/truststore-elasticsearchdev-ssl.jceks</value>
+ <description>
+ java truststore credential file
+ </description>
+ </property>
+</configuration>
diff --git a/plugin-elasticsearch/pom.xml b/plugin-elasticsearch/pom.xml
new file mode 100644
index 0000000..b997310
--- /dev/null
+++ b/plugin-elasticsearch/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ranger-elasticsearch-plugin</artifactId>
+ <name>Elasticsearch Security Plugin</name>
+ <description>Elasticsearch Security Plugin</description>
+ <packaging>jar</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <parent>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <version>${elasticsearch.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger-elasticsearch-plugin-shim</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger-plugins-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger-plugins-audit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>credentialbuilder</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <version>${httpcomponents.httpcore.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/plugin-elasticsearch/scripts/install.properties b/plugin-elasticsearch/scripts/install.properties
new file mode 100644
index 0000000..3a5b213
--- /dev/null
+++ b/plugin-elasticsearch/scripts/install.properties
@@ -0,0 +1,140 @@
+# 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.
+
+#
+# Location of Policy Manager URL
+#
+# Example:
+# POLICY_MGR_URL=http://policymanager.xasecure.net:6080
+#
+POLICY_MGR_URL=
+
+#
+# This is the repository name created within policy manager
+#
+# Example:
+# REPOSITORY_NAME=elasticsearchdev
+#
+REPOSITORY_NAME=
+
+#
+# Name of the directory where the component's lib and conf directory exist.
+# This location should be relative to the parent of the directory containing
+# the plugin installation files.
+#
+COMPONENT_INSTALL_DIR_NAME=../elasticsearch
+
+# Enable audit logs to Solr
+#Example
+#XAAUDIT.SOLR.ENABLE=true
+#XAAUDIT.SOLR.URL=http://localhost:6083/solr/ranger_audits
+#XAAUDIT.SOLR.ZOOKEEPER=
+#XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/elasticsearch/audit/solr/spool
+
+XAAUDIT.SOLR.ENABLE=false
+XAAUDIT.SOLR.URL=NONE
+XAAUDIT.SOLR.USER=NONE
+XAAUDIT.SOLR.PASSWORD=NONE
+XAAUDIT.SOLR.ZOOKEEPER=NONE
+XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/elasticsearch/audit/solr/spool
+
+# Enable audit logs to HDFS
+#Example
+#XAAUDIT.HDFS.ENABLE=true
+#XAAUDIT.HDFS.HDFS_DIR=hdfs://node-1.example.com:8020/ranger/audit
+# If using Azure Blob Storage
+#XAAUDIT.HDFS.HDFS_DIR=wasb[s]://<containername>@<accountname>.blob.core.windows.net/<path>
+#XAAUDIT.HDFS.HDFS_DIR=wasb://ranger_audit_container@my-azure-account.blob.core.windows.net/ranger/audit
+#XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/elasticsearch/audit/hdfs/spool
+
+XAAUDIT.HDFS.ENABLE=false
+XAAUDIT.HDFS.HDFS_DIR=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit
+XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/elasticsearch/audit/hdfs/spool
+
+# Following additional propertis are needed When auditing to Azure Blob Storage via HDFS
+# Get these values from your /etc/hadoop/conf/core-site.xml
+#XAAUDIT.HDFS.HDFS_DIR=wasb[s]://<containername>@<accountname>.blob.core.windows.net/<path>
+XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME
+XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY
+XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER
+XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER
+
+# End of V3 properties
+
+
+#
+# Audit to HDFS Configuration
+#
+# If XAAUDIT.HDFS.IS_ENABLED is set to true, please replace tokens
+# that start with __REPLACE__ with appropriate values
+# XAAUDIT.HDFS.IS_ENABLED=true
+# XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit/%app-type%/%time:yyyyMMdd%
+# XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=__REPLACE__LOG_DIR/elasticsearch/audit
+# XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=__REPLACE__LOG_DIR/elasticsearch/audit/archive
+#
+# Example:
+# XAAUDIT.HDFS.IS_ENABLED=true
+# XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://namenode.example.com:8020/ranger/audit/%app-type%/%time:yyyyMMdd%
+# XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/elasticsearch/audit
+# XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/elasticsearch/audit/archive
+#
+XAAUDIT.HDFS.IS_ENABLED=false
+XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit/%app-type%/%time:yyyyMMdd%
+XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=__REPLACE__LOG_DIR/elasticsearch/audit
+XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=__REPLACE__LOG_DIR/elasticsearch/audit/archive
+
+XAAUDIT.HDFS.DESTINTATION_FILE=%hostname%-audit.log
+XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900
+XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400
+XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60
+XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log
+XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60
+XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600
+XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10
+
+#Solr Audit Provder
+XAAUDIT.SOLR.IS_ENABLED=false
+XAAUDIT.SOLR.MAX_QUEUE_SIZE=1
+XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000
+XAAUDIT.SOLR.SOLR_URL=http://localhost:6083/solr/ranger_audits
+
+#
+# SSL Client Certificate Information
+#
+# Example:
+# SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks
+# SSL_KEYSTORE_PASSWORD=none
+# SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks
+# SSL_TRUSTSTORE_PASSWORD=none
+#
+# You do not need use SSL between agent and security admin tool, please leave these sample value as it is.
+#
+SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks
+SSL_KEYSTORE_PASSWORD=myKeyFilePassword
+SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks
+SSL_TRUSTSTORE_PASSWORD=changeit
+
+#
+# Custom component user
+# CUSTOM_COMPONENT_USER=<custom-user>
+# keep blank if component user is default
+CUSTOM_USER=elasticsearch
+
+
+#
+# Custom component group
+# CUSTOM_COMPONENT_GROUP=<custom-group>
+# keep blank if component group is default
+CUSTOM_GROUP=hadoop
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.java
new file mode 100644
index 0000000..a6b024f
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.java
@@ -0,0 +1,148 @@
+/*
+ * 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.authorization.elasticsearch.authorizer;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+import org.apache.ranger.services.elasticsearch.client.ElasticsearchResourceMgr;
+import org.apache.ranger.services.elasticsearch.privilege.IndexPrivilegeUtils;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+
+import com.google.common.collect.Sets;
+
+public class RangerElasticsearchAuthorizer implements RangerElasticsearchAccessControl {
+
+ private static final Logger LOG = ESLoggerFactory.getLogger(RangerElasticsearchAuthorizer.class);
+
+ private static volatile RangerElasticsearchInnerPlugin elasticsearchPlugin = null;
+
+ public RangerElasticsearchAuthorizer() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.RangerElasticsearchAuthorizer()");
+ }
+
+ this.init();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.RangerElasticsearchAuthorizer()");
+ }
+ }
+
+ public void init() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.init()");
+ }
+
+ RangerElasticsearchInnerPlugin plugin = elasticsearchPlugin;
+
+ if (plugin == null) {
+ synchronized (RangerElasticsearchAuthorizer.class) {
+ plugin = elasticsearchPlugin;
+
+ if (plugin == null) {
+ plugin = new RangerElasticsearchInnerPlugin();
+ plugin.init();
+ elasticsearchPlugin = plugin;
+ }
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.init()");
+ }
+ }
+
+ @Override
+ public boolean checkPermission(String user, List<String> groups, String index, String action,
+ String clientIPAddress) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.checkPermission( user=" + user + ", groups=" + groups
+ + ", index=" + index + ", action=" + action + ", clientIPAddress=" + clientIPAddress + ")");
+ }
+
+ boolean ret = false;
+
+ if (elasticsearchPlugin != null) {
+
+ String privilege = IndexPrivilegeUtils.getPrivilegeFromAction(action);
+ String clusterName = elasticsearchPlugin.getClusterName();
+ RangerElasticsearchAccessRequest request = new RangerElasticsearchAccessRequest(user, groups, index,
+ privilege, clusterName, clientIPAddress);
+
+ RangerAccessResult result = elasticsearchPlugin.isAccessAllowed(request);
+ if (result != null && result.getIsAllowed()) {
+ ret = true;
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.checkPermission(): result=" + ret);
+ }
+
+ return ret;
+ }
+}
+
+class RangerElasticsearchInnerPlugin extends RangerBasePlugin {
+ public RangerElasticsearchInnerPlugin() {
+ super("elasticsearch", "elasticsearch");
+ }
+
+ @Override
+ public void init() {
+ super.init();
+
+ RangerDefaultAuditHandler auditHandler = new RangerDefaultAuditHandler();
+
+ super.setResultProcessor(auditHandler);
+ }
+}
+
+class RangerElasticsearchResource extends RangerAccessResourceImpl {
+ public RangerElasticsearchResource(String index) {
+ if (StringUtils.isEmpty(index)) {
+ index = "*";
+ }
+ setValue(ElasticsearchResourceMgr.INDEX, index);
+ }
+}
+
+class RangerElasticsearchAccessRequest extends RangerAccessRequestImpl {
+ public RangerElasticsearchAccessRequest(String user, List<String> groups, String index, String privilege,
+ String clusterName, String clientIPAddress) {
+ super.setUser(user);
+ if (CollectionUtils.isNotEmpty(groups)) {
+ super.setUserGroups(Sets.newHashSet(groups));
+ }
+ super.setResource(new RangerElasticsearchResource(index));
+ super.setAccessType(privilege);
+ super.setAction(privilege);
+ super.setClusterName(clusterName);
+ super.setClientIPAddress(clientIPAddress);
+ super.setAccessTime(new Date());
+ }
+}
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/RangerServiceElasticsearch.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/RangerServiceElasticsearch.java
new file mode 100644
index 0000000..ed1c98e
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/RangerServiceElasticsearch.java
@@ -0,0 +1,88 @@
+/*
+ * 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.services.elasticsearch;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.service.RangerBaseService;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.services.elasticsearch.client.ElasticsearchResourceMgr;
+
+public class RangerServiceElasticsearch extends RangerBaseService {
+
+ private static final Logger LOG = Logger.getLogger(RangerServiceElasticsearch.class);
+
+ public RangerServiceElasticsearch() {
+ super();
+ }
+
+ @Override
+ public void init(RangerServiceDef serviceDef, RangerService service) {
+ super.init(serviceDef, service);
+ }
+
+ @Override
+ public Map<String, Object> validateConfig() throws Exception {
+ Map<String, Object> ret = new HashMap<String, Object>();
+ String serviceName = getServiceName();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerServiceElasticsearch.validateConfig() service: " + serviceName);
+ }
+ if (configs != null) {
+ try {
+ ret = ElasticsearchResourceMgr.validateConfig(serviceName, configs);
+ } catch (Exception e) {
+ LOG.error("<== RangerServiceElasticsearch.validateConfig() error: " + e);
+ throw e;
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerServiceElasticsearch.validateConfig() result: " + ret);
+ }
+ return ret;
+ }
+
+ @Override
+ public List<String> lookupResource(ResourceLookupContext context) throws Exception {
+
+ List<String> ret = new ArrayList<String>();
+ String serviceName = getServiceName();
+ Map<String, String> configs = getConfigs();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerServiceElasticsearch.lookupResource() context: " + context);
+ }
+ if (context != null) {
+ try {
+ ret = ElasticsearchResourceMgr.getElasticsearchResources(serviceName, configs, context);
+ } catch (Exception e) {
+ LOG.error("<==RangerServiceElasticsearch.lookupResource() error: " + e);
+ throw e;
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerServiceElasticsearch.lookupResource() result: " + ret);
+ }
+ return ret;
+ }
+}
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchClient.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchClient.java
new file mode 100644
index 0000000..d5c170d
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchClient.java
@@ -0,0 +1,282 @@
+/*
+ * 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.services.elasticsearch.client;
+
+import java.lang.reflect.Type;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpStatus;
+import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.client.BaseClient;
+import org.apache.ranger.plugin.client.HadoopException;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+
+public class ElasticsearchClient extends BaseClient {
+
+ private static final Logger LOG = Logger.getLogger(ElasticsearchClient.class);
+
+ private static final String ELASTICSEARCH_INDEX_API_ENDPOINT = "/_all";
+
+ private String elasticsearchUrl;
+
+ private String userName;
+
+ public ElasticsearchClient(String serviceName, Map<String, String> configs) {
+
+ super(serviceName, configs, "elasticsearch-client");
+ this.elasticsearchUrl = configs.get("elasticsearch.url");
+ this.userName = configs.get("username");
+
+ if (StringUtils.isEmpty(this.elasticsearchUrl)) {
+ LOG.error("No value found for configuration 'elasticsearch.url'. Elasticsearch resource lookup will fail.");
+ }
+
+ if (StringUtils.isEmpty(this.userName)) {
+ LOG.error("No value found for configuration 'username'. Elasticsearch resource lookup will fail.");
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Elasticsearch client is build with url: [" + this.elasticsearchUrl + "], user: [" + this.userName
+ + "].");
+ }
+ }
+
+ public List<String> getIndexList(final String indexMatching, final List<String> existingIndices) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Get elasticsearch index list for indexMatching: " + indexMatching + ", existingIndices: "
+ + existingIndices);
+ }
+ Subject subj = getLoginSubject();
+ if (subj == null) {
+ return Collections.emptyList();
+ }
+
+ List<String> ret = Subject.doAs(subj, new PrivilegedAction<List<String>>() {
+
+ @Override
+ public List<String> run() {
+
+ String indexApi = null;
+ if (StringUtils.isNotEmpty(indexMatching)) {
+ indexApi = '/' + indexMatching;
+ if (!indexApi.endsWith("*")) {
+ indexApi += "*";
+ }
+ } else {
+ indexApi = ELASTICSEARCH_INDEX_API_ENDPOINT;
+ }
+ ClientResponse response = getClientResponse(elasticsearchUrl, indexApi, userName);
+
+ Map<String, Object> index2detailMap = getElasticsearchResourceResponse(response,
+ new TypeToken<HashMap<String, Object>>() {
+ }.getType());
+ if (MapUtils.isEmpty(index2detailMap)) {
+ return Collections.emptyList();
+ }
+
+ Set<String> indexResponses = index2detailMap.keySet();
+ if (CollectionUtils.isEmpty(indexResponses)) {
+ return Collections.emptyList();
+ }
+
+ return filterResourceFromResponse(indexMatching, existingIndices, new ArrayList<>(indexResponses));
+ }
+ });
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Get elasticsearch index list result: " + ret);
+ }
+ return ret;
+ }
+
+ private static ClientResponse getClientResponse(String elasticsearchUrl, String elasticsearchApi, String userName) {
+ String[] elasticsearchUrls = elasticsearchUrl.trim().split("[,;]");
+ if (ArrayUtils.isEmpty(elasticsearchUrls)) {
+ return null;
+ }
+
+ ClientResponse response = null;
+ Client client = Client.create();
+ client.addFilter(new HTTPBasicAuthFilter(userName, StringUtils.EMPTY));
+ for (String currentUrl : elasticsearchUrls) {
+ if (StringUtils.isBlank(currentUrl)) {
+ continue;
+ }
+
+ String url = currentUrl.trim() + elasticsearchApi;
+ try {
+ response = getClientResponse(url, client);
+
+ if (response != null) {
+ if (response.getStatus() == HttpStatus.SC_OK) {
+ break;
+ } else {
+ response.close();
+ }
+ }
+ } catch (Throwable t) {
+ String msgDesc = "Exception while getting elasticsearch response, elasticsearchUrl: " + url;
+ LOG.error(msgDesc, t);
+ }
+ }
+ client.destroy();
+
+ return response;
+ }
+
+ private static ClientResponse getClientResponse(String url, Client client) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("getClientResponse():calling " + url);
+ }
+
+ WebResource webResource = client.resource(url);
+
+ ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+ if (response != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("getClientResponse():response.getStatus()= " + response.getStatus());
+ }
+ if (response.getStatus() != HttpStatus.SC_OK) {
+ LOG.warn("getClientResponse():response.getStatus()= " + response.getStatus() + " for URL " + url
+ + ", failed to get elasticsearch resource list, response= " + response.getEntity(String.class));
+ }
+ }
+ return response;
+ }
+
+ private <T> T getElasticsearchResourceResponse(ClientResponse response, Type type) {
+ T resource = null;
+ try {
+ if (response != null && response.getStatus() == HttpStatus.SC_OK) {
+ String jsonString = response.getEntity(String.class);
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ resource = gson.fromJson(jsonString, type);
+ } else {
+ String msgDesc = "Unable to get a valid response for " + "expected mime type : ["
+ + MediaType.APPLICATION_JSON + "], elasticsearchUrl: " + elasticsearchUrl
+ + " - got null response.";
+ LOG.error(msgDesc);
+ HadoopException hdpException = new HadoopException(msgDesc);
+ hdpException.generateResponseDataMap(false, msgDesc, msgDesc + DEFAULT_ERROR_MESSAGE, null, null);
+ throw hdpException;
+ }
+ } catch (HadoopException he) {
+ throw he;
+ } catch (Throwable t) {
+ String msgDesc = "Exception while getting elasticsearch resource response, elasticsearchUrl: "
+ + elasticsearchUrl;
+ HadoopException hdpException = new HadoopException(msgDesc, t);
+
+ LOG.error(msgDesc, t);
+
+ hdpException.generateResponseDataMap(false, BaseClient.getMessage(t), msgDesc + DEFAULT_ERROR_MESSAGE, null,
+ null);
+ throw hdpException;
+
+ } finally {
+ if (response != null) {
+ response.close();
+ }
+ }
+ return resource;
+ }
+
+ private static List<String> filterResourceFromResponse(String resourceMatching, List<String> existingResources,
+ List<String> resourceResponses) {
+ List<String> resources = new ArrayList<String>();
+ for (String resourceResponse : resourceResponses) {
+ if (CollectionUtils.isNotEmpty(existingResources) && existingResources.contains(resourceResponse)) {
+ continue;
+ }
+ if (StringUtils.isEmpty(resourceMatching) || resourceMatching.startsWith("*")
+ || resourceResponse.toLowerCase().startsWith(resourceMatching.toLowerCase())) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("filterResourceFromResponse(): Adding elasticsearch resource " + resourceResponse);
+ }
+ resources.add(resourceResponse);
+ }
+ }
+ return resources;
+ }
+
+ public static Map<String, Object> connectionTest(String serviceName, Map<String, String> configs) {
+ ElasticsearchClient elasticsearchClient = getElasticsearchClient(serviceName, configs);
+ List<String> indexList = elasticsearchClient.getIndexList(null, null);
+
+ boolean connectivityStatus = false;
+ if (CollectionUtils.isNotEmpty(indexList)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ConnectionTest list size " + indexList.size() + " elasticsearch indices.");
+ }
+ connectivityStatus = true;
+ }
+
+ Map<String, Object> responseData = new HashMap<String, Object>();
+ if (connectivityStatus) {
+ String successMsg = "ConnectionTest Successful.";
+ BaseClient.generateResponseDataMap(connectivityStatus, successMsg, successMsg, null, null, responseData);
+ } else {
+ String failureMsg = "Unable to retrieve any elasticsearch indices using given parameters.";
+ BaseClient.generateResponseDataMap(connectivityStatus, failureMsg, failureMsg + DEFAULT_ERROR_MESSAGE, null,
+ null, responseData);
+ }
+
+ return responseData;
+ }
+
+ public static ElasticsearchClient getElasticsearchClient(String serviceName, Map<String, String> configs) {
+ ElasticsearchClient elasticsearchClient = null;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Getting elasticsearchClient for datasource: " + serviceName);
+ }
+ if (MapUtils.isEmpty(configs)) {
+ String msgDesc = "Could not connect elasticsearch as connection configMap is empty.";
+ LOG.error(msgDesc);
+ HadoopException hdpException = new HadoopException(msgDesc);
+ hdpException.generateResponseDataMap(false, msgDesc, msgDesc + DEFAULT_ERROR_MESSAGE, null, null);
+ throw hdpException;
+ } else {
+ elasticsearchClient = new ElasticsearchClient(serviceName, configs);
+ }
+ return elasticsearchClient;
+ }
+}
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchResourceMgr.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchResourceMgr.java
new file mode 100644
index 0000000..d7980aa
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/client/ElasticsearchResourceMgr.java
@@ -0,0 +1,102 @@
+/*
+ * 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.services.elasticsearch.client;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+
+public class ElasticsearchResourceMgr {
+
+ public static final String INDEX = "index";
+
+ private static final Logger LOG = Logger.getLogger(ElasticsearchResourceMgr.class);
+
+ public static Map<String, Object> validateConfig(String serviceName, Map<String, String> configs) throws Exception {
+ Map<String, Object> ret = null;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> ElasticsearchResourceMgr.validateConfig() serviceName: " + serviceName + ", configs: "
+ + configs);
+ }
+
+ try {
+ ret = ElasticsearchClient.connectionTest(serviceName, configs);
+ } catch (Exception e) {
+ LOG.error("<== ElasticsearchResourceMgr.validateConfig() error: " + e);
+ throw e;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== ElasticsearchResourceMgr.validateConfig() result: " + ret);
+ }
+ return ret;
+ }
+
+ public static List<String> getElasticsearchResources(String serviceName, Map<String, String> configs,
+ ResourceLookupContext context) {
+ String userInput = context.getUserInput();
+ String resource = context.getResourceName();
+ Map<String, List<String>> resourceMap = context.getResources();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> ElasticsearchResourceMgr.getElasticsearchResources() userInput: " + userInput
+ + ", resource: " + resource + ", resourceMap: " + resourceMap);
+ }
+
+ if (MapUtils.isEmpty(configs)) {
+ LOG.error("Connection config is empty!");
+ return null;
+ }
+
+ if (StringUtils.isEmpty(userInput)) {
+ LOG.warn("User input is empty, set default value : *");
+ userInput = "*";
+ }
+
+ final ElasticsearchClient elasticsearchClient = ElasticsearchClient.getElasticsearchClient(serviceName, configs);
+ if (elasticsearchClient == null) {
+ LOG.error("Failed to getElasticsearchResources!");
+ return null;
+ }
+
+ List<String> resultList = null;
+
+ if (StringUtils.isNotEmpty(resource)) {
+ switch (resource) {
+ case INDEX:
+ List<String> existingConnectors = resourceMap.get(INDEX);
+ resultList = elasticsearchClient.getIndexList(userInput, existingConnectors);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== ElasticsearchResourceMgr.getElasticsearchResources() result: " + resultList);
+ }
+ return resultList;
+ }
+
+}
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilege.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilege.java
new file mode 100644
index 0000000..4691a51
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilege.java
@@ -0,0 +1,55 @@
+/*
+ * 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.services.elasticsearch.privilege;
+
+import java.util.List;
+
+public class IndexPrivilege {
+
+ private String privilege;
+
+ private List<String> actions;
+
+ public IndexPrivilege(String privilege, List<String> actions) {
+ super();
+ this.privilege = privilege;
+ this.actions = actions;
+ }
+
+ public String getPrivilege() {
+ return privilege;
+ }
+
+ public void setPrivilege(String privilege) {
+ this.privilege = privilege;
+ }
+
+ public List<String> getActions() {
+ return actions;
+ }
+
+ public void setActions(List<String> actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public String toString() {
+ return "IndexPrivilege [privilege=" + privilege + ", actions=" + actions + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilegeUtils.java b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilegeUtils.java
new file mode 100644
index 0000000..16e666a
--- /dev/null
+++ b/plugin-elasticsearch/src/main/java/org/apache/ranger/services/elasticsearch/privilege/IndexPrivilegeUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.services.elasticsearch.privilege;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+
+public class IndexPrivilegeUtils {
+ // These privileges have priority, should be matched in order
+ private static final List<IndexPrivilege> privileges = new LinkedList<>();
+
+ // External Privileges for users
+ public static final String ALL = "all";
+ public static final String MONITOR = "monitor";
+ public static final String MANAGE = "manage";
+ public static final String VIEW_INDEX_METADATA = "view_index_metadata";
+ public static final String READ = "read";
+ public static final String READ_CROSS_CLUSTER = "read_cross_cluster";
+ public static final String INDEX = "index";
+ public static final String CREATE = "create";
+ public static final String DELETE = "delete";
+ public static final String WRITE = "write";
+ public static final String DELETE_INDEX = "delete_index";
+ public static final String CREATE_INDEX = "create_index";
+
+ // Internal Privileges for Ranger authentication
+ public static final String INDICES_PUT = "indices_put";
+ public static final String INDICES_SEARCH_SHARDS = "indices_search_shards";
+ public static final String INDICES_BULK = "indices_bulk";
+ public static final String INDICES_INDEX = "indices_index";
+
+ static {
+ // First Priority
+ privileges.add(new IndexPrivilege(VIEW_INDEX_METADATA,
+ Arrays.asList("indices:admin/aliases/get", "indices:admin/aliases/exists", "indices:admin/get",
+ "indices:admin/exists", "indices:admin/mappings/fields/get", "indices:admin/mappings/get",
+ "indices:admin/types/exists", "indices:admin/validate/query", "indices:monitor/settings/get")));
+ privileges.add(new IndexPrivilege(READ, Arrays.asList("indices:data/read/")));
+ privileges.add(
+ new IndexPrivilege(READ_CROSS_CLUSTER, Arrays.asList("internal:transport/proxy/indices:data/read/")));
+ privileges.add(new IndexPrivilege(INDEX, Arrays.asList("indices:data/write/update")));
+
+ privileges.add(new IndexPrivilege(DELETE, Arrays.asList("indices:data/write/delete")));
+ privileges.add(new IndexPrivilege(DELETE_INDEX, Arrays.asList("indices:admin/delete")));
+ privileges.add(new IndexPrivilege(CREATE_INDEX, Arrays.asList("indices:admin/create")));
+
+ privileges.add(new IndexPrivilege(INDICES_PUT, Arrays.asList("indices:admin/mapping/put")));
+ privileges.add(new IndexPrivilege(INDICES_SEARCH_SHARDS, Arrays.asList("indices:admin/shards/search_shards")));
+ privileges.add(new IndexPrivilege(INDICES_BULK, Arrays.asList("indices:data/write/bulk")));
+ privileges.add(new IndexPrivilege(INDICES_INDEX, Arrays.asList("indices:data/write/index")));
+
+ // Second Priority
+ privileges.add(new IndexPrivilege(MONITOR, Arrays.asList("indices:monitor/")));
+ privileges.add(new IndexPrivilege(MANAGE, Arrays.asList("indices:admin/")));
+ privileges.add(new IndexPrivilege(WRITE, Arrays.asList("indices:data/write/")));
+
+ // Last Priority
+ privileges.add(
+ new IndexPrivilege(ALL, Arrays.asList("indices:", "internal:transport/proxy/indices:", "cluster:")));
+
+ }
+
+ /**
+ * If action is empty or not matched, set default privilege "all".
+ * @param action
+ * @return privilege
+ */
+ public static String getPrivilegeFromAction(String action) {
+ if (StringUtils.isEmpty(action)) {
+ return ALL;
+ }
+
+ for (IndexPrivilege privilege : privileges) {
+ // Get the privilege of matched action rule in order
+ for (String actionRule : privilege.getActions()) {
+ if (action.startsWith(actionRule)) {
+ return privilege.getPrivilege();
+ }
+ }
+ }
+
+ return ALL;
+ }
+}
diff --git a/pom.xml b/pom.xml
index a11cf51..2a2540a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,6 +107,7 @@
<curator.test.version>2.12.0</curator.test.version>
<derby.version>10.11.1.1</derby.version>
<eclipse.jpa.version>2.5.2</eclipse.jpa.version>
+ <elasticsearch.version>6.2.2</elasticsearch.version>
<findbugs.plugin.version>3.0.3</findbugs.plugin.version>
<google.guava.version>25.1-jre</google.guava.version>
<googlecode.log4jdbc.version>1.2</googlecode.log4jdbc.version>
@@ -221,6 +222,8 @@
<module>ranger-sqoop-plugin-shim</module>
<module>plugin-kylin</module>
<module>ranger-kylin-plugin-shim</module>
+ <module>plugin-elasticsearch</module>
+ <module>ranger-elasticsearch-plugin-shim</module>
</modules>
<build>
<plugins>
@@ -247,6 +250,7 @@
<descriptor>src/main/assembly/plugin-atlas.xml</descriptor>
<descriptor>src/main/assembly/plugin-sqoop.xml</descriptor>
<descriptor>src/main/assembly/plugin-kylin.xml</descriptor>
+ <descriptor>src/main/assembly/plugin-elasticsearch.xml</descriptor>
</descriptors>
</configuration>
</plugin>
@@ -578,6 +582,33 @@
</build>
</profile>
<profile>
+ <id>ranger-elasticsearch-plugin</id>
+ <modules>
+ <module>agents-audit</module>
+ <module>agents-common</module>
+ <module>agents-cred</module>
+ <module>agents-installer</module>
+ <module>credentialbuilder</module>
+ <module>ranger-plugin-classloader</module>
+ <module>ranger-util</module>
+ <module>plugin-elasticsearch</module>
+ <module>ranger-elasticsearch-plugin-shim</module>
+ </modules>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-beta-5</version>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/plugin-elasticsearch.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
<id>linux</id>
<activation>
<os>
@@ -629,6 +660,8 @@
<module>ranger-sqoop-plugin-shim</module>
<module>plugin-kylin</module>
<module>ranger-kylin-plugin-shim</module>
+ <module>plugin-elasticsearch</module>
+ <module>ranger-elasticsearch-plugin-shim</module>
<module>unixauthnative</module>
</modules>
<build>
@@ -656,6 +689,7 @@
<descriptor>src/main/assembly/plugin-atlas.xml</descriptor>
<descriptor>src/main/assembly/plugin-sqoop.xml</descriptor>
<descriptor>src/main/assembly/plugin-kylin.xml</descriptor>
+ <descriptor>src/main/assembly/plugin-elasticsearch.xml</descriptor>
</descriptors>
</configuration>
</plugin>
diff --git a/ranger-elasticsearch-plugin-shim/.gitignore b/ranger-elasticsearch-plugin-shim/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/ranger-elasticsearch-plugin-shim/conf/plugin-descriptor.properties b/ranger-elasticsearch-plugin-shim/conf/plugin-descriptor.properties
new file mode 100644
index 0000000..95f9604
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/conf/plugin-descriptor.properties
@@ -0,0 +1,65 @@
+# 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.
+
+# Elasticsearch plugin descriptor file
+# This file must exist as 'plugin-descriptor.properties' in a folder named `elasticsearch`
+# inside all plugins.
+#
+### example plugin for "foo"
+#
+# foo.zip <-- zip file for the plugin, with this structure:
+#|____elasticsearch/
+#| |____ <arbitrary name1>.jar <-- classes, resources, dependencies
+#| |____ <arbitrary nameN>.jar <-- any number of jars
+#| |____ plugin-descriptor.properties <-- example contents below:
+#
+# classname=foo.bar.BazPlugin
+# description=My cool plugin
+# version=2.0
+# elasticsearch.version=2.0
+# java.version=1.7
+#
+### mandatory elements for all plugins:
+#
+# 'description': simple summary of the plugin
+description=Elasticsearch Security Plugin
+#
+# 'version': plugin's version
+version=${project.version}
+#
+# 'name': the plugin name
+name=ranger-elasticsearch-plugin
+#
+# 'classname': the name of the class to load, fully-qualified.
+classname=org.apache.ranger.authorization.elasticsearch.plugin.RangerElasticsearchPlugin
+#
+# 'java.version': version of java the code is built against
+# use the system property java.specification.version
+# version string must be a sequence of nonnegative decimal integers
+# separated by "."'s and may have leading zeros
+java.version=${javac.target.version}
+#
+# 'elasticsearch.version': version of elasticsearch compiled against
+elasticsearch.version=${elasticsearch.version}
+### optional elements for plugins:
+#
+# 'extended.plugins': other plugins this plugin extends through SPI
+#extended.plugins=${extendedPlugins}
+#
+# 'has.native.controller': whether or not the plugin has a native controller
+has.native.controller=false
+#
+# 'requires.keystore': whether or not the plugin needs the elasticsearch keystore be created
+requires.keystore=false
diff --git a/ranger-elasticsearch-plugin-shim/conf/plugin-security.policy b/ranger-elasticsearch-plugin-shim/conf/plugin-security.policy
new file mode 100644
index 0000000..4f96d87
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/conf/plugin-security.policy
@@ -0,0 +1,39 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.
+ */
+
+grant {
+ permission java.lang.RuntimePermission "createClassLoader";
+ permission java.lang.RuntimePermission "getClassLoader";
+ permission java.lang.RuntimePermission "setContextClassLoader";
+ permission java.lang.RuntimePermission "shutdownHooks";
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+ permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+ permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+
+ permission javax.security.auth.AuthPermission "getLoginConfiguration";
+ permission javax.security.auth.AuthPermission "setLoginConfiguration";
+
+ permission java.net.NetPermission "getProxySelector";
+ // adapt to connect different IP and Port
+ permission java.net.SocketPermission "*", "connect,resolve";
+
+ permission java.util.PropertyPermission "*", "read,write";
+ // adapt to different directories configured by user
+ permission java.io.FilePermission "<<ALL FILES>>", "read,write";
+};
diff --git a/ranger-elasticsearch-plugin-shim/pom.xml b/ranger-elasticsearch-plugin-shim/pom.xml
new file mode 100644
index 0000000..59a653b
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ranger-elasticsearch-plugin-shim</artifactId>
+ <name>Elasticsearch Security Plugin Shim</name>
+ <description>Elasticsearch Security Plugin Shim</description>
+ <packaging>jar</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <parent>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <dependencies>
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <version>${elasticsearch.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ranger</groupId>
+ <artifactId>ranger-plugin-classloader</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>${commons.collections.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>${commons.lang.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j-api.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAccessControl.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAccessControl.java
new file mode 100644
index 0000000..f9996b0
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAccessControl.java
@@ -0,0 +1,35 @@
+/*
+ * 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.authorization.elasticsearch.authorizer;
+
+import java.util.List;
+
+public interface RangerElasticsearchAccessControl {
+
+ /**
+ * Check permission for user do action on the elasticsearch index.
+ *
+ * @param user The user do the request.
+ * @param groups The groups of the user.
+ * @param index The elasticsearch index.
+ * @param action The operation type.
+ * @param clientIPAddress The clent IP address.
+ * @return Check permission result.
+ */
+ boolean checkPermission(String user, List<String> groups, String index, String action, String clientIPAddress);
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.java
new file mode 100644
index 0000000..8f33cb9
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/authorizer/RangerElasticsearchAuthorizer.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.authorization.elasticsearch.authorizer;
+
+import java.util.List;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.plugin.classloader.RangerPluginClassLoader;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+
+public class RangerElasticsearchAuthorizer {
+
+ private static final Logger LOG = ESLoggerFactory.getLogger(RangerElasticsearchAuthorizer.class);
+
+ private static final String RANGER_PLUGIN_TYPE = "elasticsearch";
+
+ private static final String RANGER_ELASTICSEARCH_AUTHORIZER_IMPL_CLASSNAME = "org.apache.ranger.authorization.elasticsearch.authorizer.RangerElasticsearchAuthorizer";
+
+ private static RangerPluginClassLoader rangerPluginClassLoader = null;
+
+ private static ClassLoader esClassLoader = null;
+
+ private RangerElasticsearchAccessControl rangerElasticsearchAccessControl = null;
+
+ public RangerElasticsearchAuthorizer() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.RangerElasticsearchAuthorizer()");
+ }
+
+ this.init();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.RangerElasticsearchAuthorizer()");
+ }
+ }
+
+ public void init() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.init()");
+ }
+
+ try {
+
+ // In elasticsearch this.getClass().getClassLoader() is FactoryURLClassLoader,
+ // but Thread.currentThread().getContextClassLoader() is AppClassLoader.
+ esClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+
+ rangerPluginClassLoader = RangerPluginClassLoader.getInstance(RANGER_PLUGIN_TYPE, this.getClass());
+ Thread.currentThread().setContextClassLoader(esClassLoader);
+
+ @SuppressWarnings("unchecked")
+ Class<RangerElasticsearchAccessControl> cls = (Class<RangerElasticsearchAccessControl>) Class
+ .forName(RANGER_ELASTICSEARCH_AUTHORIZER_IMPL_CLASSNAME, true, rangerPluginClassLoader);
+
+ activatePluginClassLoader();
+
+ rangerElasticsearchAccessControl = cls.newInstance();
+ } catch (Exception e) {
+ LOG.error("Error Enabling RangerElasticsearchAuthorizer", e);
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.init()");
+ }
+ }
+
+ public boolean checkPermission(String user, List<String> groups, String index, String action,
+ String clientIPAddress) {
+ boolean ret = false;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> RangerElasticsearchAuthorizer.checkPermission()");
+ }
+
+ try {
+ activatePluginClassLoader();
+
+ ret = rangerElasticsearchAccessControl.checkPermission(user, groups, index, action, clientIPAddress);
+ } finally {
+ deactivatePluginClassLoader();
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<== RangerElasticsearchAuthorizer.checkPermission()");
+ }
+
+ return ret;
+ }
+
+ private void activatePluginClassLoader() {
+ if (rangerPluginClassLoader != null) {
+ Thread.currentThread().setContextClassLoader(rangerPluginClassLoader);
+ }
+ }
+
+ private void deactivatePluginClassLoader() {
+ if (esClassLoader != null) {
+ Thread.currentThread().setContextClassLoader(esClassLoader);
+ }
+ }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java
new file mode 100644
index 0000000..f9a6837
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java
@@ -0,0 +1,122 @@
+/*
+ * 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.authorization.elasticsearch.plugin;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.UnaryOperator;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.authorization.elasticsearch.plugin.action.filter.RangerSecurityActionFilter;
+import org.apache.ranger.authorization.elasticsearch.plugin.rest.filter.RangerSecurityRestFilter;
+import org.elasticsearch.action.support.ActionFilter;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.env.Environment;
+import org.elasticsearch.env.NodeEnvironment;
+import org.elasticsearch.plugins.ActionPlugin;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.rest.RestHandler;
+import org.elasticsearch.script.ScriptService;
+import org.elasticsearch.threadpool.ThreadPool;
+import org.elasticsearch.watcher.ResourceWatcherService;
+
+public class RangerElasticsearchPlugin extends Plugin implements ActionPlugin {
+
+ private static final Logger LOG = ESLoggerFactory.getLogger(RangerElasticsearchPlugin.class);
+
+ private static final String RANGER_ELASTICSEARCH_PLUGIN_CONF_NAME = "ranger-elasticsearch-plugin";
+
+ private final Settings settings;
+
+ private RangerSecurityActionFilter rangerSecurityActionFilter;
+
+ public RangerElasticsearchPlugin(Settings settings) {
+ this.settings = settings;
+ }
+
+ @Override
+ public List<ActionFilter> getActionFilters() {
+ return Collections.singletonList(rangerSecurityActionFilter);
+ }
+
+ @Override
+ public UnaryOperator<RestHandler> getRestHandlerWrapper(ThreadContext threadContext) {
+ return (UnaryOperator<RestHandler>) (handler -> new RangerSecurityRestFilter(this.settings, threadContext,
+ handler));
+ }
+
+ @Override
+ public Collection<Object> createComponents(final Client client, final ClusterService clusterService,
+ final ThreadPool threadPool, final ResourceWatcherService resourceWatcherService,
+ final ScriptService scriptService, final NamedXContentRegistry xContentRegistry,
+ final Environment environment, final NodeEnvironment nodeEnvironment,
+ final NamedWriteableRegistry namedWriteableRegistry) {
+
+ addPluginConfig2Classpath(environment);
+
+ rangerSecurityActionFilter = new RangerSecurityActionFilter(this.settings, threadPool.getThreadContext());
+ return Collections.singletonList(rangerSecurityActionFilter);
+ }
+
+ /**
+ * Add ranger elasticsearch plugin config directory to classpath,
+ * then the plugin can load its configuration files from classpath.
+ */
+ private void addPluginConfig2Classpath(Environment environment) {
+ Path configPath = environment.configFile().resolve(RANGER_ELASTICSEARCH_PLUGIN_CONF_NAME);
+ if (configPath == null) {
+ LOG.error(
+ "Failed to add ranger elasticsearch plugin config directory [ranger-elasticsearch-plugin] to classpath.");
+ return;
+ }
+ File configFile = configPath.toFile();
+
+ try {
+ if (configFile.exists()) {
+ ClassLoader classLoader = this.getClass().getClassLoader();
+ // This classLoader is FactoryURLClassLoader in eleasticsearch
+ if (classLoader instanceof URLClassLoader) {
+ URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+ Class<? extends URLClassLoader> urlClass = urlClassLoader.getClass();
+ Method method = urlClass.getSuperclass().getDeclaredMethod("addURL", new Class[] { URL.class });
+ method.setAccessible(true);
+ method.invoke(urlClassLoader, new Object[] { configFile.toURI().toURL() });
+ LOG.info("Success to add ranger elasticsearch plugin config directory [{}] to classpath.",
+ configFile.getCanonicalPath());
+ }
+ }
+ } catch (Exception e) {
+ LOG.error(
+ "Failed to add ranger elasticsearch plugin config directory [ranger-elasticsearch-plugin] to classpath.",
+ e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java
new file mode 100644
index 0000000..60baaea
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.authorization.elasticsearch.plugin.action.filter;
+
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.authorization.elasticsearch.authorizer.RangerElasticsearchAuthorizer;
+import org.apache.ranger.authorization.elasticsearch.plugin.authc.user.UsernamePasswordToken;
+import org.apache.ranger.authorization.elasticsearch.plugin.utils.RequestUtils;
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.ActionRequest;
+import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.action.support.ActionFilter;
+import org.elasticsearch.action.support.ActionFilterChain;
+import org.elasticsearch.common.component.AbstractComponent;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.tasks.Task;
+
+public class RangerSecurityActionFilter extends AbstractComponent implements ActionFilter {
+
+ private static final Logger LOG = ESLoggerFactory.getLogger(RangerSecurityActionFilter.class);
+
+ private final ThreadContext threadContext;
+
+ private final RangerElasticsearchAuthorizer rangerElasticsearchAuthorizer = new RangerElasticsearchAuthorizer();
+
+ public RangerSecurityActionFilter(Settings settings, ThreadContext threadContext) {
+ super(settings);
+ this.threadContext = threadContext;
+ }
+
+ @Override
+ public int order() {
+ return 0;
+ }
+
+ @Override
+ public <Request extends ActionRequest, Response extends ActionResponse> void apply(Task task, String action,
+ Request request, ActionListener<Response> listener, ActionFilterChain<Request, Response> chain) {
+ String user = threadContext.getTransient(UsernamePasswordToken.USERNAME);
+ // If user is not null, then should check permission of the outside caller.
+ if (StringUtils.isNotEmpty(user)) {
+ List<String> indexs = RequestUtils.getIndexFromRequest(request);
+ String clientIPAddress = threadContext.getTransient(RequestUtils.CLIENT_IP_ADDRESS);
+ for (String index : indexs) {
+ boolean result = rangerElasticsearchAuthorizer.checkPermission(user, null, index, action,
+ clientIPAddress);
+ if (!result) {
+ String errorMsg = "Error: User[{}] could not do action[{}] on index[{}]";
+ throw new ElasticsearchStatusException(errorMsg, RestStatus.FORBIDDEN, user, action, index);
+ }
+ }
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("User is null, no check permission for elasticsearch do action[{}] with request[{}]", action,
+ request);
+ }
+ }
+ chain.proceed(task, action, request, listener);
+ }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java
new file mode 100644
index 0000000..c1df938
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java
@@ -0,0 +1,103 @@
+/*
+ * 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.authorization.elasticsearch.plugin.authc.user;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.RestStatus;
+
+public class UsernamePasswordToken {
+
+ public static final String USERNAME = "username";
+
+ public static final String BASIC_AUTH_PREFIX = "Basic ";
+
+ public static final String BASIC_AUTH_HEADER = "Authorization";
+
+ private String username;
+
+ private String password;
+
+ public UsernamePasswordToken(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ public static UsernamePasswordToken parseToken(RestRequest request) {
+
+ Map<String, List<String>> headers = request.getHeaders();
+ if (MapUtils.isEmpty(headers)) {
+ return null;
+ }
+ List<String> authStrs = headers.get(BASIC_AUTH_HEADER);
+ if (CollectionUtils.isEmpty(authStrs)) {
+ return null;
+ }
+
+ String authStr = authStrs.get(0);
+ if (StringUtils.isEmpty(authStr)) {
+ return null;
+ }
+
+ String userPass = "";
+ try {
+ userPass = new String(Base64.getUrlDecoder().decode(authStr.substring(BASIC_AUTH_PREFIX.length())));
+ } catch (IllegalArgumentException e) {
+ throw new ElasticsearchStatusException("Error: Failed to parse user authentication.",
+ RestStatus.UNAUTHORIZED, e);
+ }
+
+ int i = StringUtils.indexOf(userPass, ':');
+ if (i <= 0) {
+ throw new ElasticsearchStatusException(
+ "Error: Parse user authentication to get the wrong userPass[{}].",
+ RestStatus.UNAUTHORIZED, userPass);
+ }
+ return new UsernamePasswordToken(StringUtils.substring(userPass, 0, i),
+ StringUtils.substring(userPass, i + 1, userPass.length()));
+
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public String toString() {
+ return "UsernamePasswordToken [username=" + username + ", password=" + "******" + "]";
+ }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java
new file mode 100644
index 0000000..117356a
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.authorization.elasticsearch.plugin.rest.filter;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.authorization.elasticsearch.plugin.authc.user.UsernamePasswordToken;
+import org.apache.ranger.authorization.elasticsearch.plugin.utils.RequestUtils;
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.common.component.AbstractComponent;
+import org.elasticsearch.common.logging.ESLoggerFactory;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.rest.RestChannel;
+import org.elasticsearch.rest.RestHandler;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.RestStatus;
+
+public class RangerSecurityRestFilter extends AbstractComponent implements RestHandler {
+
+ private static final Logger LOG = ESLoggerFactory.getLogger(RangerSecurityRestFilter.class);
+
+ private final RestHandler restHandler;
+
+ private final ThreadContext threadContext;
+
+ public RangerSecurityRestFilter(final Settings settings, final ThreadContext threadContext,
+ final RestHandler restHandler) {
+ super(settings);
+ this.restHandler = restHandler;
+ this.threadContext = threadContext;
+ }
+
+ @Override
+ public void handleRequest(final RestRequest request, final RestChannel channel, final NodeClient client)
+ throws Exception {
+ // Now only support to get user from request,
+ // it should work with other elasticsearch identity authentication plugins in fact.
+ UsernamePasswordToken user = UsernamePasswordToken.parseToken(request);
+ if (user == null) {
+ throw new ElasticsearchStatusException("Error: User is null, the request requires user authentication.",
+ RestStatus.UNAUTHORIZED);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Success to parse user[{}] from request[{}].", user, request);
+ }
+ }
+ threadContext.putTransient(UsernamePasswordToken.USERNAME, user.getUsername());
+
+ String clientIPAddress = RequestUtils.getClientIPAddress(request);
+ if (StringUtils.isNotEmpty(clientIPAddress)) {
+ threadContext.putTransient(RequestUtils.CLIENT_IP_ADDRESS, clientIPAddress);
+ }
+
+ this.restHandler.handleRequest(request, channel, client);
+ }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/utils/RequestUtils.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/utils/RequestUtils.java
new file mode 100644
index 0000000..8a04b06
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/utils/RequestUtils.java
@@ -0,0 +1,126 @@
+/*
+ * 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.authorization.elasticsearch.plugin.utils;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.elasticsearch.action.ActionRequest;
+import org.elasticsearch.action.DocWriteRequest;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
+import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
+import org.elasticsearch.action.bulk.BulkRequest;
+import org.elasticsearch.action.get.MultiGetRequest;
+import org.elasticsearch.action.get.MultiGetRequest.Item;
+import org.elasticsearch.action.search.SearchRequest;
+import org.elasticsearch.action.support.replication.ReplicationRequest;
+import org.elasticsearch.action.support.single.instance.InstanceShardOperationRequest;
+import org.elasticsearch.action.support.single.shard.SingleShardRequest;
+import org.elasticsearch.rest.RestRequest;
+
+public class RequestUtils {
+ public static final String CLIENT_IP_ADDRESS = "ClientIPAddress";
+
+ public static String getClientIPAddress(RestRequest request) {
+ SocketAddress socketAddress = request.getRemoteAddress();
+ if (socketAddress instanceof InetSocketAddress) {
+ return ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
+ }
+
+ return null;
+ }
+
+ // To support all kinds of request in elasticsearch
+ public static <Request extends ActionRequest> List<String> getIndexFromRequest(Request request) {
+ List<String> indexs = new ArrayList<>();
+
+ if (request instanceof SingleShardRequest) {
+ indexs.add(((SingleShardRequest<?>) request).index());
+ return indexs;
+ }
+
+ if (request instanceof ReplicationRequest) {
+ indexs.add(((ReplicationRequest<?>) request).index());
+ return indexs;
+ }
+
+ if (request instanceof InstanceShardOperationRequest) {
+ indexs.add(((InstanceShardOperationRequest<?>) request).index());
+ return indexs;
+ }
+
+ if (request instanceof CreateIndexRequest) {
+ indexs.add(((CreateIndexRequest) request).index());
+ return indexs;
+ }
+
+ if (request instanceof PutMappingRequest) {
+ indexs.add(((PutMappingRequest) request).getConcreteIndex().getName());
+ return indexs;
+ }
+
+ if (request instanceof SearchRequest) {
+ return Arrays.asList(((SearchRequest) request).indices());
+ }
+
+ if (request instanceof IndicesStatsRequest) {
+ return Arrays.asList(((IndicesStatsRequest) request).indices());
+ }
+
+ if (request instanceof OpenIndexRequest) {
+ return Arrays.asList(((OpenIndexRequest) request).indices());
+ }
+
+ if (request instanceof DeleteIndexRequest) {
+ return Arrays.asList(((DeleteIndexRequest) request).indices());
+ }
+
+ if (request instanceof BulkRequest) {
+ @SuppressWarnings("rawtypes")
+ List<DocWriteRequest> requests = ((BulkRequest) request).requests();
+
+ if (CollectionUtils.isNotEmpty(requests)) {
+ for (DocWriteRequest<?> docWriteRequest : requests) {
+ indexs.add(docWriteRequest.index());
+ }
+ return indexs;
+ }
+ }
+
+ if (request instanceof MultiGetRequest) {
+ List<Item> items = ((MultiGetRequest) request).getItems();
+ if (CollectionUtils.isNotEmpty(items)) {
+ for (Item item : items) {
+ indexs.add(item.index());
+ }
+ return indexs;
+ }
+ }
+
+ // No matched request type to find specific index , set default value *
+ indexs.add("*");
+ return indexs;
+ }
+}
diff --git a/src/main/assembly/admin-web.xml b/src/main/assembly/admin-web.xml
index b3ec885..8ea728b 100644
--- a/src/main/assembly/admin-web.xml
+++ b/src/main/assembly/admin-web.xml
@@ -376,6 +376,18 @@
</includes>
</moduleSet>
+ <moduleSet>
+ <binaries>
+ <includeDependencies>true</includeDependencies>
+ <outputDirectory>/ews/webapp/WEB-INF/classes/ranger-plugins/elasticsearch</outputDirectory>
+ <unpack>false</unpack>
+ <directoryMode>755</directoryMode>
+ <fileMode>644</fileMode>
+ </binaries>
+ <includes>
+ <include>org.apache.ranger:ranger-elasticsearch-plugin</include>
+ </includes>
+ </moduleSet>
</moduleSets>
<fileSets>
diff --git a/src/main/assembly/plugin-elasticsearch.xml b/src/main/assembly/plugin-elasticsearch.xml
new file mode 100644
index 0000000..41f73a3
--- /dev/null
+++ b/src/main/assembly/plugin-elasticsearch.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<assembly>
+ <id>elasticsearch-plugin</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+ <baseDirectory>${project.name}-${project.version}-elasticsearch-plugin</baseDirectory>
+ <includeBaseDirectory>true</includeBaseDirectory>
+ <moduleSets>
+ <moduleSet>
+ <binaries>
+ <includeDependencies>false</includeDependencies>
+ <unpack>false</unpack>
+ <directoryMode>755</directoryMode>
+ <fileMode>644</fileMode>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin</outputDirectory>
+ <unpack>false</unpack>
+ <includes>
+ <include>commons-collections:commons-collections</include>
+ <include>commons-lang:commons-lang</include>
+ <include>org.slf4j:slf4j-api</include>
+ <include>org.slf4j:slf4j-log4j12</include>
+ </includes>
+ </dependencySet>
+ </dependencySets>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin</outputDirectory>
+ </binaries>
+ <includes>
+ <include>org.apache.ranger:ranger-elasticsearch-plugin-shim</include>
+ <include>org.apache.ranger:ranger-plugin-classloader</include>
+ </includes>
+ </moduleSet>
+
+ <moduleSet>
+ <binaries>
+ <includeDependencies>false</includeDependencies>
+ <unpack>false</unpack>
+ <directoryMode>755</directoryMode>
+ <fileMode>644</fileMode>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin/ranger-elasticsearch-plugin-impl</outputDirectory>
+ <unpack>false</unpack>
+ <includes>
+ <include>commons-configuration:commons-configuration:jar:${commons.configuration.version}</include>
+ <include>org.apache.hadoop:hadoop-common:jar:${hadoop.version}</include>
+ <include>org.apache.hadoop:hadoop-common-plus:jar:${hadoop.version}</include>
+ <include>org.apache.hadoop:hadoop-auth:jar:${hadoop.version}</include>
+ <include>org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version}</include>
+ <include>com.google.code.gson:gson</include>
+ <include>org.eclipse.persistence:eclipselink</include>
+ <include>org.eclipse.persistence:javax.persistence</include>
+ <include>com.sun.jersey:jersey-bundle</include>
+ <include>commons-logging:commons-logging:jar:${commons.logging.version}</include>
+ <include>commons-io:commons-io</include>
+ <include>com.google.guava:guava:jar:${google.guava.version}</include>
+ <include>org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version}</include>
+ <include>org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version}</include>
+ <include>org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version}</include>
+ <include>org.noggit:noggit:jar:${noggit.version}</include>
+ <include>org.codehaus.jackson:jackson-core-asl</include>
+ <include>org.codehaus.jackson:jackson-jaxrs</include>
+ <include>org.codehaus.jackson:jackson-mapper-asl</include>
+ <include>org.codehaus.jackson:jackson-xc</include>
+ <include>org.apache.solr:solr-solrj</include>
+ <include>commons-codec:commons-codec</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>/install/lib</outputDirectory>
+ <unpack>false</unpack>
+ <directoryMode>755</directoryMode>
+ <fileMode>644</fileMode>
+ <includes>
+ <include>commons-cli:commons-cli</include>
+ <include>commons-collections:commons-collections</include>
+ <include>commons-configuration:commons-configuration:jar:${commons.configuration.version}</include>
+ <include>commons-io:commons-io:jar:${commons.io.version}</include>
+ <include>commons-lang:commons-lang:jar:${commons.lang.version}</include>
+ <include>commons-logging:commons-logging:jar:${commons.logging.version}</include>
+ <include>com.google.guava:guava:jar:${google.guava.version}</include>
+ <include>org.slf4j:slf4j-api:jar:${slf4j-api.version}</include>
+ <include>org.apache.hadoop:hadoop-common:jar:${hadoop.version}</include>
+ <include>org.apache.hadoop:hadoop-auth:jar:${hadoop.version}</include>
+ <include>org.apache.ranger:ranger-plugins-cred</include>
+ <include>org.apache.ranger:credentialbuilder</include>
+ </includes>
+ </dependencySet>
+ </dependencySets>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin/ranger-elasticsearch-plugin-impl</outputDirectory>
+ </binaries>
+ <includes>
+ <include>org.apache.ranger:ranger-plugins-audit</include>
+ <include>org.apache.ranger:ranger-plugins-cred</include>
+ <include>org.apache.ranger:ranger-plugins-common</include>
+ <include>org.apache.ranger:ranger-elasticsearch-plugin</include>
+ </includes>
+ </moduleSet>
+ <moduleSet>
+ <binaries>
+ <includeDependencies>true</includeDependencies>
+ <outputDirectory>/install/lib</outputDirectory>
+ <unpack>false</unpack>
+ </binaries>
+ <includes>
+ <include>org.apache.ranger:ranger-plugins-installer</include>
+ <include>org.apache.ranger:credentialbuilder</include>
+ </includes>
+ </moduleSet>
+ </moduleSets>
+ <fileSets>
+ <!-- conf.templates for enable -->
+ <fileSet>
+ <outputDirectory>/install/conf.templates/enable</outputDirectory>
+ <directory>plugin-elasticsearch/conf</directory>
+ <excludes>
+ <exclude>*.sh</exclude>
+ </excludes>
+ <fileMode>700</fileMode>
+ </fileSet>
+ <fileSet>
+ <outputDirectory>/install/conf.templates/disable</outputDirectory>
+ <directory>plugin-elasticsearch/disable-conf</directory>
+ <fileMode>700</fileMode>
+ </fileSet>
+ <fileSet>
+ <outputDirectory>/install/conf.templates/default</outputDirectory>
+ <directory>plugin-elasticsearch/template</directory>
+ <fileMode>700</fileMode>
+ </fileSet>
+ <!-- version file -->
+ <fileSet>
+ <outputDirectory>/</outputDirectory>
+ <directory>${project.build.outputDirectory}</directory>
+ <includes>
+ <include>version</include>
+ </includes>
+ <fileMode>444</fileMode>
+ </fileSet>
+ </fileSets>
+ <!-- enable/disable script for Plugin -->
+ <files>
+ <file>
+ <source>agents-common/scripts/enable-agent.sh</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>enable-elasticsearch-plugin.sh</destName>
+ <fileMode>755</fileMode>
+ </file>
+ <file>
+ <source>agents-common/scripts/enable-agent.sh</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>disable-elasticsearch-plugin.sh</destName>
+ <fileMode>755</fileMode>
+ </file>
+ <file>
+ <source>plugin-elasticsearch/scripts/install.properties</source>
+ <outputDirectory>/</outputDirectory>
+ <destName>install.properties</destName>
+ <fileMode>755</fileMode>
+ </file>
+ <file>
+ <source>security-admin/scripts/ranger_credential_helper.py</source>
+ <outputDirectory>/</outputDirectory>
+ <fileMode>755</fileMode>
+ </file>
+ <file>
+ <source>ranger-elasticsearch-plugin-shim/conf/plugin-descriptor.properties</source>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin</outputDirectory>
+ <fileMode>755</fileMode>
+ <filtered>true</filtered>
+ </file>
+ <file>
+ <source>ranger-elasticsearch-plugin-shim/conf/plugin-security.policy</source>
+ <outputDirectory>/lib/ranger-elasticsearch-plugin</outputDirectory>
+ <fileMode>755</fileMode>
+ </file>
+ </files>
+</assembly>