More on ARTEMIS-594: support HTTPS access to hawtio
Remove the keystore.jks in distribution Add documentation Add cli options
This commit is contained in:
parent
880539a960
commit
3522979bda
|
@ -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}", "");
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Binary file not shown.
|
@ -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.
|
||||
|
@ -115,6 +123,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";
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue