This is an automated email from the ASF dual-hosted git repository.

rlevas pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new dbfc6a0  [AMBARI-24085] setup-sso in Ambari fails when SSL is enabled
dbfc6a0 is described below

commit dbfc6a045856ce1050f434c0f72810258c7075f5
Author: Robert Levas <[email protected]>
AuthorDate: Tue Jun 12 18:10:31 2018 -0400

    [AMBARI-24085] setup-sso in Ambari fails when SSL is enabled
---
 .../src/main/python/ambari_server/serverUpgrade.py |  4 +-
 .../src/main/python/ambari_server/serverUtils.py   | 75 ++++++++++++++++++++--
 .../src/main/python/ambari_server/setupSecurity.py |  9 +--
 ambari-server/src/test/python/TestServerUpgrade.py |  4 +-
 ambari-server/src/test/python/TestServerUtils.py   | 39 ++++++++++-
 5 files changed, 116 insertions(+), 15 deletions(-)

diff --git a/ambari-server/src/main/python/ambari_server/serverUpgrade.py 
b/ambari-server/src/main/python/ambari_server/serverUpgrade.py
index 4f7d77a..297c853 100644
--- a/ambari-server/src/main/python/ambari_server/serverUpgrade.py
+++ b/ambari-server/src/main/python/ambari_server/serverUpgrade.py
@@ -46,7 +46,7 @@ from ambari_server.serverConfiguration import configDefaults, 
get_resources_loca
 from ambari_server.setupSecurity import adjust_directory_permissions, \
   generate_env, ensure_can_start_under_current_user
 from ambari_server.utils import compare_versions, get_json_url_from_repo_file, 
update_latest_in_repoinfos_for_all_stacks
-from ambari_server.serverUtils import is_server_runing, 
get_ambari_server_api_base
+from ambari_server.serverUtils import is_server_runing, 
get_ambari_server_api_base, get_ssl_context
 from ambari_server.userInput import get_validated_string_input, 
get_prompt_default, read_password, get_YN_input
 from ambari_server.serverClassPath import ServerClassPath
 from ambari_server.setupMpacks import replay_mpack_logs
@@ -391,7 +391,7 @@ def set_current(options):
   request.get_method = lambda: 'PUT'
 
   try:
-    response = urllib2.urlopen(request)
+    response = urllib2.urlopen(request, context=get_ssl_context(properties))
   except urllib2.HTTPError, e:
     code = e.getcode()
     content = e.read()
diff --git a/ambari-server/src/main/python/ambari_server/serverUtils.py 
b/ambari-server/src/main/python/ambari_server/serverUtils.py
index 7df85ed..be9fcde 100644
--- a/ambari-server/src/main/python/ambari_server/serverUtils.py
+++ b/ambari-server/src/main/python/ambari_server/serverUtils.py
@@ -20,6 +20,7 @@ limitations under the License.
 import base64
 import os
 import socket
+import ssl
 import urllib2
 from contextlib import closing
 
@@ -133,10 +134,7 @@ def get_ambari_server_api_base(properties):
   if api_port_prop is not None and api_port_prop != '':
     api_port = api_port_prop
 
-  api_ssl = False
-  api_ssl_prop = properties.get_property(SSL_API)
-  if api_ssl_prop is not None:
-    api_ssl = api_ssl_prop.lower() == "true"
+  api_ssl = is_api_ssl_enabled(properties)
 
   if api_ssl:
     api_host = socket.getfqdn()
@@ -203,7 +201,7 @@ def get_json_via_rest_api(properties, admin_login, 
admin_password, entry_point):
 
   print_info_msg("Fetching information from Ambari's REST API")
 
-  with closing(urllib2.urlopen(request)) as response:
+  with closing(urllib2.urlopen(request, context=get_ssl_context(properties))) 
as response:
     response_status_code = response.getcode()
     json_data = None
     print_info_msg(
@@ -226,9 +224,74 @@ def perform_changes_via_rest_api(properties, admin_login, 
admin_password, url_po
     request.add_data(json.dumps(request_data))
   request.get_method = lambda: get_method
 
-  with closing(urllib2.urlopen(request)) as response:
+  with closing(urllib2.urlopen(request, context=get_ssl_context(properties))) 
as response:
     response_status_code = response.getcode()
     if response_status_code not in (200, 201):
       err = 'Error while performing changes via Ambari REST API. Http status 
code - ' + str(
         response_status_code)
       raise FatalException(1, err)
+
+
+def get_ssl_context(properties, requested_protocol=None):
+  """
+  If needed, creates an SSL context that does not validate the SSL certificate 
provided by the server.
+
+  If api.ssl is not True, then return None, else create a new SSL context with 
either the requested
+  protocol or the best one that is available for the version of Python being 
used.
+
+  :param properties the Ambari server configuration data
+  :param requested_protocol: the requested SSL/TLS protocol; None to choose 
the protocol dynamically
+  :rtype ssl.SSLContext
+  :return: a permissive SSLContext or None
+  """
+
+  if not is_api_ssl_enabled(properties):
+    return None
+
+  if requested_protocol:
+    protocol = requested_protocol
+  else:
+    if hasattr(ssl, 'PROTOCOL_TLS'):
+      # https://docs.python.org/2/library/ssl.html#ssl.PROTOCOL_TLS
+      # Selects the highest protocol version that both the client and server 
support.
+      protocol = ssl.PROTOCOL_TLS
+    elif hasattr(ssl, 'PROTOCOL_TLSv1_2'):
+      # https://docs.python.org/2/library/ssl.html#ssl.PROTOCOL_TLSv1_2
+      # Selects TLS version 1.2 as the channel encryption protocol.
+      protocol = ssl.PROTOCOL_TLSv1_2
+    elif hasattr(ssl, 'PROTOCOL_TLSv1_1'):
+      # https://docs.python.org/2/library/ssl.html#ssl.PROTOCOL_TLSv1_1
+      # Selects TLS version 1.1 as the channel encryption protocol
+      protocol = ssl.PROTOCOL_TLSv1_1
+    elif hasattr(ssl, 'PROTOCOL_TLSv1'):
+      # https://docs.python.org/2/library/ssl.html#ssl.PROTOCOL_TLSv1
+      # Selects TLS version 1.0 as the channel encryption protocol
+      protocol = ssl.PROTOCOL_TLSv1
+    else:
+      protocol = None
+
+  if protocol:
+    context = ssl.SSLContext(protocol)
+  else:
+    context = ssl.create_default_context()
+
+  # if _https_verify_certificates is vaild, force this to be False
+  if hasattr(context, '_https_verify_certificates'):
+    context._https_verify_certificates(False)
+
+  return context
+
+
+def is_api_ssl_enabled(properties):
+  """
+  Determines if the Ambari REST API uses SSL or not.
+
+  :param properties: the Ambari server configuration data
+  :return: True, if the Ambari REST API uses SSL; otherwise False
+  """
+  ssl_enabled = False
+  api_ssl_prop = properties.get_property(SSL_API)
+  if api_ssl_prop is not None:
+    ssl_enabled = api_ssl_prop.lower() == "true"
+
+  return ssl_enabled
\ No newline at end of file
diff --git a/ambari-server/src/main/python/ambari_server/setupSecurity.py 
b/ambari-server/src/main/python/ambari_server/setupSecurity.py
index 956468c..87f09ed 100644
--- a/ambari-server/src/main/python/ambari_server/setupSecurity.py
+++ b/ambari-server/src/main/python/ambari_server/setupSecurity.py
@@ -50,7 +50,8 @@ from ambari_server.serverConfiguration import configDefaults, 
parse_properties_f
   SSL_TRUSTSTORE_PASSWORD_PROPERTY, SSL_TRUSTSTORE_PATH_PROPERTY, 
SSL_TRUSTSTORE_TYPE_PROPERTY, \
   SSL_API, SSL_API_PORT, DEFAULT_SSL_API_PORT, CLIENT_API_PORT, 
JDK_NAME_PROPERTY, JCE_NAME_PROPERTY, JAVA_HOME_PROPERTY, \
   get_resources_location, SECURITY_MASTER_KEY_LOCATION, SETUP_OR_UPGRADE_MSG, 
CHECK_AMBARI_KRB_JAAS_CONFIGURATION_PROPERTY
-from ambari_server.serverUtils import is_server_runing, 
get_ambari_server_api_base, get_ambari_admin_username_password_pair, 
perform_changes_via_rest_api
+from ambari_server.serverUtils import is_server_runing, 
get_ambari_server_api_base, \
+  get_ambari_admin_username_password_pair, perform_changes_via_rest_api, 
get_ssl_context
 from ambari_server.setupActions import SETUP_ACTION, LDAP_SETUP_ACTION
 from ambari_server.userInput import get_validated_string_input, 
get_prompt_default, read_password, get_YN_input, quit_if_has_answer
 from ambari_server.serverClassPath import ServerClassPath
@@ -296,7 +297,7 @@ def getLdapPropertyFromDB(properties, admin_login, 
admin_password, property_name
     sys.stdout.flush()
 
     try:
-      with closing(urllib2.urlopen(request)) as response:
+      with closing(urllib2.urlopen(request, 
context=get_ssl_context(properties))) as response:
         response_status_code = response.getcode()
         if response_status_code != 200:
           request_in_progress = False
@@ -398,7 +399,7 @@ def sync_ldap(options):
   request.get_method = lambda: 'POST'
 
   try:
-    response = urllib2.urlopen(request)
+    response = urllib2.urlopen(request, context=get_ssl_context(properties))
   except Exception as e:
     err = 'Sync event creation failed. Error details: %s' % e
     raise FatalException(1, err)
@@ -423,7 +424,7 @@ def sync_ldap(options):
     sys.stdout.flush()
 
     try:
-      response = urllib2.urlopen(request)
+      response = urllib2.urlopen(request, context=get_ssl_context(properties))
     except Exception as e:
       request_in_progress = False
       err = 'Sync event check failed. Error details: %s' % e
diff --git a/ambari-server/src/test/python/TestServerUpgrade.py 
b/ambari-server/src/test/python/TestServerUpgrade.py
index d944e0d..ad621ff 100644
--- a/ambari-server/src/test/python/TestServerUpgrade.py
+++ b/ambari-server/src/test/python/TestServerUpgrade.py
@@ -86,7 +86,7 @@ class TestServerUpgrade(TestCase):
     get_validated_string_input_mock.return_value = 'dummy_string'
 
     p = get_ambari_properties_mock.return_value
-    p.get_property.side_effect = ["8080", "false"]
+    p.get_property.side_effect = ["8080", "false", "false"]
 
     get_ambari_server_api_base_mock.return_value = 
'http://127.0.0.1:8080/api/v1/'
     get_verbose_mock.retun_value = False
@@ -140,7 +140,7 @@ class TestServerUpgrade(TestCase):
     get_validated_string_input_mock.return_value = 'dummy_string'
 
     p = get_ambari_properties_mock.return_value
-    p.get_property.side_effect = ["8080", "false"]
+    p.get_property.side_effect = ["8080", "false", "false"]
 
     get_ambari_server_api_base_mock.return_value = 
'http://127.0.0.1:8080/api/v1/'
     get_verbose_mock.retun_value = False
diff --git a/ambari-server/src/test/python/TestServerUtils.py 
b/ambari-server/src/test/python/TestServerUtils.py
index 3da98e4..cb548a0 100644
--- a/ambari-server/src/test/python/TestServerUtils.py
+++ b/ambari-server/src/test/python/TestServerUtils.py
@@ -23,6 +23,7 @@ from mock.mock import patch, MagicMock
 from unittest import TestCase
 import platform
 import socket 
+import ssl
 
 from ambari_commons import os_utils
 os_utils.search_file = MagicMock(return_value="/tmp/ambari.properties")
@@ -34,7 +35,8 @@ with patch.object(platform, "linux_distribution", 
return_value = MagicMock(retur
   with patch("os.path.isdir", return_value = MagicMock(return_value=True)):
     with patch("os.access", return_value = MagicMock(return_value=True)):
       with patch.object(os_utils, "parse_log4j_file", 
return_value={'ambari.log.dir': '/var/log/ambari-server'}):
-        from ambari_server.serverUtils import get_ambari_server_api_base, 
get_ambari_admin_username_password_pair
+        from ambari_server.serverUtils import get_ambari_server_api_base, 
get_ambari_admin_username_password_pair, \
+          is_api_ssl_enabled, get_ssl_context
         from ambari_server.serverConfiguration import CLIENT_API_PORT, 
CLIENT_API_PORT_PROPERTY, SSL_API, DEFAULT_SSL_API_PORT, SSL_API_PORT
 
 @patch.object(platform, "linux_distribution", new = 
MagicMock(return_value=('Redhat', '6.4', 'Final')))
@@ -98,6 +100,41 @@ class TestServerUtils(TestCase):
     self.assertEquals(user, user_name)
     self.assertEquals(pw, password)
 
+  def test_is_api_ssl_enabled(self):
+    properties = FakeProperties({
+      SSL_API: "true"
+    })
+    self.assertTrue(is_api_ssl_enabled(properties))
+
+    properties = FakeProperties({
+      SSL_API: "false"
+    })
+    self.assertFalse(is_api_ssl_enabled(properties))
+
+    properties = FakeProperties({
+      SSL_API: None
+    })
+    self.assertFalse(is_api_ssl_enabled(properties))
+
+
+  def test_get_ssl_context(self):
+    properties = FakeProperties({
+      SSL_API: "true"
+    })
+    context = get_ssl_context(properties)
+    self.assertIsNotNone(context)
+
+    context = get_ssl_context(properties, ssl.PROTOCOL_TLSv1)
+    self.assertIsNotNone(context)
+    self.assertEqual(ssl.PROTOCOL_TLSv1, context.protocol)
+
+    properties = FakeProperties({
+      SSL_API: "false"
+    })
+    context = get_ssl_context(properties)
+    self.assertIsNone(context)
+
+
 class FakeProperties(object):
   def __init__(self, prop_map):
     self.prop_map = prop_map

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to