kt_renewer.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #!/usr/bin/env python
  2. # Licensed to Cloudera, Inc. under one
  3. # or more contributor license agreements. See the NOTICE file
  4. # distributed with this work for additional information
  5. # regarding copyright ownership. Cloudera, Inc. licenses this file
  6. # to you under the Apache License, Version 2.0 (the
  7. # "License"); you may not use this file except in compliance
  8. # with the License. You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. import logging
  18. import subprocess
  19. import sys
  20. import time
  21. from desktop.conf import KERBEROS as CONF
  22. from desktop.supervisor import DjangoCommandSupervisee
  23. LOG = logging.getLogger()
  24. SPEC = DjangoCommandSupervisee("kt_renewer")
  25. NEED_KRB181_WORKAROUND = None
  26. def renew_from_kt():
  27. cmdv = [CONF.KINIT_PATH.get(),
  28. "-k", # host ticket
  29. "-t", CONF.HUE_KEYTAB.get(), # specify keytab
  30. "-c", CONF.CCACHE_PATH.get(), # specify credentials cache
  31. CONF.HUE_PRINCIPAL.get()]
  32. retries = 0
  33. max_retries = 3
  34. while retries < max_retries:
  35. LOG.info("Reinitting kerberos retry attempt %s from keytab %s" % (retries, " ".join(cmdv)))
  36. subp = subprocess.Popen(cmdv, stdout=subprocess.PIPE,
  37. stderr=subprocess.PIPE, close_fds=True,
  38. bufsize=-1)
  39. subp.wait()
  40. max_retries = 0 if subp.returncode == 0 else max_retries
  41. if subp.returncode != 0:
  42. retries = retries + 1
  43. subp_stdout = subp.stdout.readlines()
  44. subp_stderr = subp.stderr.readlines()
  45. subp_stdout = [line.decode() for line in subp_stdout]
  46. subp_stderr = [line.decode() for line in subp_stderr]
  47. LOG.error("Couldn't reinit from keytab! `kinit' exited with %s.\n%s\n%s" % (
  48. subp.returncode, "\n".join(subp_stdout), "\n".join(subp_stderr)))
  49. if retries >= max_retries:
  50. LOG.error("FATAL: max_retries of %s reached. Exiting..." % max_retries)
  51. sys.exit(subp.returncode)
  52. time.sleep(CONF.KT_RENEWER_TIMEOUT.get())
  53. elif CONF.ENABLE_RENEWLIFETIME.get() and max_retries == 0:
  54. break
  55. global NEED_KRB181_WORKAROUND
  56. if NEED_KRB181_WORKAROUND is None:
  57. NEED_KRB181_WORKAROUND = detect_conf_var()
  58. if NEED_KRB181_WORKAROUND and CONF.ENABLE_RENEWLIFETIME.get():
  59. # HUE-640. Kerberos clock have seconds level granularity. Make sure we
  60. # renew the ticket after the initial valid time.
  61. time.sleep(1.5)
  62. perform_krb181_workaround()
  63. def perform_krb181_workaround():
  64. cmdv = [CONF.KINIT_PATH.get(),
  65. "-R",
  66. "-c", CONF.CCACHE_PATH.get()]
  67. LOG.info("Renewing kerberos ticket to work around kerberos 1.8.1: " +
  68. " ".join(cmdv))
  69. ret = subprocess.call(cmdv)
  70. if ret != 0:
  71. fmt_dict = dict(princ=CONF.HUE_PRINCIPAL.get(),
  72. ccache=CONF.CCACHE_PATH.get())
  73. LOG.error("Couldn't renew kerberos ticket in order to work around "
  74. "Kerberos 1.8.1 issue. Please check that the ticket for "
  75. "'%(princ)s' is still renewable:\n"
  76. " $ klist -f -c %(ccache)s\n"
  77. "If the 'renew until' date is the same as the 'valid starting' "
  78. "date, the ticket cannot be renewed. Please check your KDC "
  79. "configuration, and the ticket renewal policy (maxrenewlife) "
  80. "for the '%(princ)s' and `krbtgt' principals." % fmt_dict)
  81. sys.exit(ret)
  82. def detect_conf_var():
  83. """Return true if the ticket cache contains "conf" information as is found
  84. in ticket caches of Kerboers 1.8.1 or later. This is incompatible with the
  85. Sun Java Krb5LoginModule in Java6, so we need to take an action to work
  86. around it.
  87. """
  88. try:
  89. # TODO: the binary check for X-CACHECONF seems fragile, it should be replaced
  90. # with something more robust.
  91. f = open(CONF.CCACHE_PATH.get(), "rb")
  92. data = f.read()
  93. return b"X-CACHECONF:" in data
  94. finally:
  95. f.close()
  96. def run():
  97. if CONF.HUE_KEYTAB.get() is None:
  98. LOG.info("Keytab renewer not starting, no keytab configured")
  99. sys.exit(0)
  100. while True:
  101. renew_from_kt()
  102. time.sleep(CONF.REINIT_FREQUENCY.get())