diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java index 3fe2ea1099..dbc20c54c0 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java @@ -54,7 +54,7 @@ public final class PasswordMaskingUtil { static { HashProcessor processor = null; Exception exception = null; - final String codecDesc = "org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;algorithm=one-way"; + final String codecDesc = new StringBuilder().append(DefaultSensitiveStringCodec.class.getName()).append(";").append(DefaultSensitiveStringCodec.ALGORITHM).append("=").append(DefaultSensitiveStringCodec.ONE_WAY).toString(); try { final DefaultSensitiveStringCodec codec = (DefaultSensitiveStringCodec) PasswordMaskingUtil.getCodec(codecDesc); processor = new SecureHashProcessor(codec); @@ -83,7 +83,7 @@ public final class PasswordMaskingUtil { } private static boolean isEncoded(String storedPassword) { - return storedPassword == null || (storedPassword.startsWith("ENC(") && storedPassword.endsWith(")")); + return storedPassword == null || (storedPassword.startsWith(SecureHashProcessor.BEGIN_HASH) && storedPassword.endsWith(SecureHashProcessor.END_HASH)); } public static HashProcessor getHashProcessor() { diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java index 81db051471..8e9cd5c0f9 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java @@ -16,13 +16,10 @@ */ package org.apache.activemq.artemis.utils; -/** - * Hash function - */ public class SecureHashProcessor implements HashProcessor { - private static final String BEGIN_HASH = "ENC("; - private static final String END_HASH = ")"; + public static final String BEGIN_HASH = "ENC("; + public static final String END_HASH = ")"; private DefaultSensitiveStringCodec codec; diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index 68facdc186..1a2f5311f6 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -39,6 +39,7 @@ * [Management](management.md) * [Management Console](management-console.md) * [Security](security.md) +* [Masking Passwords](masking-passwords.md) * [Broker Plugins](broker-plugins.md) * [Resource Limits](resource-limits.md) * [The JMS Bridge](jms-bridge.md) diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index 5311515a64..05e062eaff 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -84,7 +84,7 @@ Name | Description [large-messages-directory](large-messages.md "Configuring the server") | the directory to store large messages. Default=data/largemessages [management-address](management.md "Configuring Core Management") | the name of the management address to send management messages to. Default=activemq.management [management-notification-address](management.md "Configuring The Core Management Notification Address") | the name of the address that consumers bind to receive management notifications. Default=activemq.notifications -[mask-password](configuration-index.md "Using Masked Passwords in Configuration Files") | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. Default=false +[mask-password](masking-passwords.md "Masking Passwords") | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. Default=false [max-saved-replicated-journals-size](ha.md#data-replication) | This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. -1 Means no Limit, 0 don't keep a copy at all, Default=2 [max-disk-usage](paging.md#max-disk-usage) | The max percentage of data we should use from disks. The System will block while the disk is full. Default=100 [memory-measure-interval](perf-tuning.md) | frequency to sample JVM memory in ms (or -1 to disable memory sampling). Default=-1 @@ -94,6 +94,7 @@ Name | Description [message-counter-sample-period](management.md "Configuring Message Counters") | the sample period (in ms) to use for message counters. Default=10000 [message-expiry-scan-period](message-expiry.md "Configuring The Expiry Reaper Thread") | how often (in ms) to scan for expired messages. Default=30000 [message-expiry-thread-priority](message-expiry.md "Configuring The Expiry Reaper Thread") | the priority of the thread expiring messages. Default=3 +[password-codec](masking-passwords.md "Masking Passwords") | the name of the class (and optional configuration properties) used to decode masked passwords. Only valid when `mask-password` is `true`. Default=empty [page-max-concurrent-io](paging.md "Paging Mode") | The max number of concurrent reads allowed on paging. Default=5 [paging-directory](paging.md "Configuration") | the directory to store paged messages in. Default=data/paging [persist-delivery-count-before-delivery](undelivered-messages.md "Delivery Count Persistence") | True means that the delivery count is persisted before delivery. False means that this only happens after a message has been cancelled. Default=false @@ -255,271 +256,3 @@ Name | Description [permission.type ](security.md "Role based security for addresses") | the type of permission [permission.roles ](security.md "Role based security for addresses") | a comma-separated list of roles to apply the permission to - - -Using Masked Passwords in Configuration Files ---------------------------------------------- - -By default all passwords in Apache ActiveMQ Artemis server's configuration files are in -plain text form. This usually poses no security issues as those files -should be well protected from unauthorized accessing. However, in some -circumstances a user doesn't want to expose its passwords to more eyes -than necessary. - -Apache ActiveMQ Artemis can be configured to use 'masked' passwords in its -configuration files. A masked password is an obscure string -representation of a real password. To mask a password a user will use an -'encoder'. The encoder takes in the real password and outputs the masked -version. A user can then replace the real password in the configuration -files with the new masked password. When Apache ActiveMQ Artemis loads a masked -password, it uses a suitable 'decoder' to decode it into real password. - -Apache ActiveMQ Artemis provides a default password encoder and decoder. Optionally -users can use or implement their own encoder and decoder for masking the -passwords. - -### Password Masking in Server Configuration File - -#### The password masking property - -The server configuration file 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 -masked. The default value is "false". - -#### Specific masking behaviors - -##### cluster-password - -The nature of the value of cluster-password is subject to the value of -property 'mask-password'. If it is true the cluster-password is masked. - -##### Passwords in connectors and acceptors - -In the server configuration, Connectors and Acceptors sometimes needs to -specify passwords. For example if a users wants to use an SSL-enabled -NettyAcceptor, it can specify a key-store-password and a -trust-store-password. Because Acceptors and Connectors are pluggable -implementations, each transport will have different password masking -needs. - -When a Connector or Acceptor configuration is initialised, Apache ActiveMQ Artemis will -add the "mask-password" and "password-codec" values to the Connector or -Acceptors params using the keys `activemq.usemaskedpassword` 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. - -##### Passwords in Core 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 - -The following table summarizes the relations among the above-mentioned -properties - - mask-password | cluster-password | acceptor/connector passwords | bridge password - :------------- | :---------------- | :--------------------------- | :--------------- - absent | plain text | plain text | plain text - false | plain text | plain text | plain text - true | masked | masked | masked - -Examples - -Note: In the following examples if related attributed or properties are -absent, it means they are not specified in the configure file. - -example 1 - -```xml -bbc -``` - -This indicates the cluster password is a plain text value ("bbc"). - -example 2 - -```xml -true -80cf731af62c290 -``` - -This indicates the cluster password is a masked value and Apache ActiveMQ Artemis will -use its built-in decoder to decode it. All other passwords in the -configuration file, Connectors, Acceptors and Bridges, will also use -masked passwords. - -### JMS Bridge password masking - -The JMS Bridges are configured and deployed as separate beans so they -need separate configuration to control the password masking. A JMS -Bridge has two password parameters in its constructor, SourcePassword -and TargetPassword. It uses the following two optional properties to -control their masking: - -`useMaskedPassword` -- If set to "true" the passwords are masked. -Default is false. - -`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, separated by semi-colons. For example: - -```xml -true -com.foo.FooDecoder;key=value -``` - -Apache ActiveMQ Artemis will load this property and initialize the class with a -parameter map containing the "key"-\>"value" pair. If `passwordCodec` is -not specified, the built-in decoder is used. - -### Masking passwords in ActiveMQ Artemis ResourceAdapters 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: - -`UseMaskedPassword` -- If setting to "true" the passwords are masked. -Default is false. - -`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: - -```xml - - UseMaskedPassword - boolean - true - - - PasswordCodec - java.lang.String - com.foo.ADecoder;key=helloworld - -``` - -With this configuration, both passwords in ra.xml and all of its MDBs -will have to be in masked form. - -### Masking passwords in artemis-users.properties - -Apache ActiveMQ Artemis's built-in security manager uses plain properties files -where the user passwords are specified in hash forms by default. - -Please use Artemis CLI command to add a password. For example - -```sh - ./artemis user add --username guest --password guest --role admin -``` - -### Choosing a decoder for password masking - -As described in the previous sections, all password masking requires a -decoder. A decoder uses an algorithm to convert a masked password into -its original clear text form in order to be used in various security -operations. The algorithm used for decoding must match that for -encoding. Otherwise the decoding may not be successful. - -For user's convenience Apache ActiveMQ Artemis provides a default built-in Decoder. -However a user can if they so wish implement their own. - -#### The built-in Decoder - -Whenever no decoder is specified in the configuration file, the built-in -decoder is used. The class name for the built-in decoder is -org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec. It has both -encoding and decoding capabilities. It uses java.crypto.Cipher utilities -to encrypt (encode) a plaintext password and decrypt a mask string using -same algorithm. Using this decoder/encoder is pretty straightforward. To -get a mask for a password, just run the main class at org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec. - -An easy way to do it is through activemq-tools--jar-with-dependencies.jar since it has all the dependencies: - -```sh - java -cp artemis-tools-1.0.0-jar-with-dependencies.jar org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec "your plaintext password" -``` - -If you don't want to use the jar-with-dependencies, make sure the classpath is correct. You'll get something like - -``` - Encoded password: 80cf731af62c290 -``` - -Just copy "80cf731af62c290" and replace your plaintext password with it. - -#### Using a custom decoder - -It is possible to use a custom decoder rather than the built-in one. -Simply make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom decoder -can also be service loaded rather than class loaded, if the decoder's service provider is installed in the classpath. -Then configure the server to use it as follows: - -```xml - com.foo.SomeDecoder;key1=value1;key2=value2 -``` - -If your decoder needs params passed to it you can do this via key/value -pairs when configuring. For instance if your decoder needs say a -"key-location" parameter, you can define like so: - -```xml - com.foo.NewDecoder;key-location=/some/url/to/keyfile -``` - -Then configure your cluster-password like this: - -```xml - true - masked_password -``` - -When Apache ActiveMQ Artemis reads the cluster-password it will initialize the -NewDecoder and use it to decode "mask\_password". It also process all -passwords using the new defined decoder. - -#### Implementing your own codecs - -To use a different decoder than the built-in one, you either pick one -from existing libraries or you implement it yourself. All decoders must -implement the `org.apache.activemq.artemis.utils.SensitiveDataCodec` -interface: - -``` java -public interface SensitiveDataCodec -{ - T decode(Object mask) throws Exception; - - void init(Map params); -} -``` - -This is a generic type interface but normally for a password you just -need String type. So a new decoder would be defined like - -```java -public class MyNewDecoder implements SensitiveDataCodec -{ - public String decode(Object mask) throws Exception - { - //decode the mask into clear text password - return "the password"; - } - - public void init(Map params) - { - //initialization done here. It is called right after the decoder has been created. - } -} -``` - -Last but not least, once you get your own decoder, please add it to the -classpath. Otherwise Apache ActiveMQ Artemis will fail to load it! diff --git a/docs/user-manual/en/masking-passwords.md b/docs/user-manual/en/masking-passwords.md new file mode 100644 index 0000000000..4f435247f0 --- /dev/null +++ b/docs/user-manual/en/masking-passwords.md @@ -0,0 +1,254 @@ +# Masking Passwords + +By default all passwords in Apache ActiveMQ Artemis server's configuration files are in +plain text form. This usually poses no security issues as those files +should be well protected from unauthorized accessing. However, in some +circumstances a user doesn't want to expose its passwords to more eyes +than necessary. + +Apache ActiveMQ Artemis can be configured to use 'masked' passwords in its +configuration files. A masked password is an obscure string +representation of a real password. To mask a password a user will use an +'encoder'. The encoder takes in the real password and outputs the masked +version. A user can then replace the real password in the configuration +files with the new masked password. When Apache ActiveMQ Artemis loads a masked +password, it uses a suitable 'decoder' to decode it into real password. + +Apache ActiveMQ Artemis provides a default password encoder and decoder. Optionally +users can use or implement their own encoder and decoder for masking the +passwords. + +### 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. + +`mask-password`: this boolean type property indicates if a password +should be masked or not. Set it to "true" if you want your passwords +masked. The default value is "false". + +`password-codec`: this string type property identifies the name of the class +which will be used to decode the masked password within the broker. If not +specified then the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` +will be used. + +#### Specific Masking Behaviors + +##### cluster-password + +If `mask-password` is `true` the `cluster-password` will be treated as masked. + +##### Passwords in connectors and acceptors + +In broker.xml `connector` and `acceptor` configurations sometimes needs to +specify passwords. For example, if a user wants to use an `acceptor` with +`sslEnabled=true` it can specify `keyStorePassword` and `trustStorePassword`. +Because Acceptors and Connectors are pluggable implementations, each transport +will have different password masking needs. + +When a `connector` or `acceptor` is initialised, Apache ActiveMQ Artemis will +add the aforementioned `mask-password` and `password-codec` values to the +`connector` or `acceptor` parameters using the keys `activemq.usemaskedpassword` +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. + +##### 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 + +The following table summarizes the relations among the above-mentioned +properties + + mask-password | cluster-password | acceptor/connector passwords | bridge password + :------------- | :---------------- | :--------------------------- | :--------------- + absent | plain text | plain text | plain text + false | plain text | plain text | plain text + true | masked | masked | masked + +Examples + +Note: In the following examples if related attributed or properties are +absent, it means they are not specified in the configure file. + +example 1 + +```xml +bbc +``` + +This indicates the cluster password is a plain text value ("bbc"). + +example 2 + +```xml +true +80cf731af62c290 +``` + +This indicates the cluster password is a masked value and Apache ActiveMQ Artemis will +use its built-in decoder to decode it. All other passwords in the +configuration file, Connectors, Acceptors and Bridges, will also use +masked passwords. + +### 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: + +`UseMaskedPassword` -- If setting to "true" the passwords are masked. +Default is false. + +`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: + +```xml + + UseMaskedPassword + boolean + true + + + PasswordCodec + java.lang.String + com.foo.ADecoder;key=helloworld + +``` + +With this configuration, both passwords in ra.xml and all of its MDBs +will have to be in masked form. + +### Masking passwords in artemis-users.properties + +Apache ActiveMQ Artemis's built-in security manager uses plain properties files +where the user passwords are specified in a hashed form by default. Note, the passwords +are technically *hashed* rather than masked in this context. The default `PropertiesLoginModule` +will not decode the passwords in `artemis-users.properties` but will instead hash the input +and compare the two hashed values for password verification. + +Please use Artemis CLI command to add a password. For example: + +```sh + ./artemis user add --username guest --password guest --role admin +``` + +This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` +to perform a "one-way" hash of the password and alter both the `artemis-users.properties` +and `artemis-roles.properties` files with the specified values. + +Passwords in `artemis-users.properties` are automatically detected as hashed or not +by looking for the syntax `ENC()`. The `mask-password` parameter does not need +to be `true` to use hashed passwords here. + +### Choosing a decoder for password masking + +As described in the previous sections, all password masking requires a +decoder. A decoder uses an algorithm to convert a masked password into +its original clear text form in order to be used in various security +operations. The algorithm used for decoding must match that for +encoding. Otherwise the decoding may not be successful. + +For user's convenience Apache ActiveMQ Artemis provides a default decoder. +However a user can implement their own if they wish. + +#### The Default Decoder + +Whenever no decoder is specified in the configuration file, the default +decoder is used. The class name for the default decoder is +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec`. It has hashing, +encoding, and decoding capabilities. It uses `java.crypto.Cipher` utilities +to hash or encode a plaintext password and also to decode a masked string using +same algorithm and key. Using this decoder/encoder is pretty straightforward. To +get a mask for a password, just run the `mask` command: + +```sh +./artemis mask +``` + +You'll get something like + +``` +result: 32c6f67dae6cd61b0a7ad1702033aa81e6b2a760123f4360 +``` + +Just copy `32c6f67dae6cd61b0a7ad1702033aa81e6b2a760123f4360` and replace your +plaintext password in broker.xml with it. + +#### Using a custom decoder + +It is possible to use a custom decoder rather than the built-in one. +Simply make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom decoder +can also be service loaded rather than class loaded, if the decoder's service provider is installed in the classpath. +Then configure the server to use it as follows: + +```xml + com.foo.SomeDecoder;key1=value1;key2=value2 +``` + +If your decoder needs params passed to it you can do this via key/value +pairs when configuring. For instance if your decoder needs say a +"key-location" parameter, you can define like so: + +```xml + com.foo.NewDecoder;key-location=/some/url/to/keyfile +``` + +Then configure your cluster-password like this: + +```xml + true + masked_password +``` + +When Apache ActiveMQ Artemis reads the cluster-password it will initialize the +NewDecoder and use it to decode "mask\_password". It also process all +passwords using the new defined decoder. + +#### Implementing your own codecs + +To use a different decoder than the built-in one, you either pick one +from existing libraries or you implement it yourself. All decoders must +implement the `org.apache.activemq.artemis.utils.SensitiveDataCodec` +interface: + +``` java +public interface SensitiveDataCodec +{ + T decode(Object mask) throws Exception; + + void init(Map params); +} +``` + +This is a generic type interface but normally for a password you just +need String type. So a new decoder would be defined like + +```java +public class MyNewDecoder implements SensitiveDataCodec +{ + public String decode(Object mask) throws Exception + { + //decode the mask into clear text password + return "the password"; + } + + public void init(Map params) + { + //initialization done here. It is called right after the decoder has been created. + } +} +``` + +Last but not least, once you get your own decoder, please add it to the +classpath by packaging it in a JAR file and putting the JAR file in the `lib` +directory. Otherwise Apache ActiveMQ Artemis will fail to load it! \ No newline at end of file diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index ef22e1faba..91a3d3304e 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -427,6 +427,17 @@ the following: user=password guest=password +Passwords in `artemis-users.properties` can be hashed. Such passwords should follow the syntax `ENC()`. Hashed +passwords can easily be added to `artemis-users.properties` using the `user` CLI command, e.g.: + + +```sh + ./artemis user add --username guest --password guest --role admin +``` + +This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a "one-way" hash of +the password and alter both the `artemis-users.properties` and `artemis-roles.properties` files with the specified values. + The `artemis-roles.properties` file consists of a list of properties of the form, `Role=UserList`, where UserList is a comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file like the following: @@ -821,3 +832,7 @@ You need to put the black/white lists in its web.xml, as context parameters, as The param-value for each list is a comma separated string value representing the list. + +## Masking Passwords + +For details about masking passwords in broker.xml please see the [Masking Passwords](masking-passwords.md) chapter.