ソースを参照

HUE-2205 [hbase] Kerberos support

Romain Rigaux 11 年 前
コミット
4f29947

+ 17 - 2
apps/hbase/src/hbase/api.py

@@ -30,6 +30,7 @@ from desktop.lib.exceptions_renderable import PopupException
 
 from hbase.server.hbase_lib import get_thrift_type, get_client_type
 from hbase import conf
+from hbase.hbase_site import get_server_principal, get_server_authentication
 
 LOG = logging.getLogger(__name__)
 
@@ -83,15 +84,29 @@ class HbaseApi(object):
     raise PopupException(_("Cluster by the name of %s does not exist in configuration.") % name)
 
   def connectCluster(self, name):
+    _security = self._get_security()
     target = self.getCluster(name)
     return thrift_util.get_client(get_client_type(),
                                   target['host'],
                                   target['port'],
                                   service_name="Hue HBase Thrift Client for %s" % name,
-                                  kerberos_principal=None,
-                                  use_sasl=False,
+                                  kerberos_principal=_security['kerberos_principal_short_name'],
+                                  use_sasl=_security['use_sasl'],
                                   timeout_seconds=None,
                                   transport=conf.THRIFT_TRANSPORT.get())
+  @classmethod
+  def _get_security(cls):
+    principal = get_server_principal()
+    if principal:
+      kerberos_principal_short_name = principal.split('/', 1)[0]
+    else:
+      kerberos_principal_short_name = None
+    use_sasl = get_server_authentication() == 'KERBEROS'
+
+    return {
+        'kerberos_principal_short_name': kerberos_principal_short_name,
+        'use_sasl': use_sasl,
+    }
 
   def get(self, cluster, tableName, row, column, attributes):
     client = self.connectCluster(cluster)

+ 15 - 4
apps/hbase/src/hbase/conf.py

@@ -15,30 +15,41 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
+
+from django.utils.translation import ugettext_lazy as _t
 
 from desktop.lib.conf import Config, validate_thrift_transport
 
+
 HBASE_CLUSTERS = Config(
   key="hbase_clusters",
   default="(Cluster|localhost:9090)",
-  help="Comma-separated list of HBase Thrift servers for clusters in the format of '(name|host:port)'.",
+  help=_t("Comma-separated list of HBase Thrift servers for clusters in the format of '(name|host:port)'. Use full hostname with security."),
   type=str)
 
 TRUNCATE_LIMIT = Config(
   key="truncate_limit",
   default="500",
-  help="Hard limit of rows or columns per row fetched before truncating.",
+  help=_t("Hard limit of rows or columns per row fetched before truncating."),
   type=int)
 
 THRIFT_TRANSPORT = Config(
   key="thrift_transport",
   default="buffered",
-  help="'buffered' is the default of the HBase Thrift Server. " +
+  help=_t("'buffered' is the default of the HBase Thrift Server and supports security. " +
        "'framed' can be used to chunk up responses, " +
-       "which is useful when used in conjunction with the nonblocking server in Thrift.",
+       "which is useful when used in conjunction with the nonblocking server in Thrift."),
   type=str
 )
 
+HBASE_CONF_DIR = Config(
+  key='hbase_conf_dir',
+  help=_t('HBase configuration directory, where hbase-site.xml is located.'),
+  default=os.environ.get("HBASE_CONF_DIR", '/etc/hbase/conf')
+)
+
+
 
 def config_validator(user):
   res = []

+ 74 - 0
apps/hbase/src/hbase/hbase_site.py

@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. 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.
+
+import errno
+import logging
+import os.path
+
+from hadoop import confparse
+from desktop.lib.security_util import get_components
+
+from hbase.conf import HBASE_CONF_DIR
+
+
+LOG = logging.getLogger(__name__)
+
+
+SITE_PATH = None
+SITE_DICT = None
+
+_CNF_HBASE_THRIFT_KERBEROS_PRINCIPAL = 'hbase.thrift.kerberos.principal'
+_CNF_HBASE_AUTHENTICATION = 'hbase.security.authentication'
+
+
+def reset():
+  global SITE_DICT
+  SITE_DICT = None
+
+
+def get_conf():
+  if SITE_DICT is None:
+    _parse_site()
+  return SITE_DICT
+
+
+def get_server_principal():
+  principal = get_conf().get(_CNF_HBASE_THRIFT_KERBEROS_PRINCIPAL, None)
+  components = get_components(principal)
+  if components is not None:
+    return components[0]
+
+
+def get_server_authentication():
+  return get_conf().get(_CNF_HBASE_AUTHENTICATION, 'NOSASL').upper()
+
+
+def _parse_site():
+  global SITE_DICT
+  global SITE_PATH
+
+  SITE_PATH = os.path.join(HBASE_CONF_DIR.get(), 'hbase-site.xml')
+  try:
+    data = file(SITE_PATH, 'r').read()
+  except IOError, err:
+    if err.errno != errno.ENOENT:
+      LOG.error('Cannot read from "%s": %s' % (SITE_PATH, err))
+      return
+    data = ""
+
+  SITE_DICT = confparse.ConfParse(data)
+

+ 73 - 7
apps/hbase/src/hbase/tests.py

@@ -15,14 +15,80 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import os
+import shutil
+import tempfile
 
-import json
-import time
+from nose.tools import assert_true, assert_equal
 
-from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
+from hbase.api import HbaseApi
+from hbase.conf import HBASE_CONF_DIR
+from hbase.hbase_site import get_server_authentication, get_server_principal, reset
 
-from nose.tools import assert_true, assert_equal
 
-from desktop.lib.django_test_util import make_logged_in_client
-from desktop.lib.test_utils import grant_access
+def test_security_plain():
+  tmpdir = tempfile.mkdtemp()
+  finish = HBASE_CONF_DIR.set_for_testing(tmpdir)
+
+  try:
+    xml = hbase_site_xml()
+    file(os.path.join(tmpdir, 'hbase-site.xml'), 'w').write(xml)
+    reset()
+
+    assert_equal('NOSASL', get_server_authentication())
+    assert_equal('test', get_server_principal())
+
+    security = HbaseApi._get_security()
+
+    assert_equal('test', security['kerberos_principal_short_name'])
+    assert_equal(False, security['use_sasl'])
+  finally:
+    reset()
+    finish()
+    shutil.rmtree(tmpdir)
+
+
+def test_security_kerberos():
+  tmpdir = tempfile.mkdtemp()
+  finish = HBASE_CONF_DIR.set_for_testing(tmpdir)
+
+  try:
+    xml = hbase_site_xml(authentication='kerberos')
+    file(os.path.join(tmpdir, 'hbase-site.xml'), 'w').write(xml)
+    reset()
+
+    assert_equal('KERBEROS', get_server_authentication())
+    assert_equal('test', get_server_principal())
+
+    security = HbaseApi._get_security()
+
+    assert_equal('test', security['kerberos_principal_short_name'])
+    assert_equal(True, security['use_sasl'])
+  finally:
+    reset()
+    finish()
+    shutil.rmtree(tmpdir)
+
+
+def hbase_site_xml(
+    kerberos_principal='test/test.com@TEST.COM',
+    authentication='NOSASL'):
+
+  return """
+    <configuration>
+
+      <property>
+        <name>hbase.thrift.kerberos.principal</name>
+        <value>%(kerberos_principal)s</value>
+      </property>
+
+      <property>
+        <name>hbase.security.authentication</name>
+        <value>%(authentication)s</value>
+      </property>
+
+    </configuration>
+  """ % {
+    'kerberos_principal': kerberos_principal,
+    'authentication': authentication,
+  }

+ 6 - 3
desktop/conf.dist/hue.ini

@@ -823,14 +823,17 @@
 ###########################################################################
 
 [hbase]
-  # Comma-separated list of HBase Thrift servers for
-  # clusters in the format of '(name|host:port)'.
+  # Comma-separated list of HBase Thrift servers for clusters in the format of '(name|host:port)'.
+  # Use full hostname with security.
   ## hbase_clusters=(Cluster|localhost:9090)
 
+  # HBase configuration directory, where hbase-site.xml is located.
+  ## hbase_conf_dir=/etc/hbase/conf
+
   # Hard limit of rows or columns per row fetched before truncating.
   ## truncate_limit = 500
 
-  # 'buffered' is the default of the HBase Thrift Server.
+  # 'buffered' is the default of the HBase Thrift Server and supports security.
   # 'framed' can be used to chunk up responses,
   # which is useful when used in conjunction with the nonblocking server in Thrift.
   ## thrift_transport=buffered

+ 6 - 3
desktop/conf/pseudo-distributed.ini.tmpl

@@ -830,14 +830,17 @@
 ###########################################################################
 
 [hbase]
-  # Comma-separated list of HBase Thrift servers for
-  # clusters in the format of '(name|host:port)'.
+  # Comma-separated list of HBase Thrift servers for clusters in the format of '(name|host:port)'.
+  # Use full hostname with security.
   ## hbase_clusters=(Cluster|localhost:9090)
 
+  # HBase configuration directory, where hbase-site.xml is located.
+  ## hbase_conf_dir=/etc/hbase/conf
+
   # Hard limit of rows or columns per row fetched before truncating.
   ## truncate_limit = 500
 
-  # 'buffered' is the default of the HBase Thrift Server.
+  # 'buffered' is the default of the HBase Thrift Server and supports security.
   # 'framed' can be used to chunk up responses,
   # which is useful when used in conjunction with the nonblocking server in Thrift.
   ## thrift_transport=buffered