Prechádzať zdrojové kódy

HUE-219. Add the ability to specify a keytab for Hue which gets periodically renewed

Todd Lipcon 15 rokov pred
rodič
commit
20c405cd83

+ 3 - 1
desktop/core/setup.py

@@ -26,6 +26,8 @@ setup(
 
       entry_points = { 'console_scripts': [ 'supervisor = desktop.supervisor:main',
                                             'hue = desktop.manage_entry:entry', ],
-                       'desktop.supervisor.specs': [ 'runcpserver = desktop:SUPERVISOR_SPEC' ]
+                       'desktop.supervisor.specs': [ 'runcpserver = desktop:SUPERVISOR_SPEC',
+                                                     'kt_renewer = desktop.kt_renewer:SPEC',
+                                                     ]
                        },
       )

+ 54 - 0
desktop/core/src/desktop/conf.py

@@ -172,6 +172,44 @@ DATABASE = ConfigSection(
   )
 )
 
+KERBEROS = ConfigSection(
+  key="kerberos",
+  help="""Configuration options for specifying Hue's kerberos integration for
+          secured Hadoop clusters.""",
+  members=dict(
+    HUE_KEYTAB=Config(
+      key='hue_keytab',
+      help="Path to a Kerberos keytab file containing Hue's service credentials.",
+      type=str,
+      default=None),
+    HUE_PRINCIPAL=Config(
+      key='hue_principal',
+      help="Kerberos principal name for hue. Typically 'hue/hostname.foo.com'",
+      type=str,
+      default="hue/%s" % socket.getfqdn()),
+    KEYTAB_REINIT_FREQUENCY=Config(
+      key='reinit_frequency',
+      help="Frequency in seconds with which Hue will renew its keytab",
+      type=int,
+      default=60*60), #1h
+    CCACHE_PATH=Config(
+      key='ccache_path',
+      help="Path to keep kerberos credentials cached",
+      private=True,
+      type=str,
+      default="/tmp/hue_krb5_ccache_%d" % os.geteuid(),
+    ),
+    KINIT_PATH=Config(
+      key='kinit_path',
+      help="Path to kerberos 'kinit' command",
+      type=str,
+      default="kinit", # use PATH!
+    )
+  )
+)
+
+      
+
 # See python's documentation for time.tzset for valid values.
 TIME_ZONE = Config(
   key="time_zone",
@@ -276,13 +314,29 @@ def config_validator():
   if not SECRET_KEY.get():
     res.append((SECRET_KEY, "Secret key should be configured as a random string."))
 
+  # Validate SSL setup
   if SSL_CERTIFICATE.get():
     res.extend(validate_path(SSL_CERTIFICATE, is_dir=False))
     if not SSL_PRIVATE_KEY.get():
       res.append((SSL_PRIVATE_KEY, "SSL private key file should be set to enable HTTPS."))
     else:
       res.extend(validate_path(SSL_PRIVATE_KEY, is_dir=False))
+
+  # Validate encoding
   if not i18n.validate_encoding(DEFAULT_SITE_ENCODING.get()):
     res.append((DEFAULT_SITE_ENCODING, "Encoding not supported."))
 
+  # Validate kerberos
+  if KERBEROS.HUE_KEYTAB.get() is not None:
+    res.extend(validate_path(KERBEROS.HUE_KEYTAB, is_dir=False))
+    # Keytab should not be world or group accessible
+    kt_stat = os.stat(KERBEROS.HUE_KEYTAB.get())
+    if stat.S_IMODE(kt_stat) & 0077:
+      res.append((KERBEROS.HUE_KEYTAB,
+                  "Keytab should have 0600 permissions (has %o)" %
+                  stat.S_IMODE(kt_stat)))
+
+    res.extend(validate_path(KERBEROS.KINIT_PATH, is_dir=False))
+    res.extend(validate_path(KERBEROS.CCACHE_PATH, is_dir=False))
+
   return res

+ 49 - 0
desktop/core/src/desktop/kt_renewer.py

@@ -0,0 +1,49 @@
+#!/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 logging
+import subprocess
+import sys
+import time
+from desktop.supervisor import DjangoCommandSupervisee
+from desktop.conf import KERBEROS as CONF
+
+LOG = logging.getLogger(__name__)
+
+SPEC = DjangoCommandSupervisee("kt_renewer")
+
+def renew_from_kt():
+  cmdv = [CONF.KINIT_PATH.get(),
+          "-k", # host ticket
+          "-t", CONF.HUE_KEYTAB.get(), # specify keytab
+          "-c", CONF.CCACHE_PATH.get(), # specify credentials cache
+          CONF.HUE_PRINCIPAL.get()]
+  LOG.info("Reinitting kerberos from keytab: " +
+           " ".join(cmdv))
+  ret = subprocess.call(cmdv)
+  if ret != 0:
+    LOG.error("Couldn't reinit from keytab!")
+    sys.exit(ret)
+
+def run():
+  if CONF.HUE_KEYTAB.get() is None:
+    LOG.debug("Keytab renewer not starting, no keytab configured")
+    sys.exit(0)
+
+  while True:
+    renew_from_kt()
+    time.sleep(CONF.KEYTAB_REINIT_FREQUENCY.get())

+ 25 - 0
desktop/core/src/desktop/management/commands/kt_renewer.py

@@ -0,0 +1,25 @@
+#!/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.
+
+from django.core.management.base import NoArgsCommand
+import desktop.kt_renewer
+
+class Command(NoArgsCommand):
+  """ Starts a daemon which renews Kerberos credentials from a keytab
+  periodically. """
+  def handle_noargs(self, **options):
+    desktop.kt_renewer.run()

+ 4 - 0
desktop/core/src/desktop/settings.py

@@ -251,3 +251,7 @@ DEPENDER_DEBUG = os.getenv("DESKTOP_DEPENDER_DEBUG", "0") not in ["0",""]
 
 # Necessary for South to not futz with tests.  Fixed in South 0.7.1
 SKIP_SOUTH_TESTS = True
+
+# Set up environment variable so Kerberos libraries look at our private
+# ticket cache
+os.environ['KRB5CCNAME'] = desktop.conf.KERBEROS.CCACHE_PATH.get()