Author: smohanty
Date: Fri Jun  7 18:38:12 2013
New Revision: 1490777

URL: http://svn.apache.org/r1490777
Log:
AMBARI-2270. Provide way to optionally enable two-way SSL for Server-Agent 
communication. (Dmitry Sen via smohanty)

Added:
    
incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/
    
incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
Modified:
    
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/Controller.py
    incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/main.py
    incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/security.py
    incubator/ambari/trunk/ambari-agent/src/test/python/TestMain.py
    incubator/ambari/trunk/ambari-agent/src/test/python/TestSecurity.py
    
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
    
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java

Modified: 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/Controller.py
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/Controller.py?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/Controller.py 
(original)
+++ 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/Controller.py 
Fri Jun  7 18:38:12 2013
@@ -36,6 +36,7 @@ from Register import Register
 from ActionQueue import ActionQueue
 import security
 from NetUtil import NetUtil
+import ssl
 
 
 logger = logging.getLogger()
@@ -86,6 +87,9 @@ class Controller(threading.Thread):
           self.addToQueue(ret['statusCommands'])
           pass
         pass
+      except ssl.SSLError:
+        self.repeatRegistration=False
+        return
       except Exception, err:
         # try a reconnect only after a certain amount of random time
         delay = randint(0, self.range)
@@ -176,6 +180,9 @@ class Controller(threading.Thread):
         certVerifFailed = False
         self.DEBUG_SUCCESSFULL_HEARTBEATS += 1
         self.DEBUG_HEARTBEAT_RETRIES = 0
+      except ssl.SSLError:
+        self.repeatRegistration=False
+        return
       except Exception, err:
         #randomize the heartbeat
         delay = randint(0, self.range)

Modified: 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/main.py
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/main.py?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/main.py 
(original)
+++ incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/main.py 
Fri Jun  7 18:38:12 2013
@@ -194,15 +194,11 @@ def main():
   netutil = NetUtil()
   netutil.try_to_connect(server_url, -1, logger)
 
-  #Initiate security
-  """ Check if security is enable if not then disable it"""
-  logger.info("Creating certs")
-  certMan = security.CertificateManager(config)
-  certMan.initSecurity()
-
   # Launch Controller communication
   controller = Controller(config)
   controller.start()
+  controller.join()
+  stop_agent()
   logger.info("finished")
 
 if __name__ == "__main__":

Modified: 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/security.py
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/security.py?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/security.py 
(original)
+++ 
incubator/ambari/trunk/ambari-agent/src/main/python/ambari_agent/security.py 
Fri Jun  7 18:38:12 2013
@@ -27,7 +27,6 @@ import json
 import pprint
 import traceback
 import hostname
-import AmbariConfig
 
 logger = logging.getLogger()
 
@@ -38,12 +37,48 @@ GEN_AGENT_KEY="openssl req -new -newkey 
 
 class VerifiedHTTPSConnection(httplib.HTTPSConnection):
   """ Connecting using ssl wrapped sockets """
-  def __init__(self, host, port=None, key_file=None, cert_file=None,
-                     strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
+  def __init__(self, host, port=None, config=None):
     httplib.HTTPSConnection.__init__(self, host, port=port)
-    self.certMan = CertificateManager(AmbariConfig.config)
+    self.config=config
+    self.two_way_ssl_required=False
 
   def connect(self):
+
+    if not self.two_way_ssl_required:
+      try:
+        sock=self.create_connection()
+        self.sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_NONE)
+        logger.info('SSL connection established. Two-way SSL authentication is 
'
+                    'turned off on the server.')
+      except (ssl.SSLError, AttributeError):
+        self.two_way_ssl_required=True
+        logger.info('Insecure connection to https://' + self.host + ':' + 
self.port +
+                    '/ failed. Reconnecting using two-way SSL 
authentication..')
+
+    if self.two_way_ssl_required:
+      self.certMan=CertificateManager(self.config)
+      self.certMan.initSecurity()
+      agent_key = self.certMan.getAgentKeyName()
+      agent_crt = self.certMan.getAgentCrtName()
+      server_crt = self.certMan.getSrvrCrtName()
+
+      sock=self.create_connection()
+
+      try:
+        self.sock = ssl.wrap_socket(sock,
+                                keyfile=agent_key,
+                                certfile=agent_crt,
+                                cert_reqs=ssl.CERT_REQUIRED,
+                                ca_certs=server_crt)
+        logger.info('SSL connection established. Two-way SSL authentication '
+                    'completed successfully.')
+      except ssl.SSLError as err:
+        logger.error('Two-way SSL authentication failed. Ensure that '
+                    'server and agent certificates were signed by the same CA '
+                    'and restart the agent.\nExiting..')
+        raise err
+
+  def create_connection(self):
     if self.sock:
       self.sock.close()
     logger.info("SSL Connect being called.. connecting to the server")
@@ -52,16 +87,8 @@ class VerifiedHTTPSConnection(httplib.HT
     if self._tunnel_host:
       self.sock = sock
       self._tunnel()
-    agent_key = self.certMan.getAgentKeyName()
-    agent_crt = self.certMan.getAgentCrtName()
-    server_crt = self.certMan.getSrvrCrtName()
-    
-    self.sock = ssl.wrap_socket(sock,
-                                keyfile=agent_key,
-                                certfile=agent_crt,
-                                cert_reqs=ssl.CERT_REQUIRED,
-                                ca_certs=server_crt)
 
+    return sock
 
 class CachedHTTPSConnection:
   """ Caches a ssl socket and uses a single https connection to the server. """
@@ -75,7 +102,7 @@ class CachedHTTPSConnection:
   
   def connect(self):
     if  not self.connected:
-      self.httpsconn = VerifiedHTTPSConnection(self.server, self.port)
+      self.httpsconn = VerifiedHTTPSConnection(self.server, self.port, 
self.config)
       self.httpsconn.connect()
       self.connected = True
     # possible exceptions are caught and processed in Controller
@@ -83,7 +110,7 @@ class CachedHTTPSConnection:
 
   
   def forceClear(self):
-    self.httpsconn = VerifiedHTTPSConnection(self.server, self.port)
+    self.httpsconn = VerifiedHTTPSConnection(self.server, self.port, 
self.config)
     self.connect()
     
   def request(self, req): 

Modified: incubator/ambari/trunk/ambari-agent/src/test/python/TestMain.py
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-agent/src/test/python/TestMain.py?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-agent/src/test/python/TestMain.py (original)
+++ incubator/ambari/trunk/ambari-agent/src/test/python/TestMain.py Fri Jun  7 
18:38:12 2013
@@ -220,15 +220,12 @@ class TestMain(unittest.TestCase):
   @patch.object(main, "killstaleprocesses")
   @patch.object(main, "update_log_level")
   @patch.object(NetUtil.NetUtil, "try_to_connect")
-  @patch.object(security.CertificateManager, "__init__")
-  @patch.object(security.CertificateManager, "initSecurity")
   @patch.object(Controller, "__init__")
   @patch.object(Controller, "start")
-  def test_main(self, start_mock, Controller_init_mock , initSecurity_mock,
-                CertificateManager_init_mock, try_to_connect_mock, 
update_log_level_mock,
+  @patch.object(Controller, "join")
+  def test_main(self, join_mock, start_mock, Controller_init_mock, 
try_to_connect_mock, update_log_level_mock,
                 killstaleprocesses_mock, daemonize_mock, 
perform_prestart_checks_mock,
                 resolve_ambari_config_mock, stop_mock, 
bind_signal_handlers_mock, setup_logging_mock):
-    CertificateManager_init_mock.return_value = None
     Controller_init_mock.return_value = None
 
     #testing call without command-line arguments
@@ -236,12 +233,11 @@ class TestMain(unittest.TestCase):
 
     self.assertTrue(setup_logging_mock.called)
     self.assertTrue(bind_signal_handlers_mock.called)
-    self.assertFalse(stop_mock.called)
+    self.assertTrue(stop_mock.called)
     self.assertTrue(resolve_ambari_config_mock.called)
     self.assertTrue(perform_prestart_checks_mock.called)
     self.assertTrue(daemonize_mock.called)
     self.assertTrue(killstaleprocesses_mock.called)
     self.assertTrue(update_log_level_mock.called)
     try_to_connect_mock.assert_called_once_with(ANY, -1, ANY)
-    self.assertTrue(initSecurity_mock.called)
     self.assertTrue(start_mock.called)

Modified: incubator/ambari/trunk/ambari-agent/src/test/python/TestSecurity.py
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-agent/src/test/python/TestSecurity.py?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- incubator/ambari/trunk/ambari-agent/src/test/python/TestSecurity.py 
(original)
+++ incubator/ambari/trunk/ambari-agent/src/test/python/TestSecurity.py Fri Jun 
 7 18:38:12 2013
@@ -30,6 +30,7 @@ import logging
 import signal
 from ambari_agent.AmbariConfig import AmbariConfig
 import ConfigParser
+import ssl
 import os
 import tempfile
 from ambari_agent.Controller import Controller
@@ -56,16 +57,52 @@ class TestSecurity(unittest.TestCase):
 
   ### VerifiedHTTPSConnection ###
 
+  @patch.object(security.CertificateManager, "initSecurity")
   @patch("socket.create_connection")
   @patch("ssl.wrap_socket")
-  def test_VerifiedHTTPSConnection_connect(self, wrap_socket_mock, 
create_connection_mock):
+  def test_VerifiedHTTPSConnection_connect(self, wrap_socket_mock,
+                                           create_connection_mock,
+                                            init_security_mock):
+    init_security_mock.return_value = None
     self.config.set('security', 'keysdir', '/dummy-keysdir')
-    connection = security.VerifiedHTTPSConnection("example.com")
+    connection = security.VerifiedHTTPSConnection("example.com",
+      self.config.get('server', 'secured_url_port'), self.config)
     connection._tunnel_host = False
     connection.sock = None
     connection.connect()
     self.assertTrue(wrap_socket_mock.called)
 
+  ### VerifiedHTTPSConnection with no certificates creation
+  @patch.object(security.CertificateManager, "initSecurity")
+  @patch("socket.create_connection")
+  @patch("ssl.wrap_socket")
+  def test_Verified_HTTPSConnection_non_secure_connect(self, wrap_socket_mock,
+                                                    create_connection_mock,
+                                                    init_security_mock):
+    connection = security.VerifiedHTTPSConnection("example.com",
+      self.config.get('server', 'secured_url_port'), self.config)
+    connection._tunnel_host = False
+    connection.sock = None
+    connection.connect()
+    self.assertFalse(init_security_mock.called)
+
+  ### VerifiedHTTPSConnection with two-way SSL authentication enabled
+  @patch.object(security.CertificateManager, "initSecurity")
+  @patch("socket.create_connection")
+  @patch("ssl.wrap_socket")
+  def test_Verified_HTTPSConnection_two_way_ssl_connect(self, wrap_socket_mock,
+                                                    create_connection_mock,
+                                                    init_security_mock):
+    wrap_socket_mock.side_effect=ssl.SSLError()
+    connection = security.VerifiedHTTPSConnection("example.com",
+      self.config.get('server', 'secured_url_port'), self.config)
+    connection._tunnel_host = False
+    connection.sock = None
+    try:
+      connection.connect()
+    except ssl.SSLError:
+      pass
+    self.assertTrue(init_security_mock.called)
 
   ### CachedHTTPSConnection ###
 

Modified: 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
 (original)
+++ 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
 Fri Jun  7 18:38:12 2013
@@ -55,6 +55,7 @@ public class Configuration {
   public static final String BOOTSTRAP_MASTER_HOSTNAME = 
"bootstrap.master_host_name";
   public static final String API_AUTHENTICATE = "api.authenticate";
   public static final String API_USE_SSL = "api.ssl";
+  public static final String SRVR_TWO_WAY_SSL_KEY = 
"security.server.two_way_ssl";
   public static final String SRVR_KSTR_DIR_KEY = "security.server.keys_dir";
   public static final String SRVR_CRT_NAME_KEY = "security.server.cert_name";
   public static final String SRVR_KEY_NAME_KEY = "security.server.key_name";
@@ -155,6 +156,7 @@ public class Configuration {
   public static final String SSL_TRUSTSTORE_PASSWORD_KEY = 
"ssl.trustStore.password";
   public static final String SSL_TRUSTSTORE_TYPE_KEY = "ssl.trustStore.type";
 
+  private static final String SRVR_TWO_WAY_SSL_DEFAULT = "false";
   private static final String SRVR_KSTR_DIR_DEFAULT = ".";
   public static final String SRVR_CRT_NAME_DEFAULT = "ca.crt";
   public static final String SRVR_KEY_NAME_DEFAULT = "ca.key";
@@ -219,8 +221,8 @@ public class Configuration {
     this.properties = properties;
 
     configsMap = new HashMap<String, String>();
-    configsMap.put(SRVR_KSTR_DIR_KEY, properties.getProperty(
-        SRVR_KSTR_DIR_KEY, SRVR_KSTR_DIR_DEFAULT));
+    configsMap.put(SRVR_TWO_WAY_SSL_KEY, properties.getProperty(
+        SRVR_TWO_WAY_SSL_KEY, SRVR_TWO_WAY_SSL_DEFAULT));
     configsMap.put(SRVR_KSTR_DIR_KEY, properties.getProperty(
         SRVR_KSTR_DIR_KEY, SRVR_KSTR_DIR_DEFAULT));
     configsMap.put(SRVR_CRT_NAME_KEY, properties.getProperty(
@@ -412,6 +414,17 @@ public class Configuration {
   }
 
   /**
+   * Check to see if two-way SSL auth should be used between server and agents
+   * or not
+   *
+   * @return
+   */
+  public boolean getTwoWaySsl() {
+    return ("true".equals(properties.getProperty(SRVR_TWO_WAY_SSL_KEY,
+      SRVR_TWO_WAY_SSL_DEFAULT)));
+  }
+
+  /**
    * Check persistence type Ambari Server should use. Possible values:
    * in-memory - use in-memory Derby database to store data
    * local - use local Postgres instance

Modified: 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java?rev=1490777&r1=1490776&r2=1490777&view=diff
==============================================================================
--- 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
 (original)
+++ 
incubator/ambari/trunk/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
 Fri Jun  7 18:38:12 2013
@@ -41,6 +41,7 @@ import org.apache.ambari.server.orm.dao.
 import org.apache.ambari.server.resources.ResourceManager;
 import org.apache.ambari.server.resources.api.rest.GetResource;
 import org.apache.ambari.server.security.CertificateManager;
+import org.apache.ambari.server.security.SecurityFilter;
 import 
org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
 import 
org.apache.ambari.server.security.authorization.AmbariLocalUserDetailsService;
 import org.apache.ambari.server.security.authorization.Users;
@@ -177,13 +178,15 @@ public class AmbariServer {
       root.addFilter(new 
FilterHolder(injector.getInstance(AmbariPersistFilter.class)), "/api/*", 1);
       agentroot.addFilter(new 
FilterHolder(injector.getInstance(AmbariPersistFilter.class)), "/agent/*", 1);
 
+      agentroot.addFilter(SecurityFilter.class, "/*", 1);
+
       if (configs.getApiAuthentication()) {
         root.addFilter(new FilterHolder(springSecurityFilter), "/api/*", 1);
       }
 
 
       //Secured connector for 2-way auth
-      SslSelectChannelConnector sslConnectorTwoWay = new  
+      SslSelectChannelConnector sslConnectorTwoWay = new
           SslSelectChannelConnector();
       sslConnectorTwoWay.setPort(AGENT_TWO_WAY_AUTH);
 
@@ -198,7 +201,7 @@ public class AmbariServer {
       sslConnectorTwoWay.setTrustPassword(srvrCrtPass);
       sslConnectorTwoWay.setKeystoreType("PKCS12");
       sslConnectorTwoWay.setTruststoreType("PKCS12");
-      sslConnectorTwoWay.setNeedClientAuth(true);
+      sslConnectorTwoWay.setNeedClientAuth(configs.getTwoWaySsl());
 
       //Secured connector for 1-way auth
       //SslSelectChannelConnector sslConnectorOneWay = new 
SslSelectChannelConnector();

Added: 
incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
URL: 
http://svn.apache.org/viewvc/incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java?rev=1490777&view=auto
==============================================================================
--- 
incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
 (added)
+++ 
incubator/ambari/trunk/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
 Fri Jun  7 18:38:12 2013
@@ -0,0 +1,84 @@
+/**
+ * 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.ambari.server.configuration;
+
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import junit.framework.Assert;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Properties;
+
+public class ConfigurationTest {
+
+  private Injector injector;
+
+  @Inject
+  private Configuration config;
+
+  @Before
+  public void setup() throws Exception {
+    injector = Guice.createInjector(new InMemoryDefaultTestModule());
+    injector.injectMembers(this);
+  }
+
+  @After
+  public void teardown() throws AmbariException {
+  }
+
+  /**
+   * ambari.properties doesn't contain "security.server.two_way_ssl" option
+   * @throws Exception
+   */
+  @Test
+  public void testDefaultTwoWayAuthNotSet() throws Exception {
+    Assert.assertFalse(config.getTwoWaySsl());
+  }
+
+  /**
+   * ambari.properties contains "security.server.two_way_ssl=true" option
+   * @throws Exception
+   */
+  @Test
+  public void testTwoWayAuthTurnedOn() throws Exception {
+    Properties ambariProperties = new Properties();
+    ambariProperties.setProperty("security.server.two_way_ssl", "true");
+    Configuration conf = new Configuration(ambariProperties);
+    Assert.assertTrue(conf.getTwoWaySsl());
+  }
+
+  /**
+   * ambari.properties contains "security.server.two_way_ssl=false" option
+   * @throws Exception
+   */
+  @Test
+  public void testTwoWayAuthTurnedOff() throws Exception {
+    Properties ambariProperties = new Properties();
+    ambariProperties.setProperty("security.server.two_way_ssl", "false");
+    Configuration conf = new Configuration(ambariProperties);
+    Assert.assertFalse(conf.getTwoWaySsl());
+  }
+
+
+}


Reply via email to