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;