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.
Related
Can someone please help me to get/read the port number, hostname, channel details when i using ccdt.tab file. these details were configured in tab file. queue creation was opened successfully by using tab file but i want to get above details (connection details) using java.
I can able to get only queue manager name and queue name by using MQQueueSender.getstringproperty(); but other not able to find.
I expect port number, hostname, channel which i defined in TAB file
MQQueueConnection connection = null;
MQQueueSession session = null;
MQQueueSender sender = null;
MQQueueReceiver receiver = null;
HashMap<String, String> setValue = null;
try {
connection = getConnection(prop.getProperty("tabFilePath"));
session = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueue sendQueue = (MQQueue) session.createQueue(prop.getProperty("queueName"));
sender = (MQQueueSender) session.createSender(sendQueue); System.out.println(sender.getStringProperty("XMSC_WMQ_RESOLVED_QUEUE_MANAGER"));
System.out.println(sender.getStringProperty("XMSC_WMQ_QUEUE_MANAGER"));
There is no MQ API or Class that will parse a CCDT file for an MQ application.
Set the environment variables MQCHLLIB and MQCHLTAB to point to where your CCDT file located and then use runmqsc with the '-n' parameter to have MQ manage it then issue the following MQSC command:
DIS CHL(*) ALL
Now you will have all of the details of all CLNTCONN channels from the CCDT file.
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.
I have a Spring Boot application that sends emails. For any action that requires notification, a Mail instance is created with status PENDING in the database and a job is run to send the pending emails every minute. The status of the Mail instances are set as PENDING, SENT or FAILED.
try {
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.from", myEmailAddress);
props.put("mail.smtp.timeout", 2000);
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
MimeMessage message = new MimeMessage(session);
MimeMultipart content = getContent(mail, message, username);
message.setContent(content);
Transport.send(message);
mail.setStatus(MailStatus.SENT);
mailService.save(mail);
} catch (MailConnectException mce) {
mail.setStatus(MailStatus.FAILED);
mailService.save(mail);
} catch (Exception e) {
// other actions
}
Now, this works fine if a valid email id is provided. But when the receiving email address is a non-existent one like somerandomname#gmail.com, there are no exceptions thrown. From what I read from similar questions in SO and elsewhere, I understand that mail sending is an asynchronous process and hence there is no way to determine that a given email is existing or not (or other issues like Inbox full). That is why after the Transport.send(message); statement, the mail.setStatus(MailStatus.SENT); statement will always be executed irrespective of the email address being present. Later the mail will actually be attempted to be sent and I get an email in myEmailAddress with content like the following:
The response from the remote server was: 550 5.1.1
somerandomname#gmail.com User unknown
Okay, accepted until this point. But now I need a way to alert the user that the email couldn't be sent because they entered an invalid email so that they can update their email. More specifically, I need to set the status of the Mail instance to FAILED. What is the best way to achieve this?
If you use a service like AWS SES to send your mail, you can receive notifications about bounces (and complaints) either simply as notifications for a human to read, or as programmatic triggers that could be used to call an alert endpoint on your spring boot server, for example through AWS Lambda. There are other services that may be able to do the same, such as SendGrid.
Your Spring Boot application would have to retrieve the notifications (an email itself) from your inbox corresponding to the sender address of the initial email. There are various options how to achieve this:
Use a service with provides callbacks (triggers) for incoming messages as mentioned by Benjamin
Poll your inbox using a standard protocol like POP or IMAP
Hint for the second option: https://docs.spring.io/spring-integration/reference/html/mail.html#mail-inbound
I wrote a test program based on below example to connect to service bus(1.1) installed on windows server using amqp 1.0 java api.
https://msdn.microsoft.com/en-us/library/dn574799.aspx?f=255&MSPPError=-2147217396
Everything is fine till session creation. I am getting "Peer did not create remote endpoint for link, target" exception when I tried to create MessageProducer. I used different versions of qpid-amqp-1-0(0.20 to 0.32) but the error is same.
I put some debug statements in source code of qpid-amqp and I observed SendingLinkEndPoint Target became null after few secs.
End point target: Target{address=testnamespace/testqueue}
End point target: Target{address=testnamespace/testqueue}
End point target: Target{address=testnamespace/testqueue}
End point target: Target{address=testnamespace/testqueue}
Attach{name=testnamespace/testqueue}
End point target: null
javax.jms.JMSException: Peer did not create remote endpoint for link, target: testnamespace/testqueue
RECV: com.microsoft:timeout\xa1\xbcThe operation did not complete within the allocated time 00:00:15.0675072 for object connection
I made sure the user got domain suffix as per below post
Connecting to Service Bus on Windows Server (1.1) using Java and AMQP 1.0
I enabled qpid jms debug in log4j.properties but I dont see any debug statements displayed on the console. Not sure what else I need to do to see what other user oberved(in above post).
log4j.rootLogger=TRACE, stdout
log4j.logger.org.apache.qpid.jms=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] - %-5p%-30.30c{1} - %m%n
the test code looks like below
String connectionString = "amqps://" + encode(userName) + ":" + encode(password) + "#" + fqdn;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
env.put(Context.PROVIDER_URL, "blah.txt");
env.put("connectionfactory.ServiceBusConnectionFactory", connectionString);
Context context = null;
ConnectionFactory connectionFactory = null;
Connection connection = connectionFactory.createConnection();
System.setProperty("javax.net.ssl.trustStore","C:\\Program Files (x86)\\Java\\jre1.8.0_111\\lib\\security\\cacerts");
System.setProperty("javax.net.ssl.trustStorePassword",pwd);
Session session = null;
MessageProducer producer = null;
try
{
System.out.println("Creating session\n");
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("Creating Queue Impl\n");
QueueImpl queueImpl = QueueImpl.createQueue("testnamespace/testqueue");
System.out.println("Creating producer\n");
producer = session.createProducer(queueImpl);
}
catch (Exception e)`enter code here`
{
System.out.println("Exception creating session/producer\n");
return;
}
All the required ports are opened and enabled debug/trace at windows service level in event viewer but I am unable to identify whats the problem.
Any help would be greatly appreciated.
Thank you,
S.
One of my customers is using Gmail for business (part of Google Apps) and I had to reconfigure the website I've developed so it would match the new credentials. After a bit of struggle due to TLS errors, I've managed to make the code work on localhost and on my test server (both Apache Tomcat 5.5). Everything was going smooth until I had to move it on his server (another Tomcat 5.5 as the hosting company told me). On the client's server I get the following error:
javax.mail.SendFailedException: Sending failed;
nested exception is:
class javax.mail.MessagingException: Could not connect to SMTP host: smtp.gmail.com, port: 465;
nested exception is:
java.io.IOException: Couldn't connect using "javax.net.ssl.SSLSocketFactory" socket factory to host, port: smtp.gmail.com, 465; Exception: java.lang.reflect.InvocationTargetException
The strange thing is that on localhost and the test server the port 465 works fine, and the guys from hosting said that port is opened on their server.
The code that connects to the mailserver is:
private void initConfigParams() throws CMSGeneralException {
try {
props = System.getProperties();
String smtpHost = Messages.getDBConfString("mail.smtp.host");
String mailPort = Messages.getDBConfString("mail.smtp.port");
String socketFallback = Messages.getDBConfString("mail.smtp.socketFactory.fallback");
String enableTls = Messages.getDBConfString("mail.smtp.starttls.enable");
String authSmtp = Messages.getDBConfString("mail.smtp.auth");
String tlsRequired = Messages.getDBConfString("mail.smtp.stattls.required");
String sktFactory = Messages.getDBConfString("mail.smtp.socketFactory.class");
props.put("mail.smtp.starttls.enable", enableTls);
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.auth", authSmtp);
props.put("mail.smtp.starttls.required", tlsRequired);
props.setProperty("mail.smtp.socketFactory.class", sktFactory);
props.setProperty("mail.smtp.socketFactory.fallback", socketFallback);
props.setProperty("mail.smtp.port", mailPort);
props.setProperty("mail.smtp.socketFactory.port", mailPort);
props.put("mail.transport.protocol", Messages.getDBConfString("mail.transport.protocol"));
Authenticator auth = null;
userName = Messages.getDBConfString("mail.username");
userPassword = Messages.getDBConfString("mail.userpassword");
if (!CMSUtils.isEmptyString(userName) && !CMSUtils.isEmptyString(userPassword)){
/* props.put("mail.smtp.auth", "true"); */
auth = new SMTPAuthenticator(userName, userPassword);
}
session = Session.getDefaultInstance(props, auth);
session.setDebug(false);
address = new InternetAddress[1];
address[0] = new InternetAddress(recipients);
mbText = new MimeBodyPart();
mbText.setContent(text, "text/html");
mp = new MimeMultipart();
mp.addBodyPart(mbText);
} catch (MessagingException e) {
e.printStackTrace();
throw new CMSGeneralException();
}
}
With the following .properties file
mail.smtp.starttls.enable=true
mail.smtp.host=smtp.gmail.com
mail.smtp.auth=true
mail.smtp.starttls.required=true
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback=false
mail.smtp.port=465
mail.transport.protocol=smtps
mail.username=website#example.com
mail.userpassword=password
mail.from=website#example.com
mail.to=mail#example.com
I tried the other ports for GMail, 587, but it doesn't work on any of the servers. Not even port 25 won't do the trick.
So, what am I doing wrong, and what should I do to make the mailing work?
Get rid of all the socket factory properties; if you're using a reasonably recent version of JavaMail you don't need them. See the JavaMail FAQ for how to configure JavaMail to access Gmail. You'll also find debugging tips there if it still doesn't work.
Also, change Session.getDefaultInstance to Session.getInstance.
And finally, if you're setting "mail.transport.protocol" to "smtps", you need to set the other properties as "mail.smtps." properties, not "mail.smtp." properties.
It seems to you have problems with java.lang.reflect.InvocationTargetException. So, I mean it's not a network problem. May be you've specified some parameters wrong and JavaMail API routes couldn't invoke a method. May be you should also specify property mail.smtp.ssl.socketFactory.
Some documentation here http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html .