ActiveMQ 5.15.9 Security - java

I'm currently using ActiveMQ 5.15.9 deployed on our Test Server.
I have to implement some sort of security so that the Queues inside won't be access by anyone.
So far what I've done is add the following to the activemq.xml:
<plugins>
<simpleAuthenticationPlugin anonymousAccessAllowed ="false">
<users>
<authenticationUser
username="admin"
password="pass"
groups="admins,publishers,consumers" />
</users>
</simpleAuthenticationPlugin>
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry topic =">" write="producers" read="consumers" admin="admins" />
<authorizationEntry queue ="TEST.Q" write="producers" read="consumers" admin="admins" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
On my C# this is how a access the Queue:
private static void SendNewMessageQueue(string text)
{
string queueName = "TEST";
Console.WriteLine($"Adding message to queue topic: {queueName}");
string brokerUri = $"activemq:tcp://localhost:61616"; // Default port
NMSConnectionFactory factory = new NMSConnectionFactory(brokerUri);
using (IConnection connection = factory.CreateConnection("admin","pass"))
{
connection.Start();
using (ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge))
using (IDestination dest = session.GetQueue(queueName))
using (IMessageProducer producer = session.CreateProducer(dest))
{
producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
producer.Send(session.CreateTextMessage(text));
Console.WriteLine($"Sent {text} messages");
}
}
}
But when I try and Run my Code I get the Following Error:
User admin is not authorized to write to: queue://TEST
I need this so I can expose this MQ to the Internet and have this secured by only allowing consumers/publishers who has the credentials.

You have configured security for a Queue called TEST.Q but you are trying to use a queue called TEST which is not the same so you are getting this error. If you want to expose all queues under the test prefix then it'd look more like:
<authorizationEntry queue ="TEST.>" write="producers" read="consumers" admin="admins" />
There are some docs for security configuration here, and also understanding the wildcard syntax will help.
Exposing a broker over the internet as you've mentioned is no small task so proceed with caution.

Related

java.lang.reflect.InvocationTargetException - RabbitMQ guest login deletion implication in SpringBoot Application [duplicate]

I am getting below exception
org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
Configuration: RabbitMQ 3.3.5 on windows
On Config file in %APPDATA%\RabbitMQ\rabbit.config
I have done below change as per https://www.rabbitmq.com/access-control.html
[{rabbit, [{loopback_users, []}]}].
I also tried creating a user/pwd - test/test doesn't seem to make it work.
Tried the Steps from this post.
Other Configuration Details are as below:
Tomcat hosted Spring Application Context:
<!-- Rabbit MQ configuration Start -->
<!-- Connection Factory -->
<rabbit:connection-factory id="rabbitConnFactory" virtual-host="/" username="guest" password="guest" port="5672"/>
<!-- Spring AMQP Template -->
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnFactory" routing-key="ecl.down.queue" queue="ecl.down.queue" />
<!-- Spring AMQP Admin -->
<rabbit:admin id="admin" connection-factory="rabbitConnFactory"/>
<rabbit:queue id="ecl.down.queue" name="ecl.down.queue" />
<rabbit:direct-exchange name="ecl.down.exchange">
<rabbit:bindings>
<rabbit:binding key="ecl.down.key" queue="ecl.down.queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
In my Controller Class
#Autowired
RmqMessageSender rmqMessageSender;
//Inside a method
rmqMessageSender.submitToECLDown(orderInSession.getOrderNo());
In My Message sender:
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component("messageSender")
public class RmqMessageSender {
#Autowired
AmqpTemplate rabbitTemplate;
public void submitToRMQ(String orderId){
try{
rabbitTemplate.convertAndSend("Hello World");
} catch (Exception e){
LOGGER.error(e.getMessage());
}
}
}
Above exception Block gives below Exception
org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
Error Log
=ERROR REPORT==== 7-Nov-2014::18:04:37 ===
closing AMQP connection <0.489.0> (10.1.XX.2XX:52298 -> 10.1.XX.2XX:5672):
{handshake_error,starting,0,
{amqp_error,access_refused,
"PLAIN login refused: user 'guest' can only connect via localhost",
'connection.start_ok'}}
Pls find below the pom.xml entry
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
Please let me know if you have any thoughts/suggestions
I am sure what Artem Bilan has explained here might be one of the reasons for this error:
Caused by: com.rabbitmq.client.AuthenticationFailureException:
ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN.
For details see the
but the solution for me was that I logged in to rabbitMQ admin page (http://localhost:15672/#/users) with the default user name and password which is guest/guest then added a new user and for that new user I enabled the permission to access it from virtual host and then used the new user name and password instead of default guest and that cleared the error.
To complete #cpu-100 answer,
in case you don't want to enable/use web interface, you can create a new credentials using command line like below and use it in your code to connect to RabbitMQ.
$ rabbitmqctl add_user YOUR_USERNAME YOUR_PASSWORD
$ rabbitmqctl set_user_tags YOUR_USERNAME administrator
$ rabbitmqctl set_permissions -p / YOUR_USERNAME ".*" ".*" ".*"
user 'guest' can only connect via localhost
That's true since RabbitMQ 3.3.x. Hence you should upgrade to the same version the client library, or just upgrade Spring AMQP to the latest version (if you use dependency managent system).
Previous version of client used 127.0.0.1 as default value for the host option of ConnectionFactory.
The error
ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
can occur if the credentials that your application is trying to use to connect to RabbitMQ are incorrect or missing.
I had this happen when the RabbitMQ credentials stored in my ASP.NET application's web.config file had a value of "" for the password instead of the actual password string value.
To allow guest access remotely, write this
[{rabbit, [{loopback_users, []}]}].
to here
c:\Users\[your user name]\AppData\Roaming\RabbitMQ\rabbitmq.config
then restart the rabbitmq windows service (Source https://www.rabbitmq.com/access-control.html)
On localhost , By default use 'amqp://guest:guest#localhost:5672'
So on a remote or hosted RabbitMQ. Let's say you have the following credentials
username: niceboy
password: notnice
host: goxha.com
port : 1597
then the uri you should pass will be
amqp://niceboy:notnice#goxha.com:1597
following the template amqp://user:pass#host:10000
if you have a vhost you can do amqp://user:pass#host:10000/vhost where the trailing vhost will be the name of your vhost
New solution:
The node module can't handle : in a password properly. Even url encoded, like it would work normally, it does not work.
Don't use typicalspecial characters from an URL in the password!
Like one of the following: : . ? + %
Original, wrong answer:
The error message clearly complains about using PLAIN, it does not mean the crendentials are wrong, it means you must use encrypted data delivery (TLS) instead of plaintext.
Changing amqp:// in the connection string to amqps:// (note the s) solves this.
just add login password to connect to RabbitMq
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("rabbit_host");
connectionFactory.setUsername("login");
connectionFactory.setPassword("password");
For me the solution was simple: the user name is case sensitive. Failing to use the correct caps will also lead to the error.
if you use the number as your password, maybe you should try to change your password using string.
I can login using deltaqin:000000 on the website, but had this while running the program. then change the password to deltaiqn. and it works.
I made exactly what #grepit made.
But I had to made some changes in my Java code:
In Producer and Receiver project I altered:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("your-host-ip");
factory.setUsername("username-you-created");
factory.setPassword("username-password");
Doing that, you are connecting an specific host as the user you have created.
It works for me!
In my case I had this error, cuz of wrongly set password (I tried to use 5672, when the actual one in my system was 5676).
Maybe this will help someone to double check ports...
I was facing this issue due to empty space at the end of the password(spring.rabbitmq.password=rabbit ) in spring boot application.properties got resolved on removing the empty space. Hope this checklist helps some one facing this issue.
For C# coder, I tried below code and It worked, may be this can help someone so posting here.
scenario- RabbitMQ queue is running on another system in local area network but I was having same error.
by default there is a "guest" user exists. but you can not access remote server's queue (rabbitMq) using "guest" user so you need to create new user, Here I created "tester001" user to access data of remote server's queue.
ConnectionFactory factory = new ConnectionFactory();
factory.UserName = "tester001";
factory.Password = "testing";
factory.VirtualHost = "/";
factory.HostName = "192.168.1.101";
factory.Port = AmqpTcpEndpoint.UseDefaultPort;
If you tried all of these answers for your issue but you still got "ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN", maybe you should remove rabbitmq and install it with a newer version.
Newer version worked for me.
Add one user and pass and connect to them. You can add 1 user via env variables (e.g., useful when Rabbit initializes in a Docker): RABBITMQ_DEFAULT_USER and RABBITMQ_DEFAULT_PASS. See more details here:
https://stackoverflow.com/a/70676040/1200914
set ConnectionFactory or Connection hostname to localhost

Create a security level through authentification when connecting to a ActiveMQ destination

I use Apache Activemq version 5.14.4 to send some messages into a queue and to read them. I want to implement a security level so as when I connect to the destination to be asked to give a username and a password.
Reading the official documentation for ActiveMQ security and looking at a lot of examples including these: example1, example2, I choose to use the Simple Authentication Plugin to achieve this.
So, in my activemq.xml, inside broker element I wrote the plugin:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="Admin01" password="anything" groups="users,admins"/>
</users>
</simpleAuthenticationPlugin>
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue=">" write="producers" read="consumers" admin="admins" />
<authorizationEntry topic="ActiveMQ.Advisory.>" admin="admins" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
...
</broker>
Doing so, I expected to be asked about a username and a password in order to connect to a destination to consume the messages from the queue.
I create the connection and destination this way:
String username = "Admin01";
String password = "anithing"
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
factory.setTrustedPackages(Arrays.asList("myClass.package_name"));
Connection connection = null;
Session session = null;
Destination destination = null;
try {
connection = factory.createConnection(username, password);
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("myQueue");
...
The ActiveMQ broker is created and when the events that I want to sent occurs, they are sent to myQueue.
(I also tryed withowt setting any username and password to the factory with factory.setPassword() and factory.setUserName() or setting the username and password only to the connection, or both and there is no exception thrown when I don't set any username and password).
Well If I want to consume the messages from the queue I intentionally not set any password and there is no exception thrown and the messages are consumed. (I expected to be thrown an exception asking for the username and password. Also I tried with wrong username and password).
private void consumeMessage() {
String userName = "Admin01";
String password = "anything";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// factory.setUserName(userName);
// factory.setPassword(password);
factory.setTrustAllPackages(true);
Destination destination = null;
try {
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("myQueue");
consumer = session.createConsumer(destination);
consumer.setMessageListener(this);
connection.start();
}catch(JMSException e) {
e.printStackTrace();
}
}
I have overridden the credentials.properties file:
Default values:
activemq.username=system
activemq.password=manager
guest.password=password
Overridden:
activemq.username=Admin01
activemq.password=anything
I also overridden the files:
groups.properties file to admins=Admin01 and users.properties to admin=Admin01 and still no password asked.
All files mentioned above are in \apache-activemq-5.14.4\conf directory.
What can I do to implement this security level on ActiveMQ so as, when I want to connect to the destination in order to consume the messages from "myQueue" to be asked for the username and password?
By default
<simpleAuthenticationPlugin anonymousAccessAllowed="false"> deny anonymous access.

Spring Java Mail - How to know send mail be read, bounce or forward by receiver?

I'm new on spring and java mail, may i know how the email send by java mail is read, bounce or forward. I used google search only found the way how to send a mail. Anyone know where can get the reference on it or provide me some example on it?
Thank you.
Below is my code send mail with spring java mail:
Spring-Mail.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="xxxxxxxx#gmail.com" />
<property name="password" value="xxxxxx" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.from">xxxxxxxx#hotmail.com</prop>
</props>
</property>
</bean>
<bean id="mailMail" class="com.penril.my.MailMail">
<property name="mailSender" ref="mailSender" />
</bean>
</beans>
MailMail.java
public class MailMail
{
private JavaMailSender mailSender;
public void setMailSender(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void sendMail(String from, String to, String subject, String msg) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(msg);
mailSender.send(message);
} catch (MessagingException e) {
throw new MailParseException(e);
}
}
}
MainClass.java
public class MainClass
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext("Spring-Mail.xml");
MailMail mm = (MailMail) context.getBean("mailMail");
mm.sendMail("xxx123xxx#gmail.com",
"xxx234xxx#hotmail.co1m",
"Testing123",
"Testing only \n\n Hello Spring Email Sender");
}
}
There is no standard way of doing this that's accepted and honored across the board. I see that you have some options, though:
Add a header "Return-Receipt-To" with your e-mail address in the value. If the recipient of the e-mail has a client which honors this header, then a return receipt will be sent to you when the e-mail is opened. This is not reliable, mind you, as the user can always decide not to send the receipt, even if he has a client that supports it.
Add an image into your e-mail that loads from your server and put a parameter on the image that includes the user's e-mail address. When the e-mail loads, the image will load from your server. Write a script that collects the e-mail parameter and then delivers a blank image. This is also not reliable, however, as many mail clients prompt users if they wish to download images and they can always choose not to. Also, some (mostly older) e-mail clients do not support images.
Perhaps the most reliable way is not to include the message in your e-mail at all. Include only a link to a website where the message can be read, and include their e-mail address or a unique code in the link. This way, you know exactly who read the message. Of course, this has the downside that people aren't actually getting the message in their inbox, and they also may choose not to go to the website to read it.
Ultimately, I think you're going to have to come up with a creative solution to solve this problem, unless you're happy getting spotty results.
Sign up for a free account for a Cloud Based SMTP service, for example Sendgrid, which will save you the trouble of manually implementing what was suggested in the previous answer.
https://sendgrid.com/
You can send 400 emails a day on the free tier. You can manually check the status of individual messages 'opened', 'bounced' etc using the management console or there are various APIs available to do this programatically (although some are only available on paid tiers).
For example their WebHooks API will call back your server when an event (opened, bounced, click etc.) occurs:
https://sendgrid.com/docs/API_Reference/Webhooks/event.html

Adding attributes to Log4j 2 Events

I'd like to add some additional information/attributes like "application" name to my log4j2 Events (for some reason this attribute is not availible in log4j2 anymore..)
The Logs are sent over LAN to a Logstash instance.
I've worked out a solution including:
Custom Appender Layout (https://github.com/majikthys/log4j2-logstash-jsonevent-layout): the layout extracts all attributes from the Log4jLogEvent and the additional attributes that can be provided through the log4j2 configuration and produces a JSON String.
Logstash configuration:
input {
tcp {
codec => json_lines { charset => "UTF-8" }
port => 4560
type => "log4j2-json"
mode => "server"
}
}
...
The solution above works but requires the Layout to be built and added/maintained as a jar in every application.
So the question is - are there any better solutions that I've missed out?
Ideal would be a solution that wouldn't require adding any new jars/classes and uses of 3rd party software. Something like RewriteAppender but without use of "MapMessage".
You can use logstash over GELF with https://github.com/mp911de/logstash-gelf for your use case. Application name is more related to the client than to a central logstash server. Config looks like:
<Configuration>
<Appenders>
<Gelf name="gelf" host="udp:localhost" port="12201" originHost="%host{fqdn}">
<Field name="timestamp" pattern="%d{dd MMM yyyy HH:mm:ss,SSS}" />
<Field name="level" pattern="%level" />
<Field name="simpleClassName" pattern="%C{1}" />
<Field name="applicationName" literal="MyApplicationName" />
<!-- This is a field using MDC -->
<Field name="mdcField2" mdc="mdcField2" />
<DynamicMdcFields regex="mdc.*" />
</Gelf>
</Appenders>
...
</Configuration>
I ended up using a log4j 2 TcpSocketServer to receive remote logs and then forward them locally to logstash over a tcp socket (you could use a file too...).
http://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/net/server/TcpSocketServer.html
I don't think this solution is ideal, but it doesn't require the custom appender.

Spring JmsTemplate + Security

I've just refactored some code that published to a JMS topic to use Spring's JmsTemplate class and now I'm receiving an exception stating I'm not authenticated.
Previously I created the factory, made a connection, then session etc as follows:
MQTopicConnectionFactory factory = new MQTopicConnectionFactory();
factory.setQueueManager(qMgr);
factory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
factory.setHostName(hostname);
factory.setPort(listenerPort);
factory.setChannel(channel);
// setting username and password to be empty string ==> no authentication
connection = factory.createConnection("", "");
...
connection.start();
I don't see anywhere in the JmsTemplate to set the username and password to empty strings. My config looks like this:
<bean id="jmsFactory" class="com.ibm.mq.jms.MQTopicConnectionFactory">
<property name="queueManager">
<value>ACT01</value>
</property>
<property name="hostName">
<value>xx.xx.xx.xx</value>
</property>
<property name="port">
<value>15004</value>
</property>
<property name="transportType">
<value>1</value>
</property>
<property name="channel">
<value>CONDUCTOR.ACT01</value>
</property>
</bean>
<bean id="impactJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
</property>
</bean>
I have also tried wrapping the jmsFactory in a UserCredentialsConnectionFactoryAdapter object to no avail:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="jmsFactory"/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
Stack trace:
Caused by: com.ibm.msg.client.jms.DetailedJMSSecurityException: JMSWMQ2013: The security authentication was not valid that was supplied for QueueManager 'LOROL' with connection mode 'Client' and host name 'xx.xx.xx.xx'. Please check if the supplied username and password are correct on the QueueManager you are connecting to
at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:531)
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:219)
at com.ibm.msg.client.wmq.internal.WMQConnection.<init>(WMQConnection.java:410)
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createV7ProviderConnection(WMQConnectionFactory.java:7855)
at com.ibm.msg.client.wmq.factories.WMQConnectionFactory.createProviderConnection(WMQConnectionFactory.java:7331)
at com.ibm.msg.client.jms.admin.JmsConnectionFactoryImpl.createConnection(JmsConnectionFactoryImpl.java:276)
at com.ibm.mq.jms.MQConnectionFactory.createCommonConnection(MQConnectionFactory.java:6055)
at com.ibm.mq.jms.MQTopicConnectionFactory.createTopicConnection(MQTopicConnectionFactory.java:114)
at com.ibm.mq.jms.MQTopicConnectionFactory.createConnection(MQTopicConnectionFactory.java:197)
at org.springframework.jms.connection.SingleConnectionFactory.doCreateConnection(SingleConnectionFactory.java:343)
at org.springframework.jms.connection.SingleConnectionFactory.initConnection(SingleConnectionFactory.java:290)
at org.springframework.jms.connection.SingleConnectionFactory.createConnection(SingleConnectionFactory.java:227)
at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:184)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:461)
... 25 more
Caused by: com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2035' ('MQRC_NOT_AUTHORIZED').
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:206)
... 37 more
A few options here...
The properties you were setting before resulted in the channel running as an administrator. To get the same functionality you can set the channel's MCAUSER to whatever ID the QMgr is running as (commonly mqm for UNIX and MUSR_MQADMIN on Windows). Boom. Done. Bob's yer uncle.
Yes, this does mean that anyone connecting to that channel is an administrator. On the other hand, this is no worse than it was before as demonstrated by your previous code working the way it did.
You can still use Spring and pass in the ID and password as described in this forum post. Just keep in mind the password is not actually checked. Whatever ID you pass in is accepted at face value unless you use a channel exit to validate it.
For more on WMQ security over client connections, see the Hardening WebSphere MQ presentation. If you wanted to actually secure access to the QMgr you'd want to set MCAUSER to a low-privileged user ID, perform setmqaut commands to authorize that ID's group and then lock down all the other channels like SYSTEM.AUTO.* and SYSTEM.DEF.* so they could not run.
I am running Websphere in my local windows machine and connecting to MQ server in Unix machine . For me only the third option worked. Setting the userID from console didn't work.I tried both mqm and MUSR_MQADMIN.
//connection created using username and password
QueueConnection connection = factory.createQueueConnection("mqm","mqm");

Categories

Resources