You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by jb...@apache.org on 2018/01/18 14:59:17 UTC
[2/3] activemq-artemis git commit: ARTEMIS-1600 Support masked
passwords in bootstrap.xm and login.config
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/docs/user-manual/en/masking-passwords.md
----------------------------------------------------------------------
diff --git a/docs/user-manual/en/masking-passwords.md b/docs/user-manual/en/masking-passwords.md
index 4f43524..32329ba 100644
--- a/docs/user-manual/en/masking-passwords.md
+++ b/docs/user-manual/en/masking-passwords.md
@@ -18,12 +18,33 @@ Apache ActiveMQ Artemis provides a default password encoder and decoder. Optiona
users can use or implement their own encoder and decoder for masking the
passwords.
+In general, a masked password can be identified using one of two ways. The first one
+is the ENC() syntax, i.e. any string value wrapped in ENC() is to be treated as
+a masked password. For example
+
+`ENC(xyz)`
+
+The above indicates that the password is masked and the masked value is `xyz`.
+
+The ENC() syntax is the preferred way to indicating a masked password and is
+universally supported in every password configuration in Artemis.
+
+The other way is to use a `mask-password` attribute to tell that a password
+in a configuration file should be treated as 'masked'. For example:
+
+```
+<mask-password>true</mask-password>
+<cluster-password>xyz</cluster-password>
+```
+This method is now deprecated and exists only to maintain backward-compatibility.
+Newer configurations may not support it.
+
### Password Masking in Server Configuration File
#### General Masking Configuration
-The server configuration file (i.e. broker.xml )has a property that defines the
-default masking behaviors over the entire file scope.
+Besides supporting the ENC() syntax, the server configuration file (i.e. broker.xml)
+has a property that defines the default masking behaviors over the entire file scope.
`mask-password`: this boolean type property indicates if a password
should be masked or not. Set it to "true" if you want your passwords
@@ -38,6 +59,8 @@ will be used.
##### cluster-password
+If it is specified in ENC() syntax it will be treated as masked, or
+
If `mask-password` is `true` the `cluster-password` will be treated as masked.
##### Passwords in connectors and acceptors
@@ -55,16 +78,16 @@ and `activemq.passwordcodec` respectively. The Netty and InVM implementations
will use these as needed and any other implementations will have access to
these to use if they so wish.
+The preferred way, however, is to use the ENC() syntax.
+
##### Passwords in bridge configurations
Core Bridges are configured in the server configuration file and so the
masking of its `password` properties follows the same rules as that of
-`cluster-password`.
-
-#### Examples
+`cluster-password`. It supports ENC() syntax.
-The following table summarizes the relations among the above-mentioned
-properties
+For using 'mask-password' property, the following table summarizes the
+relations among the above-mentioned properties
mask-password | cluster-password | acceptor/connector passwords | bridge password
:------------- | :---------------- | :--------------------------- | :---------------
@@ -72,7 +95,9 @@ properties
false | plain text | plain text | plain text
true | masked | masked | masked
-Examples
+It is recommended that you use the ENC() syntax for new applications/deployments.
+
+#### Examples
Note: In the following examples if related attributed or properties are
absent, it means they are not specified in the configure file.
@@ -88,6 +113,14 @@ This indicates the cluster password is a plain text value ("bbc").
example 2
```xml
+<cluster-password>ENC(xyz)</cluster-password>
+```
+
+This indicates the cluster password is a masked value ("xyz").
+
+example 3
+
+```xml
<mask-password>true</mask-password>
<cluster-password>80cf731af62c290</cluster-password>
```
@@ -97,21 +130,64 @@ use its built-in decoder to decode it. All other passwords in the
configuration file, Connectors, Acceptors and Bridges, will also use
masked passwords.
+#### Passwords in bootstrap.xml
+
+The broker embeds a web-server for hosting some web applications such as a
+management console. It is configured in bootstrap.xml as a web
+component. The web server can be secured using https protocol, and it can be
+configured with a keystore password and/or truststore password which by
+default are specified in plain text forms.
+
+To mask these passwords you need to use ENC() syntax. The `mask-password` is
+not supported here.
+
+You can also set the `passwordCodec` attribute if you want to use a password codec
+other than the default one. For example
+
+```xml
+ <web bind="https://localhost:8443" path="web"
+ keyStorePassword="ENC(-5a2376c61c668aaf)"
+ trustStorePassword="ENC(3d617352d12839eb71208edf41d66b34)">
+ <app url="activemq-branding" war="activemq-branding.war"/>
+ </web>
+```
+
### Masking passwords in ActiveMQ Artemis JCA ResourceAdapter and MDB activation configurations
Both ra.xml and MDB activation configuration have a `password` property
-that can be masked. They are controlled by the following two optional
-Resource Adapter properties in ra.xml:
+that can be masked preferably using ENC() syntax.
+
+Alternatively it can use a optional attribute in ra.xml to indicate that a password
+is masked:
`UseMaskedPassword` -- If setting to "true" the passwords are masked.
Default is false.
+There is another property in ra.xml that can specify a codec:
+
`PasswordCodec` -- Class name and its parameters for the Decoder used to
decode the masked password. Ignored if UseMaskedPassword is false. The
format of this property is a full qualified class name optionally
followed by key/value pairs. It is the same format as that for JMS
Bridges. Example:
+Example 1 Using the ENC() syntax:
+
+```xml
+<config-property>
+ <config-property-name>password</config-property-name>
+ <config-property-type>String</config-property-type>
+ <config-property-value>ENC(xyz)</config-property-value>
+</config-property>
+<config-property>
+ <config-property-name>PasswordCodec</config-property-name>
+ <config-property-type>java.lang.String</config-property-type>
+ <config-property-value>com.foo.ADecoder;key=helloworld</config-property-value>
+</config-property>
+```
+
+Example 2 Using the "UseMaskedPassword" property:
+
```xml
<config-property>
<config-property-name>UseMaskedPassword</config-property-name>
@@ -119,6 +195,11 @@ Bridges. Example:
<config-property-value>true</config-property-value>
</config-property>
<config-property>
+ <config-property-name>password</config-property-name>
+ <config-property-type>String</config-property-type>
+ <config-property-value>xyz</config-property-value>
+</config-property>
+<config-property>
<config-property-name>PasswordCodec</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>com.foo.ADecoder;key=helloworld</config-property-value>
@@ -150,6 +231,43 @@ Passwords in `artemis-users.properties` are automatically detected as hashed or
by looking for the syntax `ENC(<hash>)`. The `mask-password` parameter does not need
to be `true` to use hashed passwords here.
+### Masking password in JAAS login config file (login.config)
+
+Artemis supports LDAP login modules to be configured in JAAS configuration
+file (default name is `login.config`). When connecting to a LDAP server usually
+you need to supply a connection password in the config file. By default this
+password is in plain text form.
+
+To mask it you need to configure the passwords in your login module
+using ENC() syntax. To specify a codec using the following property:
+
+`passwordCodec` - the password codec class name. (the default codec
+will be used if it is absent)
+
+For example:
+
+```
+LDAPLoginExternalPasswordCodec {
+ org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required
+ debug=true
+ initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+ connectionURL="ldap://localhost:1024"
+ connectionUsername="uid=admin,ou=system"
+ connectionPassword="ENC(-170b9ef34d79ed12)"
+ passwordCodec="org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=helloworld"
+ connectionProtocol=s
+ authentication=simple
+ userBase="ou=system"
+ userSearchMatching="(uid={0})"
+ userSearchSubtree=false
+ roleBase="ou=system"
+ roleName=dummyRoleName
+ roleSearchMatching="(uid={1})"
+ roleSearchSubtree=false
+ ;
+};
+```
+
### Choosing a decoder for password masking
As described in the previous sections, all password masking requires a
@@ -206,8 +324,7 @@ pairs when configuring. For instance if your decoder needs say a
Then configure your cluster-password like this:
```xml
- <mask-password>true</mask-password>
- <cluster-password>masked_password</cluster-password>
+ <cluster-password>ENC(masked_password)</cluster-password>
```
When Apache ActiveMQ Artemis reads the cluster-password it will initialize the
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java
----------------------------------------------------------------------
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java
index c7a1474..811cc29 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ResourceAdapterTest.java
@@ -41,6 +41,7 @@ import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConf
import org.apache.activemq.artemis.tests.unit.ra.BootstrapContext;
import org.apache.activemq.artemis.tests.unit.ra.MessageEndpointFactory;
import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
import org.junit.Test;
public class ResourceAdapterTest extends ActiveMQRATestBase {
@@ -565,7 +566,7 @@ public class ResourceAdapterTest extends ActiveMQRATestBase {
ActiveMQRATestBase.MyBootstrapContext ctx = new ActiveMQRATestBase.MyBootstrapContext();
DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec();
- String mask = (String) codec.encode("helloworld");
+ String mask = codec.encode("helloworld");
qResourceAdapter.setUseMaskedPassword(true);
qResourceAdapter.setPassword(mask);
@@ -595,6 +596,41 @@ public class ResourceAdapterTest extends ActiveMQRATestBase {
}
@Test
+ public void testMaskPasswordENC() throws Exception {
+ ActiveMQResourceAdapter qResourceAdapter = new ActiveMQResourceAdapter();
+ qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
+ ActiveMQRATestBase.MyBootstrapContext ctx = new ActiveMQRATestBase.MyBootstrapContext();
+
+ DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec();
+ String mask = codec.encode("helloworld");
+
+ qResourceAdapter.setPassword(PasswordMaskingUtil.wrap(mask));
+
+ qResourceAdapter.start(ctx);
+
+ assertEquals("helloworld", qResourceAdapter.getPassword());
+
+ ActiveMQActivationSpec spec = new ActiveMQActivationSpec();
+ spec.setResourceAdapter(qResourceAdapter);
+ spec.setUseJNDI(false);
+ spec.setDestinationType("javax.jms.Queue");
+ spec.setDestination(MDBQUEUE);
+
+ mask = codec.encode("mdbpassword");
+ spec.setPassword(PasswordMaskingUtil.wrap(mask));
+ qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
+ CountDownLatch latch = new CountDownLatch(1);
+ DummyMessageEndpoint endpoint = new DummyMessageEndpoint(latch);
+ DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, false);
+ qResourceAdapter.endpointActivation(endpointFactory, spec);
+
+ assertEquals("mdbpassword", spec.getPassword());
+
+ qResourceAdapter.stop();
+ assertTrue(endpoint.released);
+ }
+
+ @Test
public void testMaskPassword2() throws Exception {
ActiveMQResourceAdapter qResourceAdapter = new ActiveMQResourceAdapter();
qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
@@ -609,7 +645,7 @@ public class ResourceAdapterTest extends ActiveMQRATestBase {
prop.put("key", "anotherkey");
codec.init(prop);
- String mask = (String) codec.encode("helloworld");
+ String mask = codec.encode("helloworld");
qResourceAdapter.setPassword(mask);
@@ -623,7 +659,7 @@ public class ResourceAdapterTest extends ActiveMQRATestBase {
spec.setDestinationType("javax.jms.Queue");
spec.setDestination(MDBQUEUE);
- mask = (String) codec.encode("mdbpassword");
+ mask = codec.encode("mdbpassword");
spec.setPassword(mask);
qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
CountDownLatch latch = new CountDownLatch(1);
@@ -638,6 +674,48 @@ public class ResourceAdapterTest extends ActiveMQRATestBase {
}
@Test
+ public void testMaskPassword2ENC() throws Exception {
+ ActiveMQResourceAdapter qResourceAdapter = new ActiveMQResourceAdapter();
+ qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
+ ActiveMQRATestBase.MyBootstrapContext ctx = new ActiveMQRATestBase.MyBootstrapContext();
+
+ qResourceAdapter.setPasswordCodec(DefaultSensitiveStringCodec.class.getName() + ";key=anotherkey");
+
+ DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec();
+ Map<String, String> prop = new HashMap<>();
+
+ prop.put("key", "anotherkey");
+ codec.init(prop);
+
+ String mask = codec.encode("helloworld");
+
+ qResourceAdapter.setPassword(PasswordMaskingUtil.wrap(mask));
+
+ qResourceAdapter.start(ctx);
+
+ assertEquals("helloworld", qResourceAdapter.getPassword());
+
+ ActiveMQActivationSpec spec = new ActiveMQActivationSpec();
+ spec.setResourceAdapter(qResourceAdapter);
+ spec.setUseJNDI(false);
+ spec.setDestinationType("javax.jms.Queue");
+ spec.setDestination(MDBQUEUE);
+
+ mask = codec.encode("mdbpassword");
+ spec.setPassword(PasswordMaskingUtil.wrap(mask));
+ qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY);
+ CountDownLatch latch = new CountDownLatch(1);
+ DummyMessageEndpoint endpoint = new DummyMessageEndpoint(latch);
+ DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, false);
+ qResourceAdapter.endpointActivation(endpointFactory, spec);
+
+ assertEquals("mdbpassword", spec.getPassword());
+
+ qResourceAdapter.stop();
+ assertTrue(endpoint.released);
+ }
+
+ @Test
public void testConnectionParameterStringParsing() throws Exception {
ActiveMQResourceAdapter resourceAdapter = new ActiveMQResourceAdapter();
resourceAdapter.setConnectionParameters("enabledProtocols=TLS1\\,TLS1.2;sslEnabled=true");
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java
----------------------------------------------------------------------
diff --git a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java
index 9007bb4..f6a08a0 100644
--- a/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java
+++ b/tests/timing-tests/src/test/java/org/apache/activemq/artemis/tests/timing/jms/bridge/impl/JMSBridgeImplTest.java
@@ -288,6 +288,45 @@ public class JMSBridgeImplTest extends ActiveMQTestBase {
* expires even if the maxBatchSize is not reached
*/
@Test
+ public void testBridgeWithMaskPasswords() throws Exception {
+
+ ConnectionFactoryFactory sourceCFF = JMSBridgeImplTest.newConnectionFactoryFactory(JMSBridgeImplTest.createConnectionFactory());
+ ConnectionFactoryFactory targetCFF = JMSBridgeImplTest.newConnectionFactoryFactory(JMSBridgeImplTest.createConnectionFactory());
+ DestinationFactory sourceDF = JMSBridgeImplTest.newDestinationFactory(ActiveMQJMSClient.createQueue(JMSBridgeImplTest.SOURCE));
+ DestinationFactory targetDF = JMSBridgeImplTest.newDestinationFactory(ActiveMQJMSClient.createQueue(JMSBridgeImplTest.TARGET));
+ TransactionManager tm = JMSBridgeImplTest.newTransactionManager();
+
+ JMSBridgeImpl bridge = new JMSBridgeImpl();
+ Assert.assertNotNull(bridge);
+
+ bridge.setSourceConnectionFactoryFactory(sourceCFF);
+ bridge.setSourceDestinationFactory(sourceDF);
+ bridge.setTargetConnectionFactoryFactory(targetCFF);
+ bridge.setTargetDestinationFactory(targetDF);
+ bridge.setFailureRetryInterval(10);
+ bridge.setMaxRetries(1);
+ bridge.setMaxBatchSize(1);
+ bridge.setMaxBatchTime(-1);
+ bridge.setTransactionManager(tm);
+ bridge.setQualityOfServiceMode(QualityOfServiceMode.AT_MOST_ONCE);
+
+ bridge.setSourceUsername("sourceuser");
+ bridge.setSourcePassword("ENC(5493dd76567ee5ec269d11823973462f)");
+ bridge.setTargetUsername("targetuser");
+ bridge.setTargetPassword("ENC(56a0db3b71043054269d11823973462f)");
+
+ Assert.assertFalse(bridge.isStarted());
+ bridge.start();
+ Assert.assertTrue(bridge.isStarted());
+
+ assertEquals("sourcepassword", bridge.getSourcePassword());
+ assertEquals("targetpassword", bridge.getTargetPassword());
+
+ bridge.stop();
+ Assert.assertFalse(bridge.isStarted());
+ }
+
+ @Test
public void testSendMessagesWhenMaxBatchTimeExpires() throws Exception {
int maxBatchSize = 2;
long maxBatchTime = 500;