Repository: activemq-artemis Updated Branches: refs/heads/master 880539a96 -> 77cc6407c
More on ARTEMIS-594: support HTTPS access to hawtio Remove the keystore.jks in distribution Add documentation Add cli options Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/3522979b Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/3522979b Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/3522979b Branch: refs/heads/master Commit: 3522979bda95e9be57f31739b22101feffbb21a2 Parents: 880539a Author: Howard Gao <[email protected]> Authored: Thu Jun 30 09:56:04 2016 +0800 Committer: Howard Gao <[email protected]> Committed: Thu Jun 30 09:56:58 2016 +0800 ---------------------------------------------------------------------- .../activemq/artemis/cli/commands/Create.java | 57 ++++++++++-- .../cli/commands/etc/bootstrap-web-settings.txt | 2 +- .../artemis/cli/commands/etc/keystore.jks | Bin 2236 -> 0 bytes .../apache/activemq/cli/test/ArtemisTest.java | 88 +++++++++++++++++++ docs/user-manual/en/security.md | 32 +++++++ 5 files changed, 173 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/3522979b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java ---------------------------------------------------------------------- diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java index 991bd69..c99bc78 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java @@ -58,7 +58,7 @@ public class Create extends InputAbstract { private static final Integer HQ_PORT = 5445; - private static final Integer HTTP_PORT = 8161; + public static final Integer HTTP_PORT = 8161; private static final Integer MQTT_PORT = 1883; @@ -72,7 +72,6 @@ public class Create extends InputAbstract { public static final String ETC_LOGGING_PROPERTIES = "etc/logging.properties"; public static final String ETC_BOOTSTRAP_XML = "etc/bootstrap.xml"; public static final String ETC_BROKER_XML = "etc/broker.xml"; - public static final String ETC_WEB_KEYSTORE = "etc/keystore.jks"; public static final String ETC_ARTEMIS_ROLES_PROPERTIES = "etc/artemis-roles.properties"; public static final String ETC_ARTEMIS_USERS_PROPERTIES = "etc/artemis-users.properties"; @@ -103,6 +102,21 @@ public class Create extends InputAbstract { @Option(name = "--http-port", description = "The port number to use for embedded web server (Default: 8161)") int httpPort = HTTP_PORT; + @Option(name = "--ssl-key", description = "The key store path for embedded web server") + String sslKey; + + @Option(name = "--ssl-key-password", description = "The key store password") + String sslKeyPassword; + + @Option(name = "--use-client-auth", description = "If the embedded server requires client authentication") + boolean useClientAuth; + + @Option(name = "--ssl-trust", description = "The trust store path in case of client authentication") + String sslTrust; + + @Option(name = "--ssl-trust-password", description = "The trust store password") + String sslTrustPassword; + @Option(name = "--name", description = "The name of the broker (Default: same as host)") String name; @@ -347,6 +361,27 @@ public class Create extends InputAbstract { return clusterPassword; } + public String getSslKeyPassword() { + if (sslKeyPassword == null) { + sslKeyPassword = inputPassword("--ssl-key-password", "Please enter the keystore password:", "password"); + } + return sslKeyPassword; + } + + public String getSslTrust() { + if (sslTrust == null) { + sslTrust = input("--ssl-trust", "Please enter the trust store path:", "/etc/truststore.jks"); + } + return sslTrust; + } + + public String getSslTrustPassword() { + if (sslTrustPassword == null) { + sslTrustPassword = inputPassword("--ssl-key-password", "Please enter the keystore password:", "password"); + } + return sslTrustPassword; + } + public void setClusterPassword(String clusterPassword) { this.clusterPassword = clusterPassword; } @@ -522,6 +557,21 @@ public class Create extends InputAbstract { filters.put("${journal.settings}", "ASYNCIO"); } + if (sslKey != null) { + filters.put("${web.protocol}", "https"); + getSslKeyPassword(); + String extraWebAttr = " keyStorePath=\"" + sslKey + "\" keyStorePassword=\"" + sslKeyPassword + "\""; + if (useClientAuth) { + getSslTrust(); + getSslTrustPassword(); + extraWebAttr += " clientAuth=\"true\" trustStorePath=\"" + sslTrust + "\" trustStorePassword=\"" + sslTrustPassword + "\""; + } + filters.put("${extra.web.attributes}", extraWebAttr); + } + else { + filters.put("${web.protocol}", "http"); + filters.put("${extra.web.attributes}", ""); + } filters.put("${user}", System.getProperty("user.name", "")); filters.put("${default.port}", String.valueOf(defaultPort + portOffset)); filters.put("${amqp.port}", String.valueOf(AMQP_PORT + portOffset)); @@ -625,9 +675,6 @@ public class Create extends InputAbstract { filters.put("${bootstrap-web-settings}", applyFilters(readTextFile(ETC_BOOTSTRAP_WEB_SETTINGS_TXT), filters)); } - //keystore - write(ETC_WEB_KEYSTORE); - if (noAmqpAcceptor) { filters.put("${amqp-acceptor}", ""); } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/3522979b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap-web-settings.txt ---------------------------------------------------------------------- diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap-web-settings.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap-web-settings.txt index 5612269..49c5e37 100644 --- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap-web-settings.txt +++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap-web-settings.txt @@ -1,4 +1,4 @@ <!-- The web server is only bound to loalhost by default --> - <web bind="http://localhost:${http.port}" path="web"> + <web bind="${web.protocol}://localhost:${http.port}" path="web"${extra.web.attributes}> <app url="jolokia" war="jolokia-war-1.3.3.war"/> </web> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/3522979b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/keystore.jks ---------------------------------------------------------------------- diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/keystore.jks b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/keystore.jks deleted file mode 100644 index f5a6760..0000000 Binary files a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/keystore.jks and /dev/null differ http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/3522979b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java ---------------------------------------------------------------------- diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java index b3a9e29..969138f 100644 --- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java +++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java @@ -20,7 +20,12 @@ import javax.jms.Connection; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.SimpleString; @@ -43,6 +48,9 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; /** * Test to validate that the CLI doesn't throw improper exceptions when invoked. @@ -116,6 +124,81 @@ public class ArtemisTest { } @Test + public void testWebConfig() throws Exception { + Run.setEmbedded(true); + //instance1: default using http + File instance1 = new File(temporaryFolder.getRoot(), "instance1"); + Artemis.main("create", instance1.getAbsolutePath(), "--silent"); + File bootstrapFile = new File(new File(instance1, "etc"), "bootstrap.xml"); + Assert.assertTrue(bootstrapFile.exists()); + Document config = parseXml(bootstrapFile); + Element webElem = (Element)config.getElementsByTagName("web").item(0); + + String bindAttr = webElem.getAttribute("bind"); + String bindStr = "http://localhost:" + Create.HTTP_PORT; + + Assert.assertEquals(bindAttr, bindStr); + //no any of those + Assert.assertFalse(webElem.hasAttribute("keyStorePath")); + Assert.assertFalse(webElem.hasAttribute("keyStorePassword")); + Assert.assertFalse(webElem.hasAttribute("clientAuth")); + Assert.assertFalse(webElem.hasAttribute("trustStorePath")); + Assert.assertFalse(webElem.hasAttribute("trustStorePassword")); + + //instance2: https + File instance2 = new File(temporaryFolder.getRoot(), "instance2"); + Artemis.main("create", instance2.getAbsolutePath(), "--silent", "--ssl-key", "etc/keystore", "--ssl-key-password", "password1"); + bootstrapFile = new File(new File(instance2, "etc"), "bootstrap.xml"); + Assert.assertTrue(bootstrapFile.exists()); + config = parseXml(bootstrapFile); + webElem = (Element)config.getElementsByTagName("web").item(0); + + bindAttr = webElem.getAttribute("bind"); + bindStr = "https://localhost:" + Create.HTTP_PORT; + Assert.assertEquals(bindAttr, bindStr); + + String keyStr = webElem.getAttribute("keyStorePath"); + Assert.assertEquals("etc/keystore", keyStr); + String keyPass = webElem.getAttribute("keyStorePassword"); + Assert.assertEquals("password1", keyPass); + + Assert.assertFalse(webElem.hasAttribute("clientAuth")); + Assert.assertFalse(webElem.hasAttribute("trustStorePath")); + Assert.assertFalse(webElem.hasAttribute("trustStorePassword")); + + //instance3: https with clientAuth + File instance3 = new File(temporaryFolder.getRoot(), "instance3"); + Artemis.main("create", instance3.getAbsolutePath(), "--silent", "--ssl-key", "etc/keystore", + "--ssl-key-password", "password1", + "--use-client-auth", "--ssl-trust", "etc/truststore", "--ssl-trust-password", "password2"); + bootstrapFile = new File(new File(instance3, "etc"), "bootstrap.xml"); + Assert.assertTrue(bootstrapFile.exists()); + + byte[] contents = Files.readAllBytes(bootstrapFile.toPath()); + String cfgText = new String(contents); + System.out.println("confg: " + cfgText); + + config = parseXml(bootstrapFile); + webElem = (Element)config.getElementsByTagName("web").item(0); + + bindAttr = webElem.getAttribute("bind"); + bindStr = "https://localhost:" + Create.HTTP_PORT; + Assert.assertEquals(bindAttr, bindStr); + + keyStr = webElem.getAttribute("keyStorePath"); + Assert.assertEquals("etc/keystore", keyStr); + keyPass = webElem.getAttribute("keyStorePassword"); + Assert.assertEquals("password1", keyPass); + + String clientAuthAttr = webElem.getAttribute("clientAuth"); + Assert.assertEquals("true", clientAuthAttr); + String trustPathAttr = webElem.getAttribute("trustStorePath"); + Assert.assertEquals("etc/truststore", trustPathAttr); + String trustPass = webElem.getAttribute("trustStorePassword"); + Assert.assertEquals("password2", trustPass); + } + + @Test public void testSimpleRun() throws Exception { String queues = "q1,t2"; String topics = "t1,t2"; @@ -230,5 +313,10 @@ public class ArtemisTest { Assert.assertEquals(0, LibaioContext.getTotalMaxIO()); } + private static Document parseXml(File xmlFile) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder domBuilder = domFactory.newDocumentBuilder(); + return domBuilder.parse(xmlFile); + } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/3522979b/docs/user-manual/en/security.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index 53a6efb..6c0d078 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -648,3 +648,35 @@ they use for this should always be changed from the installation default to prevent a security risk. Please see [Management](management.md) for instructions on how to do this. + + +## Securing the console + +Artemis comes with a web console that allows user to browse Artemis documentation via an embedded server. By default the +web access is plain HTTP. It is configured in `bootstrap.xml`: + + <web bind="http://localhost:8161" path="web"> + <app url="jolokia" war="jolokia-war-1.3.3.war"/> + </web> + +Alternatively you can edit the above configuration to enable secure access using HTTPS protocol. e.g.: + + <web bind="https://localhost:8443" + path="web" + keyStorePath="${artemis.instance}/etc/keystore.jks" + keyStorePassword="password"> + <app url="jolokia" war="jolokia-war-1.3.3.war"/> + </web> + +As shown in the example, to enable https the first thing to do is config the `bind` to be an `https` url. In addition, +You will have to configure a few extra properties desribed as below. + +- `keyStorePath` - The path of the key store file. + +- `keyStorePassword` - The key store's password. + +- `clientAuth` - The boolean flag indicates whether or not client authentication is required. Default is `false`. + +- `trustStorePath` - The path of the trust store file. This is needed only if `clientAuth` is `true`. + +- `trustStorePassword` - The trust store's password.
