You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@slider.apache.org by jm...@apache.org on 2014/07/09 21:38:19 UTC
[2/2] git commit: SLIDER-131 enabling agent to AM one way and two way
SSL
SLIDER-131 enabling agent to AM one way and two way SSL
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/859ca521
Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/859ca521
Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/859ca521
Branch: refs/heads/develop
Commit: 859ca521214c63c4d0956f98d001920705760bd0
Parents: 55aa2b1
Author: Jon Maron <jm...@hortonworks.com>
Authored: Wed Jul 9 15:36:59 2014 -0400
Committer: Jon Maron <jm...@hortonworks.com>
Committed: Wed Jul 9 15:36:59 2014 -0400
----------------------------------------------------------------------
pom.xml | 17 +-
.../src/main/python/agent/AgentConfig.py | 3 +
.../src/main/python/agent/Controller.py | 32 ++-
slider-agent/src/main/python/agent/NetUtil.py | 8 +-
slider-agent/src/main/python/agent/main.py | 15 +-
slider-agent/src/main/python/agent/security.py | 7 +-
.../src/test/python/agent/TestController.py | 4 +-
slider-agent/src/test/python/agent/TestMain.py | 8 +-
.../src/test/python/agent/TestNetUtil.py | 2 +-
slider-core/pom.xml | 7 +
.../java/org/apache/slider/api/StatusKeys.java | 3 +
.../org/apache/slider/common/SliderKeys.java | 14 +
.../apache/slider/common/tools/SliderUtils.java | 2 +-
.../core/launch/ClasspathConstructor.java | 1 -
.../registry/info/CustomRegistryConstants.java | 2 +-
.../providers/AbstractProviderService.java | 7 +-
.../slider/providers/ProviderService.java | 8 +-
.../slider/providers/agent/AgentKeys.java | 1 +
.../providers/agent/AgentProviderService.java | 16 +-
.../slideram/SliderAMClientProvider.java | 7 +-
.../slideram/SliderAMProviderService.java | 18 +-
.../server/appmaster/SliderAppMaster.java | 68 ++++-
.../server/appmaster/web/AgentService.java | 53 ++++
.../server/appmaster/web/SliderAmIpFilter.java | 18 +-
.../slider/server/appmaster/web/WebAppApi.java | 9 +-
.../server/appmaster/web/WebAppApiImpl.java | 16 +-
.../appmaster/web/rest/AMWebServices.java | 7 +-
.../server/appmaster/web/rest/RestPaths.java | 5 +-
.../appmaster/web/rest/agent/AgentResource.java | 25 +-
.../appmaster/web/rest/agent/AgentWebApp.java | 203 +++++++++++++++
.../web/rest/agent/AgentWebServices.java | 40 +++
.../services/security/CertificateManager.java | 257 +++++++++++++++++++
.../server/services/security/SecurityUtils.java | 209 +++++++++++++++
.../services/security/SignCertResponse.java | 67 +++++
.../server/services/security/SignMessage.java | 54 ++++
.../providers/slideram/instance/appconf.json | 5 +-
.../main/resources/webapps/slideragent/.keep | 0
.../agent/TestAgentAMManagementWS.groovy | 46 +++-
.../model/mock/MockProviderService.groovy | 12 +-
.../view/TestClusterSpecificationBlock.groovy | 2 +-
.../web/view/TestContainerStatsBlock.groovy | 2 +-
.../appmaster/web/view/TestIndexBlock.groovy | 2 +-
.../agent/TestAgentProviderService.java | 6 +-
.../web/rest/agent/TestAMAgentWebServices.java | 217 ++++++++--------
.../management/TestAMManagementWebServices.java | 2 +-
.../security/TestCertificateManager.java | 60 +++++
slider-core/src/test/python/agent/main.py | 2 +
.../providers/hbase/HBaseProviderService.java | 5 +-
48 files changed, 1381 insertions(+), 193 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1ee78fe..a4489bb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -303,6 +303,7 @@
<exclude>**/dfs.exclude</exclude>
<exclude>**/*.iml</exclude>
<exclude>**/rat.txt</exclude>
+ <exclude>**/get-hbase-site.sh</exclude>
<exclude>DISCLAIMER</exclude>
<exclude>app-packages/hbase/target/**</exclude>
</excludes>
@@ -1140,9 +1141,9 @@
</dependency>
- <!-- ======================================================== -->
- <!-- Mocking -->
- <!-- ======================================================== -->
+ <!-- ======================================================== -->
+ <!-- Mocking -->
+ <!-- ======================================================== -->
<dependency>
<groupId>org.mockito</groupId>
@@ -1156,6 +1157,16 @@
<version>${easymock.version}</version>
</dependency>
+ <!-- ======================================================== -->
+ <!-- Jetty -->
+ <!-- ======================================================== -->
+
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-sslengine</artifactId>
+ <version>6.1.26</version>
+ </dependency>
+
</dependencies>
</dependencyManagement>
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/main/python/agent/AgentConfig.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/main/python/agent/AgentConfig.py b/slider-agent/src/main/python/agent/AgentConfig.py
index 91adfdd..16b924c 100644
--- a/slider-agent/src/main/python/agent/AgentConfig.py
+++ b/slider-agent/src/main/python/agent/AgentConfig.py
@@ -60,6 +60,9 @@ max_retries=2
sleep_between_retries=1
[security]
+keysdir=security/keys
+server_crt=ca.crt
+passphrase_env_var_name=SLIDER_PASSPHRASE
[heartbeat]
state_interval=6
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/main/python/agent/Controller.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/main/python/agent/Controller.py b/slider-agent/src/main/python/agent/Controller.py
index b5dca92..92e9086 100644
--- a/slider-agent/src/main/python/agent/Controller.py
+++ b/slider-agent/src/main/python/agent/Controller.py
@@ -37,6 +37,7 @@ from NetUtil import NetUtil
import ssl
import ProcessHelper
import Constants
+import security
logger = logging.getLogger()
@@ -57,10 +58,10 @@ class Controller(threading.Thread):
self.credential = None
self.config = config
self.hostname = config.getLabel()
- server_url = 'http://' + config.get(AgentConfig.SERVER_SECTION,
+ server_url = 'https://' + config.get(AgentConfig.SERVER_SECTION,
'hostname') + \
':' + config.get(AgentConfig.SERVER_SECTION,
- 'port')
+ 'secured_port')
self.registerUrl = server_url + '/ws/v1/slider/agents/' + self.hostname + '/register'
self.heartbeatUrl = server_url + '/ws/v1/slider/agents/' + self.hostname + '/heartbeat'
self.netutil = NetUtil()
@@ -111,7 +112,8 @@ class Controller(threading.Thread):
while not self.isRegistered:
try:
data = json.dumps(self.register.build(id))
- logger.info("Registering with the server " + pprint.pformat(data))
+ logger.info("Registering with the server at " + self.registerUrl +
+ " with data " + pprint.pformat(data))
response = self.sendRequest(self.registerUrl, data)
ret = json.loads(response)
exitstatus = 0
@@ -395,11 +397,25 @@ class Controller(threading.Thread):
pass
def sendRequest(self, url, data):
- req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
- f = urllib2.urlopen(req)
- response = f.read()
- f.close()
- return response
+ response = None
+ try:
+ if self.cachedconnect is None: # Lazy initialization
+ self.cachedconnect = security.CachedHTTPSConnection(self.config)
+ req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
+ response = self.cachedconnect.request(req)
+ return response
+ except Exception:
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ logger.error("Exception raised", exc_info=(exc_type, exc_value, exc_traceback))
+ if response is None:
+ err_msg = 'Request failed! Data: ' + str(data)
+ logger.warn(err_msg)
+ return {'exitstatus': 1, 'log': err_msg}
+ else:
+ err_msg = ('Response parsing failed! Request data: ' + str(data)
+ + '; Response: ' + str(response))
+ logger.warn(err_msg)
+ return {'exitstatus': 1, 'log': err_msg}
def main(argv=None):
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/main/python/agent/NetUtil.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/main/python/agent/NetUtil.py b/slider-agent/src/main/python/agent/NetUtil.py
index ed8e687..eb658f7 100644
--- a/slider-agent/src/main/python/agent/NetUtil.py
+++ b/slider-agent/src/main/python/agent/NetUtil.py
@@ -18,6 +18,7 @@ from urlparse import urlparse
import time
import logging
import httplib
+from ssl import SSLError
logger = logging.getLogger()
@@ -37,7 +38,7 @@ class NetUtil:
logger.info("Connecting to the following url " + url);
try:
parsedurl = urlparse(url)
- ca_connection = httplib.HTTPConnection(parsedurl[1])
+ ca_connection = httplib.HTTPSConnection(parsedurl[1])
ca_connection.request("GET", parsedurl[2])
response = ca_connection.getresponse()
status = response.status
@@ -47,6 +48,11 @@ class NetUtil:
return True
else:
return False
+ except SSLError as slerror:
+ logger.error(str(slerror))
+ logger.error("SSLError: Failed to connect. Please check openssl library versions. \n" +
+ "Refer to: https://bugzilla.redhat.com/show_bug.cgi?id=1022468 for more details.")
+ return False
except Exception, e:
logger.info("Failed to connect to " + str(url) + " due to " + str(e))
return False
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/main/python/agent/main.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/main/python/agent/main.py b/slider-agent/src/main/python/agent/main.py
index 3632157..12e07ba 100644
--- a/slider-agent/src/main/python/agent/main.py
+++ b/slider-agent/src/main/python/agent/main.py
@@ -27,7 +27,6 @@ import traceback
import os
import time
import errno
-import ConfigParser
import ProcessHelper
from Controller import Controller
from AgentConfig import AgentConfig
@@ -40,7 +39,7 @@ agentPid = os.getpid()
configFileRelPath = "infra/conf/agent.ini"
logFileName = "agent.log"
-SERVER_STATUS_URL="http://{0}:{1}{2}"
+SERVER_STATUS_URL="https://{0}:{1}{2}"
def signal_handler(signum, frame):
@@ -176,6 +175,7 @@ def main():
parser.add_option("-l", "--label", dest="label", help="label of the agent", default=None)
parser.add_option("--host", dest="host", help="AppMaster host", default=None)
parser.add_option("--port", dest="port", help="AppMaster port", default=None)
+ parser.add_option("--secured_port", dest="secured_port", help="AppMaster 2 Way port", default=None)
parser.add_option("--debug", dest="debug", help="Agent debug hint", default="")
(options, args) = parser.parse_args()
@@ -201,12 +201,23 @@ def main():
if options.port:
agentConfig.set(AgentConfig.SERVER_SECTION, "port", options.port)
+ if options.secured_port:
+ agentConfig.set(AgentConfig.SERVER_SECTION, "secured_port", options.secured_port)
+
if options.debug:
agentConfig.set(AgentConfig.AGENT_SECTION, AgentConfig.APP_DBG_CMD, options.debug)
+ # set the security directory to a subdirectory of the run dir
+ secDir = os.path.join(agentConfig.getResolvedPath(AgentConfig.RUN_DIR), "security")
+ logger.info("Security/Keys directory: " + secDir)
+ agentConfig.set(AgentConfig.SECURITY_SECTION, "keysdir", secDir)
+
logFile = os.path.join(agentConfig.getResolvedPath(AgentConfig.LOG_DIR), logFileName)
+
perform_prestart_checks(agentConfig)
ensure_folder_layout(agentConfig)
+ # create security dir if necessary
+ ensure_path_exists(secDir)
setup_logging(options.verbose, logFile)
update_log_level(agentConfig, logFile)
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/main/python/agent/security.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/main/python/agent/security.py b/slider-agent/src/main/python/agent/security.py
index 4037733..76671dc 100644
--- a/slider-agent/src/main/python/agent/security.py
+++ b/slider-agent/src/main/python/agent/security.py
@@ -140,7 +140,7 @@ class CertificateManager():
self.keysdir = self.config.get('security', 'keysdir')
self.server_crt=self.config.get('security', 'server_crt')
self.server_url = 'https://' + self.config.get('server', 'hostname') + ':' \
- + self.config.get('server', 'url_port')
+ + self.config.get('server', 'port')
def getAgentKeyName(self):
keysdir = self.config.get('security', 'keysdir')
@@ -187,7 +187,7 @@ class CertificateManager():
logger.info("Agent certificate exists, ok")
def loadSrvrCrt(self):
- get_ca_url = self.server_url + '/cert/ca/'
+ get_ca_url = self.server_url + '/ws/v1/slider/agents/cert/ca/'
logger.info("Downloading server cert from " + get_ca_url)
stream = urllib2.urlopen(get_ca_url)
response = stream.read()
@@ -196,7 +196,8 @@ class CertificateManager():
srvr_crt_f.write(response)
def reqSignCrt(self):
- sign_crt_req_url = self.server_url + '/certs/' + hostname.hostname()
+ sign_crt_req_url = self.server_url + '/ws/v1/slider/agents/certs/' + \
+ hostname.hostname()
agent_crt_req_f = open(self.getAgentCrtReqName())
agent_crt_req_content = agent_crt_req_f.read()
passphrase_env_var = self.config.get('security', 'passphrase_env_var_name')
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/test/python/agent/TestController.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/test/python/agent/TestController.py b/slider-agent/src/test/python/agent/TestController.py
index 8c671da..939e63f 100644
--- a/slider-agent/src/test/python/agent/TestController.py
+++ b/slider-agent/src/test/python/agent/TestController.py
@@ -262,7 +262,9 @@ class TestController(unittest.TestCase):
data = "data"
requestMock.return_value = conMock
- self.assertEqual("response", self.controller.sendRequest(url, data))
+ expected = {'exitstatus': 1, 'log': 'Request failed! Data: ' + data}
+
+ self.assertEqual(expected, self.controller.sendRequest(url, data))
requestMock.called_once_with(url, data,
{'Content-Type': 'application/json'})
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/test/python/agent/TestMain.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/test/python/agent/TestMain.py b/slider-agent/src/test/python/agent/TestMain.py
index 179d1b4..9ef1cad 100644
--- a/slider-agent/src/test/python/agent/TestMain.py
+++ b/slider-agent/src/test/python/agent/TestMain.py
@@ -259,10 +259,11 @@ class TestMain(unittest.TestCase):
self.assertTrue(start_mock.called)
class AgentOptions:
- def __init__(self, label, host, port, verbose, debug):
+ def __init__(self, label, host, port, secured_port, verbose, debug):
self.label = label
self.host = host
self.port = port
+ self.secured_port = secured_port
self.verbose = verbose
self.debug = debug
@@ -290,16 +291,17 @@ class TestMain(unittest.TestCase):
Controller_init_mock.return_value = None
isAlive_mock.return_value = False
parse_args_mock.return_value = (
- TestMain.AgentOptions("agent", "host1", "8080", True, ""), [])
+ TestMain.AgentOptions("agent", "host1", "8080", "8081", True, ""), [])
tmpdir = tempfile.gettempdir()
#testing call without command-line arguments
os.environ["AGENT_WORK_ROOT"] = os.path.join(tmpdir, "work")
os.environ["AGENT_LOG_ROOT"] = os.path.join(tmpdir, "log")
main.main()
- self.assertTrue(AgentConfig_set_mock.call_count == 2)
+ self.assertTrue(AgentConfig_set_mock.call_count == 4)
AgentConfig_set_mock.assert_any_call("server", "hostname", "host1")
AgentConfig_set_mock.assert_any_call("server", "port", "8080")
+ AgentConfig_set_mock.assert_any_call("server", "secured_port", "8081")
if __name__ == "__main__":
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-agent/src/test/python/agent/TestNetUtil.py
----------------------------------------------------------------------
diff --git a/slider-agent/src/test/python/agent/TestNetUtil.py b/slider-agent/src/test/python/agent/TestNetUtil.py
index c19ec19..550e148 100644
--- a/slider-agent/src/test/python/agent/TestNetUtil.py
+++ b/slider-agent/src/test/python/agent/TestNetUtil.py
@@ -25,7 +25,7 @@ import unittest
class TestNetUtil(unittest.TestCase):
@patch("urlparse.urlparse")
- @patch("httplib.HTTPConnection")
+ @patch("httplib.HTTPSConnection")
def test_checkURL(self, httpsConMock, parseMock):
NetUtil.logger = MagicMock()
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/pom.xml
----------------------------------------------------------------------
diff --git a/slider-core/pom.xml b/slider-core/pom.xml
index ca09541..b6120fb 100644
--- a/slider-core/pom.xml
+++ b/slider-core/pom.xml
@@ -215,6 +215,7 @@
<exclude>src/test/python/agent.ini</exclude>
<exclude>src/test/python/version</exclude>
<exclude>src/main/resources/webapps/slideram/.keep</exclude>
+ <exclude>src/main/resources/webapps/slideragent/.keep</exclude>
<exclude>src/main/resources/webapps/static/yarn.dt.plugins.js</exclude>
<!-- jQuery DataTables files (BSD license) -->
<exclude>src/main/resources/webapps/static/dt-1.9.4/**</exclude>
@@ -507,6 +508,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-sslengine</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
</dependencies>
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/api/StatusKeys.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/api/StatusKeys.java b/slider-core/src/main/java/org/apache/slider/api/StatusKeys.java
index 709c137..4bfcf41 100644
--- a/slider-core/src/main/java/org/apache/slider/api/StatusKeys.java
+++ b/slider-core/src/main/java/org/apache/slider/api/StatusKeys.java
@@ -67,4 +67,7 @@ public interface StatusKeys {
String INFO_AM_RPC_PORT = "info.am.rpc.port";
String INFO_AM_WEB_PORT = "info.am.web.port";
String INFO_AM_WEB_URL = "info.am.web.url";
+ String INFO_AM_AGENT_PORT = "info.am.agent.port";
+ String INFO_AM_AGENT_URL = "info.am.agent.url";
+ String INFO_AM_SECURED_AGENT_PORT = "info.am.agent.secured.port";
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
index a0e29c9..0ba562a 100644
--- a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
+++ b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
@@ -151,6 +151,20 @@ public interface SliderKeys extends SliderXmlConfKeys {
String HADOOP_USER_NAME = "HADOOP_USER_NAME";
String HADOOP_PROXY_USER = "HADOOP_PROXY_USER";
+ String SLIDER_PASSPHRASE = "SLIDER_PASSPHRASE";
boolean PROPAGATE_RESOURCE_OPTION = true;
+
+ /**
+ * Security associated keys.
+ */
+ String SECURITY_DIR = "security";
+ String CRT_FILE_NAME = "ca.crt";
+ String CSR_FILE_NAME = "ca.csr";
+ String KEY_FILE_NAME = "ca.key";
+ String KEYSTORE_FILE_NAME = "keystore.p12";
+ String CRT_PASS_FILE_NAME = "pass.txt";
+ String PASSPHRASE = "DEV";
+ String PASS_LEN = "50";
+ String KEYSTORE_LOCATION = "ssl.server.keystore.location";
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java b/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
index 7c89321..17f8b70 100644
--- a/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
+++ b/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
@@ -473,7 +473,7 @@ public final class SliderUtils {
}
return trailing?
b.toString()
- : (b.substring(0, b.length() - 1));
+ : (b.substring(0, b.length() - separator.length()));
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/core/launch/ClasspathConstructor.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/core/launch/ClasspathConstructor.java b/slider-core/src/main/java/org/apache/slider/core/launch/ClasspathConstructor.java
index 5b74c03..3527149 100644
--- a/slider-core/src/main/java/org/apache/slider/core/launch/ClasspathConstructor.java
+++ b/slider-core/src/main/java/org/apache/slider/core/launch/ClasspathConstructor.java
@@ -24,7 +24,6 @@ import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.slider.common.tools.SliderUtils;
-import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java b/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java
index 1eb87c6..38fb4a5 100644
--- a/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/info/CustomRegistryConstants.java
@@ -32,7 +32,7 @@ public class CustomRegistryConstants {
"org.apache.slider.publisher";
public static final String AGENT_REST_API =
- "org.apache.slider.publisher";
+ "org.apache.slider.agents";
public static final String AM_IPC_PROTOCOL =
"org.apache.slider.appmaster";
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/AbstractProviderService.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/AbstractProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/AbstractProviderService.java
index a1940dd..e35227c 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/AbstractProviderService.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/AbstractProviderService.java
@@ -312,11 +312,12 @@ public abstract class AbstractProviderService
}
}
@Override
- public void applyInitialRegistryDefinitions(URL amWebAPI,
- ServiceInstanceData registryInstanceData) throws MalformedURLException,
+ public void applyInitialRegistryDefinitions(URL unsecureWebAPI,
+ URL secureWebAPI,
+ ServiceInstanceData registryInstanceData) throws MalformedURLException,
IOException {
- this.amWebAPI = amWebAPI;
+ this.amWebAPI = unsecureWebAPI;
this.registryInstanceData = registryInstanceData;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/ProviderService.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/ProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/ProviderService.java
index 1778197..56e24e9 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/ProviderService.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/ProviderService.java
@@ -159,10 +159,12 @@ public interface ProviderService extends ProviderCore, Service,
/**
* Prior to going live -register the initial service registry data
- * @param amWebAPI
+ * @param unsecureWebAPI
+ * @param secureWebAPI
* @param registryInstanceData
*/
- void applyInitialRegistryDefinitions(URL amWebAPI,
- ServiceInstanceData registryInstanceData) throws MalformedURLException,
+ void applyInitialRegistryDefinitions(URL unsecureWebAPI,
+ URL secureWebAPI,
+ ServiceInstanceData registryInstanceData) throws MalformedURLException,
IOException;
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java
index 96acd41..31d09c4 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java
@@ -67,6 +67,7 @@ public interface AgentKeys {
String ARG_LABEL = "--label";
String ARG_HOST = "--host";
String ARG_PORT = "--port";
+ String ARG_SECURED_PORT = "--secured_port";
String ARG_DEBUG = "--debug";
String AGENT_MAIN_SCRIPT_ROOT = "./infra/agent/slider-agent/";
String AGENT_MAIN_SCRIPT = "agent/main.py";
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
index 3430baf..c1719b7 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
@@ -209,6 +209,8 @@ public class AgentProviderService extends AbstractProviderService implements
launcher.setEnv("AGENT_LOG_ROOT", logDir);
log.info("AGENT_LOG_ROOT set to {}", logDir);
launcher.setEnv(HADOOP_USER_NAME, System.getenv(HADOOP_USER_NAME));
+ // for 2-Way SSL
+ launcher.setEnv(SLIDER_PASSPHRASE, SliderKeys.PASSPHRASE);
//local resources
@@ -261,7 +263,9 @@ public class AgentProviderService extends AbstractProviderService implements
operation.add(ARG_HOST);
operation.add(getClusterInfoPropertyValue(StatusKeys.INFO_AM_HOSTNAME));
operation.add(ARG_PORT);
- operation.add(getClusterInfoPropertyValue(StatusKeys.INFO_AM_WEB_PORT));
+ operation.add(getClusterInfoPropertyValue(StatusKeys.INFO_AM_AGENT_PORT));
+ operation.add(ARG_SECURED_PORT);
+ operation.add(getClusterInfoPropertyValue(StatusKeys.INFO_AM_SECURED_AGENT_PORT));
String debugCmd = agentLaunchParameter.getNextLaunchParameter(role);
if (debugCmd != null && debugCmd.length() != 0) {
@@ -986,15 +990,19 @@ public class AgentProviderService extends AbstractProviderService implements
}
@Override
- public void applyInitialRegistryDefinitions(URL amWebAPI,
+ public void applyInitialRegistryDefinitions(URL unsecureWebAPI,
+ URL secureWebAPI,
ServiceInstanceData instanceData) throws IOException {
- super.applyInitialRegistryDefinitions(amWebAPI, instanceData);
+ super.applyInitialRegistryDefinitions(unsecureWebAPI,
+ secureWebAPI,
+ instanceData
+ );
try {
instanceData.internalView.endpoints.put(
CustomRegistryConstants.AGENT_REST_API,
new RegisteredEndpoint(
- new URL(amWebAPI, SLIDER_PATH_AGENTS),
+ new URL(secureWebAPI, SLIDER_PATH_AGENTS),
"Agent REST API"));
} catch (URISyntaxException e) {
throw new IOException(e);
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java
index 6b40856..6aeb801 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java
@@ -44,6 +44,7 @@ import org.apache.slider.providers.AbstractClientProvider;
import org.apache.slider.providers.PlacementPolicy;
import org.apache.slider.providers.ProviderRole;
import org.apache.slider.providers.ProviderUtils;
+import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -160,7 +161,8 @@ public class SliderAMClientProvider extends AbstractClientProvider implements
Class<?>[] classes = {
JCommander.class,
GsonBuilder.class,
-
+ SslSelectChannelConnector.class,
+
CuratorFramework.class,
CuratorZookeeperClient.class,
ServiceInstance.class,
@@ -171,7 +173,8 @@ public class SliderAMClientProvider extends AbstractClientProvider implements
{
JCOMMANDER_JAR,
GSON_JAR,
-
+ "jetty-sslengine.jar",
+
"curator-framework.jar",
"curator-client.jar",
"curator-x-discovery.jar",
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java
index 1610954..184c25a 100644
--- a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java
+++ b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMProviderService.java
@@ -110,9 +110,13 @@ public class SliderAMProviderService extends AbstractProviderService implements
}
@Override
- public void applyInitialRegistryDefinitions(URL amWebAPI,
- ServiceInstanceData instanceData) throws IOException {
- super.applyInitialRegistryDefinitions(amWebAPI, instanceData);
+ public void applyInitialRegistryDefinitions(URL unsecureWebAPI,
+ URL secureWebAPI,
+ ServiceInstanceData instanceData) throws IOException {
+ super.applyInitialRegistryDefinitions(unsecureWebAPI,
+ secureWebAPI,
+ instanceData
+ );
// now publish site.xml files
YarnConfiguration defaultYarnConfig = new YarnConfiguration();
@@ -146,24 +150,24 @@ public class SliderAMProviderService extends AbstractProviderService implements
try {
RegistryView externalView = instanceData.externalView;
RegisteredEndpoint webUI =
- new RegisteredEndpoint(amWebAPI, "Application Master Web UI");
+ new RegisteredEndpoint(unsecureWebAPI, "Application Master Web UI");
externalView.endpoints.put(CommonRegistryConstants.WEB_UI, webUI);
externalView.endpoints.put(
CustomRegistryConstants.MANAGEMENT_REST_API,
new RegisteredEndpoint(
- new URL(amWebAPI, SLIDER_PATH_MANAGEMENT),
+ new URL(unsecureWebAPI, SLIDER_PATH_MANAGEMENT),
"Management REST API") );
externalView.endpoints.put(
CustomRegistryConstants.REGISTRY_REST_API,
new RegisteredEndpoint(
- new URL(amWebAPI, RestPaths.SLIDER_PATH_REGISTRY + "/" +
+ new URL(unsecureWebAPI, RestPaths.SLIDER_PATH_REGISTRY + "/" +
RestPaths.REGISTRY_SERVICE),
"Registry Web Service" ) );
- URL publisherURL = new URL(amWebAPI, SLIDER_PATH_PUBLISHER);
+ URL publisherURL = new URL(unsecureWebAPI, SLIDER_PATH_PUBLISHER);
externalView.endpoints.put(
CustomRegistryConstants.PUBLISHER_REST_API,
new RegisteredEndpoint(
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
index 739d22c..0b22910 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
@@ -105,6 +105,8 @@ import org.apache.slider.server.appmaster.state.ProviderAppState;
import org.apache.slider.server.appmaster.state.RMOperationHandler;
import org.apache.slider.server.appmaster.state.RoleInstance;
import org.apache.slider.server.appmaster.state.RoleStatus;
+import org.apache.slider.server.appmaster.web.AgentService;
+import org.apache.slider.server.appmaster.web.rest.agent.AgentWebApp;
import org.apache.slider.server.appmaster.web.SliderAMWebApp;
import org.apache.slider.server.appmaster.web.SliderAmFilterInitializer;
import org.apache.slider.server.appmaster.web.SliderAmIpFilter;
@@ -112,9 +114,10 @@ import org.apache.slider.server.appmaster.web.WebAppApi;
import org.apache.slider.server.appmaster.web.WebAppApiImpl;
import org.apache.slider.server.appmaster.web.rest.RestPaths;
import org.apache.slider.server.services.registry.SliderRegistryService;
+import org.apache.slider.server.services.security.CertificateManager;
import org.apache.slider.server.services.utility.AbstractSliderLaunchedService;
-import org.apache.slider.server.services.workflow.WorkflowRpcService;
import org.apache.slider.server.services.utility.WebAppService;
+import org.apache.slider.server.services.workflow.WorkflowRpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -123,6 +126,7 @@ import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
+import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -135,6 +139,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
+import static org.apache.slider.server.appmaster.web.rest.RestPaths.WS_AGENT_CONTEXT_ROOT;
import static org.apache.slider.server.appmaster.web.rest.RestPaths.WS_CONTEXT_ROOT;
/**
@@ -307,6 +312,8 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private InetSocketAddress rpcServiceAddress;
private ProviderService sliderAMProvider;
+ private String agentAccessUrl;
+ private CertificateManager certificateManager;
/**
* Service Constructor
@@ -473,7 +480,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
// Try to get the proper filtering of static resources through the yarn proxy working
serviceConf.set(HADOOP_HTTP_FILTER_INITIALIZERS,
SliderAmFilterInitializer.NAME);
- serviceConf.set(SliderAmIpFilter.WS_CONTEXT_ROOT, WS_CONTEXT_ROOT);
+ serviceConf.set(SliderAmIpFilter.WS_CONTEXT_ROOT, WS_CONTEXT_ROOT + "|" + WS_AGENT_CONTEXT_ROOT);
//get our provider
MapOperations globalInternalOptions =
@@ -590,10 +597,20 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
providerRoles.addAll(SliderAMClientProvider.ROLES);
// Start up the WebApp and track the URL for it
+ certificateManager = new CertificateManager();
+ certificateManager.initRootCert(
+ instanceDefinition.getAppConfOperations()
+ .getComponent(SliderKeys.COMPONENT_AM));
+
+ startAgentWebApp(appInformation, serviceConf);
+
webApp = new SliderAMWebApp(registry);
WebApps.$for(SliderAMWebApp.BASE_PATH, WebAppApi.class,
- new WebAppApiImpl(this, stateForProviders, providerService),
- RestPaths.WS_CONTEXT)
+ new WebAppApiImpl(this,
+ stateForProviders,
+ providerService,
+ certificateManager),
+ RestPaths.WS_CONTEXT)
.with(serviceConf)
.start(webApp);
appMasterTrackingUrl = "http://" + appMasterHostname + ":" + webApp.port();
@@ -605,7 +622,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
addService(webAppService);
appInformation.put(StatusKeys.INFO_AM_WEB_URL, appMasterTrackingUrl + "/");
- appInformation.set(StatusKeys.INFO_AM_WEB_PORT, webApp.port());
+ appInformation.set(StatusKeys.INFO_AM_WEB_PORT, webApp.port());
// Register self with ResourceManager
// This will start heartbeating to the RM
@@ -723,6 +740,32 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
return amExitCode;
}
+ private void startAgentWebApp(MapOperations appInformation,
+ Configuration serviceConf) {
+ LOG_YARN.info("AM classpath:" + ((URLClassLoader) AgentWebApp.class.getClassLoader() ).getURLs());
+ // Start up the agent web app and track the URL for it
+ AgentWebApp agentWebApp = AgentWebApp.$for(AgentWebApp.BASE_PATH,
+ new WebAppApiImpl(this,
+ stateForProviders,
+ providerService,
+ certificateManager),
+ RestPaths.AGENT_WS_CONTEXT)
+ .withComponentConfig(getInstanceDefinition().getAppConfOperations()
+ .getComponent(SliderKeys.COMPONENT_AM))
+ .start();
+ agentAccessUrl = "https://" + appMasterHostname + ":" + agentWebApp.getSecuredPort();
+ AgentService agentService =
+ new AgentService("slider-agent", agentWebApp);
+
+ agentService.init(serviceConf);
+ agentService.start();
+ addService(agentService);
+
+ appInformation.put(StatusKeys.INFO_AM_AGENT_URL, agentAccessUrl + "/");
+ appInformation.set(StatusKeys.INFO_AM_AGENT_PORT, agentWebApp.getPort());
+ appInformation.set(StatusKeys.INFO_AM_SECURED_AGENT_PORT,
+ agentWebApp.getSecuredPort());
+ }
/**
* This registers the service instance and its external values
@@ -733,7 +776,8 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
private void registerServiceInstance(String instanceName,
ApplicationId appid) throws Exception {
// the registry is running, so register services
- URL amWebAPI = new URL(appMasterTrackingUrl);
+ URL unsecureWebAPI = new URL(appMasterTrackingUrl);
+ URL secureWebAPI = new URL(agentAccessUrl);
String serviceName = SliderKeys.APP_TYPE;
int id = appid.getId();
String appServiceType = RegistryNaming.createRegistryServiceType(
@@ -762,16 +806,22 @@ public class SliderAppMaster extends AbstractSliderLaunchedService
// internal services
- sliderAMProvider.applyInitialRegistryDefinitions(amWebAPI, instanceData);
+ sliderAMProvider.applyInitialRegistryDefinitions(unsecureWebAPI,
+ secureWebAPI,
+ instanceData
+ );
// provider service dynamic definitions.
- providerService.applyInitialRegistryDefinitions(amWebAPI, instanceData);
+ providerService.applyInitialRegistryDefinitions(unsecureWebAPI,
+ secureWebAPI,
+ instanceData
+ );
// push the registration info to ZK
registry.registerSelf(
- instanceData, amWebAPI
+ instanceData, unsecureWebAPI
);
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/AgentService.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/AgentService.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/AgentService.java
new file mode 100644
index 0000000..08338e8
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/AgentService.java
@@ -0,0 +1,53 @@
+/*
+ * 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.slider.server.appmaster.web;
+
+import org.apache.hadoop.service.AbstractService;
+import org.apache.slider.server.appmaster.web.rest.agent.AgentWebApp;
+
+/**
+ *
+ */
+public class AgentService extends AbstractService {
+ private volatile AgentWebApp webApp;
+
+ public AgentService(String name) {
+ super(name);
+ }
+
+ public AgentService(String name, AgentWebApp app) {
+ super(name);
+ webApp = app;
+ }
+
+ @Override
+ protected void serviceStart() throws Exception {
+
+ }
+
+ /**
+ * Stop operation stops the webapp; sets the reference to null
+ * @throws Exception
+ */
+ @Override
+ protected void serviceStop() throws Exception {
+ if (webApp != null) {
+ webApp.stop();
+ webApp = null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/SliderAmIpFilter.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/SliderAmIpFilter.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/SliderAmIpFilter.java
index aba344e..ad5e219 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/SliderAmIpFilter.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/SliderAmIpFilter.java
@@ -36,7 +36,9 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
public class SliderAmIpFilter implements Filter {
@@ -54,13 +56,13 @@ public class SliderAmIpFilter implements Filter {
private Set<String> proxyAddresses = null;
private long lastUpdate;
private String proxyUriBase;
- private String wsContextRoot;
+ private List<String> wsContextRoots;
@Override
public void init(FilterConfig conf) throws ServletException {
proxyHost = conf.getInitParameter(PROXY_HOST);
proxyUriBase = conf.getInitParameter(PROXY_URI_BASE);
- wsContextRoot = conf.getInitParameter(WS_CONTEXT_ROOT);
+ wsContextRoots = Arrays.asList(conf.getInitParameter(WS_CONTEXT_ROOT).split("\\|"));
}
protected Set<String> getProxyAddresses() throws ServletException {
@@ -102,7 +104,7 @@ public class SliderAmIpFilter implements Filter {
log.debug("Remote address for request is: " + httpReq.getRemoteAddr());
}
String requestURI = httpReq.getRequestURI();
- if(!requestURI.startsWith(wsContextRoot) &&
+ if(!isWsRequest(requestURI) &&
!getProxyAddresses().contains(httpReq.getRemoteAddr())) {
String redirectUrl = httpResp.encodeRedirectURL(proxyUriBase +
requestURI);
@@ -136,4 +138,14 @@ public class SliderAmIpFilter implements Filter {
throw e;
}
}
+
+ private boolean isWsRequest(String requestURI) {
+ boolean isWsReq = false;
+ for (String wsContext : wsContextRoots) {
+ isWsReq = requestURI.startsWith(wsContext);
+ if (isWsReq) break;
+ }
+
+ return isWsReq;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApi.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApi.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApi.java
index 4fac962..aa20baa 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApi.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApi.java
@@ -22,6 +22,7 @@ import org.apache.slider.server.appmaster.state.AppState;
import org.apache.slider.server.appmaster.state.RoleStatus;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.appmaster.web.rest.agent.AgentRestOperations;
+import org.apache.slider.server.services.security.CertificateManager;
import java.util.Map;
@@ -39,7 +40,13 @@ public interface WebAppApi {
* The {@link ProviderService} for the current cluster
*/
public ProviderService getProviderService();
-
+
+
+ /**
+ * The {@link CertificateManager} for the current cluster
+ */
+ public CertificateManager getCertificateManager();
+
/**
* The {@link SliderClusterProtocol} for the current cluster
*/
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApiImpl.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApiImpl.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApiImpl.java
index 9a5a628..4eebd45 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApiImpl.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/WebAppApiImpl.java
@@ -23,6 +23,7 @@ import org.apache.slider.providers.ProviderService;
import org.apache.slider.server.appmaster.state.RoleStatus;
import org.apache.slider.server.appmaster.state.StateAccessForProviders;
import org.apache.slider.server.appmaster.web.rest.agent.AgentRestOperations;
+import org.apache.slider.server.services.security.CertificateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,9 +46,12 @@ public class WebAppApiImpl implements WebAppApi {
protected final SliderClusterProtocol clusterProto;
protected final StateAccessForProviders appState;
protected final ProviderService provider;
+ protected final CertificateManager certificateManager;
public WebAppApiImpl(SliderClusterProtocol clusterProto,
- StateAccessForProviders appState, ProviderService provider) {
+ StateAccessForProviders appState,
+ ProviderService provider,
+ CertificateManager certificateManager) {
checkNotNull(clusterProto);
checkNotNull(appState);
checkNotNull(provider);
@@ -55,6 +59,7 @@ public class WebAppApiImpl implements WebAppApi {
this.clusterProto = clusterProto;
this.appState = appState;
this.provider = provider;
+ this.certificateManager = certificateManager;
}
/* (non-Javadoc)
@@ -73,9 +78,14 @@ public class WebAppApiImpl implements WebAppApi {
return provider;
}
+ @Override
+ public CertificateManager getCertificateManager() {
+ return certificateManager;
+ }
+
/* (non-Javadoc)
- * @see org.apache.slider.server.appmaster.web.WebAppApi#getClusterProtocol()
- */
+ * @see org.apache.slider.server.appmaster.web.WebAppApi#getClusterProtocol()
+ */
@Override
public SliderClusterProtocol getClusterProtocol() {
return clusterProto;
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/AMWebServices.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/AMWebServices.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/AMWebServices.java
index 91c83f2..4f068f3 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/AMWebServices.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/AMWebServices.java
@@ -42,12 +42,7 @@ public class AMWebServices {
return new ManagementResource(slider);
}
- @Path(RestPaths.SLIDER_SUBPATH_AGENTS)
- public AgentResource getAgentResource () {
- return new AgentResource(slider);
- }
-
- @Path(RestPaths.SLIDER_SUBPATH_PUBLISHER)
+ @Path(RestPaths.SLIDER_SUBPATH_PUBLISHER)
public PublisherResource getPublisherResource() {
return new PublisherResource(slider);
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java
index d55635f..0571ca1 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/RestPaths.java
@@ -24,15 +24,18 @@ package org.apache.slider.server.appmaster.web.rest;
public class RestPaths {
public static final String WS_CONTEXT = "ws";
+ public static final String AGENT_WS_CONTEXT = "ws";
public static final String WS_CONTEXT_ROOT = "/" + WS_CONTEXT;
+ public static final String WS_AGENT_CONTEXT_ROOT = "/" + AGENT_WS_CONTEXT;
public static final String SLIDER_CONTEXT_ROOT = WS_CONTEXT_ROOT +"/v1/slider";
+ public static final String SLIDER_AGENT_CONTEXT_ROOT = WS_AGENT_CONTEXT_ROOT +"/v1/slider";
public static final String SLIDER_SUBPATH_MANAGEMENT = "/mgmt";
public static final String SLIDER_SUBPATH_AGENTS = "/agents";
public static final String SLIDER_SUBPATH_PUBLISHER = "/publisher";
public static final String SLIDER_PATH_MANAGEMENT = SLIDER_CONTEXT_ROOT
+ SLIDER_SUBPATH_MANAGEMENT;
- public static final String SLIDER_PATH_AGENTS = SLIDER_CONTEXT_ROOT
+ public static final String SLIDER_PATH_AGENTS = SLIDER_AGENT_CONTEXT_ROOT
+ SLIDER_SUBPATH_AGENTS;
public static final String SLIDER_PATH_PUBLISHER = SLIDER_CONTEXT_ROOT
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentResource.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentResource.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentResource.java
index 96b7b47..9d1e840 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentResource.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentResource.java
@@ -17,9 +17,12 @@
package org.apache.slider.server.appmaster.web.rest.agent;
import org.apache.slider.server.appmaster.web.WebAppApi;
+import org.apache.slider.server.services.security.SignCertResponse;
+import org.apache.slider.server.services.security.SignMessage;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -50,14 +53,14 @@ public class AgentResource {
}
@GET
- @Path("/agents/register")
+ @Path("/agent/register")
public Response endpointAgentRegister() {
Response response = Response.status(200).entity("/agent/register").build();
return response;
}
@GET
- @Path("/agents")
+ @Path("/agent")
public Response endpointAgent() {
Response response = Response.status(200).entity("/agent").build();
return response;
@@ -94,4 +97,22 @@ public class AgentResource {
AgentRestOperations ops = slider.getAgentRestOperations();
return ops.handleHeartBeat(message);
}
+
+ @GET
+ @Path("/cert/ca")
+ @Produces({MediaType.TEXT_PLAIN})
+ public String downloadSrvrCrt() {
+ return slider.getCertificateManager().getServerCert();
+ }
+
+ @Path("/certs/{hostName}")
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ public SignCertResponse signAgentCrt(@PathParam("hostName") String hostname,
+ SignMessage message, @Context HttpServletRequest req) {
+ return slider.getCertificateManager().signAgentCrt(hostname,
+ message.getCsr(),
+ message.getPassphrase());
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
new file mode 100644
index 0000000..54d2b1f
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
@@ -0,0 +1,203 @@
+/*
+ * 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.slider.server.appmaster.web.rest.agent;
+
+import com.google.common.base.Preconditions;
+import com.sun.jersey.api.core.ResourceConfig;
+import com.sun.jersey.spi.container.WebApplication;
+import com.sun.jersey.spi.container.servlet.ServletContainer;
+import com.sun.jersey.spi.container.servlet.WebConfig;
+import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
+import org.apache.slider.core.conf.MapOperations;
+import org.apache.slider.server.appmaster.web.WebAppApi;
+import org.apache.slider.server.appmaster.web.rest.RestPaths;
+import org.apache.slider.server.services.security.SecurityUtils;
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.security.SslSelectChannelConnector;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
+import org.mortbay.thread.QueuedThreadPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.ext.Provider;
+import java.io.File;
+import java.util.Set;
+
+/**
+ *
+ */
+public class AgentWebApp {
+ protected static final Logger LOG = LoggerFactory.getLogger(AgentWebApp.class);
+ private int port;
+ private int securedPort;
+ private static Server agentServer;
+ public static final String BASE_PATH = "slideragent";
+
+ public static class Builder {
+ final String name;
+ final String wsName;
+ final WebAppApi application;
+ MapOperations configsMap;
+
+ public Builder(String name, String wsName, WebAppApi application) {
+ this.name = name;
+ this.wsName = wsName;
+ this.application = application;
+ }
+
+ public Builder withComponentConfig(MapOperations appMasterConfig) {
+ this.configsMap = appMasterConfig;
+ return this;
+ }
+
+ public AgentWebApp start() {
+ if (configsMap == null) {
+ throw new IllegalStateException("No SSL Configuration Available");
+ }
+
+ agentServer = new Server();
+ agentServer.setThreadPool(
+ new QueuedThreadPool(
+ configsMap.getOptionInt("agent.threadpool.size.max", 25)));
+ agentServer.setStopAtShutdown(true);
+
+ SslSelectChannelConnector ssl1WayConnector = createSSLConnector(false);
+ SslSelectChannelConnector ssl2WayConnector =
+ createSSLConnector(Boolean.valueOf(
+ configsMap.getOption("ssl.server.client.auth","false")));
+ agentServer.setConnectors(new Connector[]{ssl1WayConnector,
+ ssl2WayConnector});
+
+ ServletHolder agent = new ServletHolder(new AgentServletContainer());
+ Context agentRoot = new Context(agentServer, "/", Context.SESSIONS);
+
+ agent.setInitParameter("com.sun.jersey.config.property.resourceConfigClass",
+ "com.sun.jersey.api.core.PackagesResourceConfig");
+ agent.setInitParameter("com.sun.jersey.config.property.packages",
+ "org.apache.slider.server.appmaster.web.rest.agent");
+ agent.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature",
+ "true");
+// agent.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter");
+// agent.setInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.api.container.filter.LoggingFilter");
+// agent.setInitParameter("com.sun.jersey.config.feature.Trace", "true");
+ agentRoot.addServlet(agent, "/*");
+
+ try {
+ agentServer.start();
+ } catch (Exception e) {
+ LOG.error("Unable to start agent server", e);
+ }
+
+ AgentWebApp webApp = new AgentWebApp();
+ webApp.setPort(getConnectorPort(agentServer, 0));
+ webApp.setSecuredPort(getConnectorPort(agentServer, 1));
+
+ return webApp;
+
+ }
+
+ private SslSelectChannelConnector createSSLConnector(boolean needClientAuth) {
+ SslSelectChannelConnector sslConnector = new
+ SslSelectChannelConnector();
+
+ String keystore = SecurityUtils.getSecurityDir() +
+ File.separator + "keystore.p12";
+ String srvrCrtPass = SecurityUtils.getKeystorePass();
+ sslConnector.setKeystore(keystore);
+ sslConnector.setTruststore(keystore);
+ sslConnector.setPassword(srvrCrtPass);
+ sslConnector.setKeyPassword(srvrCrtPass);
+ sslConnector.setTrustPassword(srvrCrtPass);
+ sslConnector.setKeystoreType("PKCS12");
+ sslConnector.setTruststoreType("PKCS12");
+ sslConnector.setNeedClientAuth(needClientAuth);
+
+ sslConnector.setAcceptors(2);
+ return sslConnector;
+ }
+
+ @Provider
+ public class WebAppApiProvider extends
+ SingletonTypeInjectableProvider<javax.ws.rs.core.Context, WebAppApi> {
+
+ public WebAppApiProvider () {
+ super(WebAppApi.class, application);
+ }
+ }
+
+ public class AgentServletContainer extends ServletContainer {
+ public AgentServletContainer() {
+ super();
+ }
+
+ @Override
+ protected void configure(WebConfig wc,
+ ResourceConfig rc,
+ WebApplication wa) {
+ super.configure(wc, rc, wa);
+ Set<Object> singletons = rc.getSingletons();
+ singletons.add(new WebAppApiProvider());
+ }
+ }
+
+ private int getConnectorPort(Server webServer, int index) {
+ Preconditions.checkArgument(index >= 0);
+ if (index > webServer.getConnectors().length)
+ throw new IllegalStateException("Illegal connect index requested");
+
+ Connector c = webServer.getConnectors()[index];
+ if (c.getLocalPort() == -1) {
+ // The connector is not bounded
+ throw new IllegalStateException("The connector is not bound to a port");
+ }
+
+ return c.getLocalPort();
+ }
+ }
+
+ public static Builder $for(String name, WebAppApi app, String wsPrefix) {
+ return new Builder(name, wsPrefix, app);
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public void setSecuredPort(int securedPort) {
+ this.securedPort = securedPort;
+ }
+
+ public int getSecuredPort() {
+ return securedPort;
+ }
+
+ public void stop() {
+ //need to stop server and reset injector
+ try {
+ agentServer.stop();
+ } catch (Exception e) {
+ LOG.warn("Unable to stop agent server", e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebServices.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebServices.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebServices.java
new file mode 100644
index 0000000..684ce6f
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebServices.java
@@ -0,0 +1,40 @@
+/*
+ * 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.slider.server.appmaster.web.rest.agent;
+
+import org.apache.slider.server.appmaster.web.WebAppApi;
+import org.apache.slider.server.appmaster.web.rest.RestPaths;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+/** The available agent REST services exposed by a slider AM. */
+@Path(RestPaths.SLIDER_AGENT_CONTEXT_ROOT)
+public class AgentWebServices {
+ /** AM/WebApp info object */
+ @Context
+ private WebAppApi slider;
+
+ public AgentWebServices() {
+ }
+
+ @Path(RestPaths.SLIDER_SUBPATH_AGENTS)
+ public AgentResource getAgentResource () {
+ return new AgentResource(slider);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/services/security/CertificateManager.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/services/security/CertificateManager.java b/slider-core/src/main/java/org/apache/slider/server/services/security/CertificateManager.java
new file mode 100644
index 0000000..3771208
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/services/security/CertificateManager.java
@@ -0,0 +1,257 @@
+/**
+ * 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.slider.server.services.security;
+
+import com.google.inject.Singleton;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.hadoop.yarn.api.ApplicationConstants;
+import org.apache.slider.common.SliderKeys;
+import org.apache.slider.core.conf.MapOperations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.text.MessageFormat;
+
+@Singleton
+public class CertificateManager {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(CertificateManager.class);
+
+ private static final String GEN_SRVR_KEY = "openssl genrsa -des3 " +
+ "-passout pass:{0} -out {1}/{2} 4096 ";
+ private static final String GEN_SRVR_REQ = "openssl req -passin pass:{0} " +
+ "-new -key {1}/{2} -out {1}/{5} -batch";
+ private static final String SIGN_SRVR_CRT = "openssl ca -create_serial " +
+ "-out {1}/{3} -days 365 -keyfile {1}/{2} -key {0} -selfsign " +
+ "-extensions jdk7_ca -config {1}/ca.config -batch " +
+ "-infiles {1}/{5}";
+ private static final String EXPRT_KSTR = "openssl pkcs12 -export" +
+ " -in {1}/{3} -inkey {1}/{2} -certfile {1}/{3} -out {1}/{4} " +
+ "-password pass:{0} -passin pass:{0} \n";
+ private static final String REVOKE_AGENT_CRT = "openssl ca " +
+ "-config {0}/ca.config -keyfile {0}/{4} -revoke {0}/{2} -batch " +
+ "-passin pass:{3} -cert {0}/{5}";
+ private static final String SIGN_AGENT_CRT = "openssl ca -config " +
+ "{0}/ca.config -in {0}/{1} -out {0}/{2} -batch -passin pass:{3} " +
+ "-keyfile {0}/{4} -cert {0}/{5}"; /**
+ * Verify that root certificate exists, generate it otherwise.
+ */
+ public void initRootCert(MapOperations compOperations) {
+ SecurityUtils.initializeSecurityParameters(compOperations);
+
+ LOG.info("Initialization of root certificate");
+ boolean certExists = isCertExists();
+ LOG.info("Certificate exists:" + certExists);
+
+ if (!certExists) {
+ generateServerCertificate();
+ }
+
+ }
+
+ /**
+ * Checks root certificate state.
+ * @return "true" if certificate exists
+ */
+ private boolean isCertExists() {
+
+ String srvrKstrDir = SecurityUtils.getSecurityDir();
+ String srvrCrtName = SliderKeys.CRT_FILE_NAME;
+ File certFile = new File(srvrKstrDir + File.separator + srvrCrtName);
+ LOG.debug("srvrKstrDir = " + srvrKstrDir);
+ LOG.debug("srvrCrtName = " + srvrCrtName);
+ LOG.debug("certFile = " + certFile.getAbsolutePath());
+
+ return certFile.exists();
+ }
+
+ /**
+ * Runs os command
+ *
+ * @return command execution exit code
+ */
+ private int runCommand(String command) {
+ String line = null;
+ Process process = null;
+ BufferedReader br= null;
+ try {
+ process = Runtime.getRuntime().exec(command);
+ br = new BufferedReader(new InputStreamReader(
+ process.getInputStream(), Charset.forName("UTF8")));
+
+ while ((line = br.readLine()) != null) {
+ LOG.info(line);
+ }
+
+ try {
+ process.waitFor();
+ SecurityUtils.logOpenSslExitCode(command, process.exitValue());
+ return process.exitValue(); //command is executed
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ return -1;//some exception occurred
+
+ }
+
+ private void generateServerCertificate() {
+ LOG.info("Generation of server certificate");
+
+ String srvrKstrDir = SecurityUtils.getSecurityDir();
+ String srvrCrtName = SliderKeys.CRT_FILE_NAME;
+ String srvrCsrName = SliderKeys.CSR_FILE_NAME;
+ String srvrKeyName = SliderKeys.KEY_FILE_NAME;
+ String kstrName = SliderKeys.KEYSTORE_FILE_NAME;
+ String srvrCrtPass = SecurityUtils.getKeystorePass();
+
+ Object[] scriptArgs = {srvrCrtPass, srvrKstrDir, srvrKeyName,
+ srvrCrtName, kstrName, srvrCsrName};
+
+ String command = MessageFormat.format(GEN_SRVR_KEY,scriptArgs);
+ runCommand(command);
+
+ command = MessageFormat.format(GEN_SRVR_REQ,scriptArgs);
+ runCommand(command);
+
+ command = MessageFormat.format(SIGN_SRVR_CRT,scriptArgs);
+ runCommand(command);
+
+ command = MessageFormat.format(EXPRT_KSTR,scriptArgs);
+ runCommand(command);
+
+ }
+
+ /**
+ * Returns server certificate content
+ * @return string with server certificate content
+ */
+ public String getServerCert() {
+ File certFile = new File(SecurityUtils.getSecurityDir() +
+ File.separator + SliderKeys.CRT_FILE_NAME);
+ String srvrCrtContent = null;
+ try {
+ srvrCrtContent = FileUtils.readFileToString(certFile);
+ } catch (IOException e) {
+ LOG.error(e.getMessage());
+ }
+ return srvrCrtContent;
+ }
+
+ /**
+ * Signs agent certificate
+ * Adds agent certificate to server keystore
+ * @return string with agent signed certificate content
+ */
+ public synchronized SignCertResponse signAgentCrt(String agentHostname,
+ String agentCrtReqContent,
+ String passphraseAgent) {
+ SignCertResponse response = new SignCertResponse();
+ LOG.info("Signing of agent certificate");
+ LOG.info("Verifying passphrase");
+
+ String passphraseSrvr = SliderKeys.PASSPHRASE;
+
+ if (!passphraseSrvr.equals(passphraseAgent.trim())) {
+ LOG.warn("Incorrect passphrase from the agent");
+ response.setResult(SignCertResponse.ERROR_STATUS);
+ response.setMessage("Incorrect passphrase from the agent");
+ return response;
+ }
+
+ String srvrKstrDir = SecurityUtils.getSecurityDir();
+ String srvrCrtPass = SecurityUtils.getKeystorePass();
+ String srvrCrtName = SliderKeys.CRT_FILE_NAME;
+ String srvrKeyName = SliderKeys.KEY_FILE_NAME;
+ String agentCrtReqName = agentHostname + ".csr";
+ String agentCrtName = agentHostname + ".crt";
+
+ Object[] scriptArgs = {srvrKstrDir, agentCrtReqName, agentCrtName,
+ srvrCrtPass, srvrKeyName, srvrCrtName};
+
+ //Revoke previous agent certificate if exists
+ File agentCrtFile = new File(srvrKstrDir + File.separator + agentCrtName);
+
+ if (agentCrtFile.exists()) {
+ LOG.info("Revoking of " + agentHostname + " certificate.");
+ String command = MessageFormat.format(REVOKE_AGENT_CRT, scriptArgs);
+ int commandExitCode = runCommand(command);
+ if (commandExitCode != 0) {
+ response.setResult(SignCertResponse.ERROR_STATUS);
+ response.setMessage(
+ SecurityUtils.getOpenSslCommandResult(command, commandExitCode));
+ return response;
+ }
+ }
+
+ File agentCrtReqFile = new File(srvrKstrDir + File.separator +
+ agentCrtReqName);
+ try {
+ FileUtils.writeStringToFile(agentCrtReqFile, agentCrtReqContent);
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ String command = MessageFormat.format(SIGN_AGENT_CRT, scriptArgs);
+
+ LOG.debug(SecurityUtils.hideOpenSslPassword(command));
+
+ int commandExitCode = runCommand(command); // ssl command execution
+ if (commandExitCode != 0) {
+ response.setResult(SignCertResponse.ERROR_STATUS);
+ response.setMessage(
+ SecurityUtils.getOpenSslCommandResult(command, commandExitCode));
+ //LOG.warn(ShellCommandUtil.getOpenSslCommandResult(command, commandExitCode));
+ return response;
+ }
+
+ String agentCrtContent = "";
+ try {
+ agentCrtContent = FileUtils.readFileToString(agentCrtFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ LOG.error("Error reading signed agent certificate");
+ response.setResult(SignCertResponse.ERROR_STATUS);
+ response.setMessage("Error reading signed agent certificate");
+ return response;
+ }
+ response.setResult(SignCertResponse.OK_STATUS);
+ response.setSignedCa(agentCrtContent);
+ //LOG.info(ShellCommandUtil.getOpenSslCommandResult(command, commandExitCode));
+ return response;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/services/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/services/security/SecurityUtils.java b/slider-core/src/main/java/org/apache/slider/server/services/security/SecurityUtils.java
new file mode 100644
index 0000000..5238d90
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/services/security/SecurityUtils.java
@@ -0,0 +1,209 @@
+/*
+ * 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.slider.server.services.security;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.slider.common.SliderKeys;
+import org.apache.slider.core.conf.MapOperations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+
+/**
+ *
+ */
+public class SecurityUtils {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SecurityUtils.class);
+
+ private static String CA_CONFIG_CONTENTS ="[ ca ]\n"
+ + "default_ca = CA_CLIENT\n"
+ + "[ CA_CLIENT ]\n"
+ + "dir\t\t = ${SEC_DIR}/db\n"
+ + "certs = $dir/certs\n"
+ + "new_certs_dir = $dir/newcerts\n"
+ + "\n"
+ + "database = $dir/index.txt\n"
+ + "serial = $dir/serial\n"
+ + "default_days = 365 \n"
+ + "\n"
+ + "default_crl_days = 7 \n"
+ + "default_md = md5 \n"
+ + "\n"
+ + "policy = policy_anything \n"
+ + "\n"
+ + "[ policy_anything ]\n"
+ + "countryName = optional\n"
+ + "stateOrProvinceName = optional \n"
+ + "localityName = optional\n"
+ + "organizationName = optional\n"
+ + "organizationalUnitName = optional\n"
+ + "commonName = optional \n"
+ + "emailAddress = optional \n"
+ + "\n"
+ + "[ jdk7_ca ]\n"
+ + "subjectKeyIdentifier = hash\n"
+ + "authorityKeyIdentifier = keyid:always,issuer:always\n"
+ + "basicConstraints = CA:true";
+
+ private static final String PASS_TOKEN = "pass:";
+ private static String keystorePass;
+ private static String securityDir;
+
+ public static void logOpenSslExitCode(String command, int exitCode) {
+ if (exitCode == 0) {
+ LOG.info(getOpenSslCommandResult(command, exitCode));
+ } else {
+ LOG.warn(getOpenSslCommandResult(command, exitCode));
+ }
+
+ }
+
+ public static String hideOpenSslPassword(String command){
+ int start = command.indexOf(PASS_TOKEN)+PASS_TOKEN.length();
+ CharSequence cs = command.subSequence(start, command.indexOf(" ", start));
+ return command.replace(cs, "****");
+ }
+
+ public static String getOpenSslCommandResult(String command, int exitCode) {
+ return new StringBuilder().append("Command ").append(hideOpenSslPassword(command)).append(" was finished with exit code: ")
+ .append(exitCode).append(" - ").append(getOpenSslExitCodeDescription(exitCode)).toString();
+ }
+
+ private static String getOpenSslExitCodeDescription(int exitCode) {
+ switch (exitCode) {
+ case 0: {
+ return "the operation was completed successfully.";
+ }
+ case 1: {
+ return "an error occurred parsing the command options.";
+ }
+ case 2: {
+ return "one of the input files could not be read.";
+ }
+ case 3: {
+ return "an error occurred creating the PKCS#7 file or when reading the MIME message.";
+ }
+ case 4: {
+ return "an error occurred decrypting or verifying the message.";
+ }
+ case 5: {
+ return "the message was verified correctly but an error occurred writing out the signers certificates.";
+ }
+ default:
+ return "unsupported code";
+ }
+ }
+
+ public static void writeCaConfigFile(String path) throws IOException {
+ String contents = CA_CONFIG_CONTENTS.replace("${SEC_DIR}", path);
+ FileUtils.writeStringToFile(new File(path, "ca.config"), contents);
+ }
+
+ public static String getKeystorePass() {
+ return keystorePass;
+ }
+
+ public static String getSecurityDir() {
+ return securityDir;
+ }
+
+ public static void initializeSecurityParameters(MapOperations configMap) {
+ String keyStoreLocation = configMap.getOption(
+ SliderKeys.KEYSTORE_LOCATION, getDefaultKeystoreLocation());
+ File secDirFile = new File(keyStoreLocation).getParentFile();
+ if (!secDirFile.exists()) {
+ // create entire required directory structure
+ File dbDir = new File(secDirFile, "db");
+ File newCertsDir = new File(dbDir, "newcerts");
+ newCertsDir.mkdirs();
+ try {
+ Set<PosixFilePermission> perms =
+ PosixFilePermissions.fromString("rwx------");
+ Files.setPosixFilePermissions(Paths.get(secDirFile.toURI()), perms);
+ Files.setPosixFilePermissions(Paths.get(dbDir.toURI()), perms);
+ Files.setPosixFilePermissions(Paths.get(newCertsDir.toURI()), perms);
+ File indexFile = new File(dbDir, "index.txt");
+ indexFile.createNewFile();
+
+ SecurityUtils.writeCaConfigFile(secDirFile.getAbsolutePath());
+
+ } catch (IOException e) {
+ LOG.error("Unable to create SSL configuration directories/files", e);
+ }
+ // need to create the password
+ }
+ keystorePass = getKeystorePassword(secDirFile);
+ securityDir = secDirFile.getAbsolutePath();
+ }
+
+ private static String getKeystorePassword(File secDirFile) {
+ File passFile = new File(secDirFile, SliderKeys.CRT_PASS_FILE_NAME);
+ String password = null;
+
+ if (!passFile.exists()) {
+ LOG.info("Generation of file with password");
+ try {
+ password = RandomStringUtils.randomAlphanumeric(
+ Integer.valueOf(SliderKeys.PASS_LEN));
+ FileUtils.writeStringToFile(passFile, password);
+ passFile.setWritable(true);
+ passFile.setReadable(true);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(
+ "Error creating certificate password file");
+ }
+ } else {
+ LOG.info("Reading password from existing file");
+ try {
+ password = FileUtils.readFileToString(passFile);
+ password = password.replaceAll("\\p{Cntrl}", "");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return password;
+ }
+
+ private static String getDefaultKeystoreLocation() {
+ Path workDir = null;
+ try {
+ workDir = Files.createTempDirectory("sec");
+ } catch (IOException e) {
+ LOG.warn("Unable to create security directory");
+ return null;
+ }
+
+ return new StringBuilder().append(workDir.toAbsolutePath())
+ .append(File.separator)
+ .append(SliderKeys.SECURITY_DIR)
+ .append(File.separator)
+ .append(SliderKeys.KEYSTORE_FILE_NAME).toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/859ca521/slider-core/src/main/java/org/apache/slider/server/services/security/SignCertResponse.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/server/services/security/SignCertResponse.java b/slider-core/src/main/java/org/apache/slider/server/services/security/SignCertResponse.java
new file mode 100644
index 0000000..8437d88
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/server/services/security/SignCertResponse.java
@@ -0,0 +1,67 @@
+/**
+ * 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.slider.server.services.security;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ *
+ * Sign certificate response data model.
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {})
+public class SignCertResponse {
+
+ public static final String ERROR_STATUS = "ERROR";
+ public static final String OK_STATUS = "OK";
+
+ @XmlElement
+ private String result;
+ @XmlElement
+ private String signedCa;
+ @XmlElement
+ private String message;
+
+ public String getResult() {
+ return result;
+ }
+ public void setResult(String result) {
+ this.result = result;
+ }
+ public String getSignedCa() {
+ return signedCa;
+ }
+ public void setSignedCa(String signedCa) {
+ this.signedCa = signedCa;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
+