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());
+ }
+
+
+}