Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AMQPS Support #77

Closed
seonixx opened this issue Feb 19, 2017 · 15 comments
Closed

AMQPS Support #77

seonixx opened this issue Feb 19, 2017 · 15 comments

Comments

@seonixx
Copy link

seonixx commented Feb 19, 2017

I'm trying to connect to a hosted amqp provider that mandates AMQPS be used. Does this module support AMPQS and if so, is there an example of a working connection?

@seonixx
Copy link
Author

seonixx commented Feb 19, 2017

Assuming type remains amqp, I'm getting the following error when trying to connect with AMPQS:

Seneca Fatal Error
10:18:55 PM service_meta.1 |  ==================
10:18:55 PM service_meta.1 |  Message: seneca: unable to verify the first certificate
10:18:55 PM service_meta.1 |  Code: transport_listen

@nfantone
Copy link
Collaborator

nfantone commented Feb 20, 2017

Hi, @seonixx.

Does this module support AMPQS and if so, is there an example of a working connection?

Yes and yes.

Plugin supports whatever ampqlib does, actually.

Assuming type remains amqp.

Correct. Type remains 'amqp', but protocol in URI should really be amqps://.

@seonixx
Copy link
Author

seonixx commented Feb 21, 2017

@nfantone thanks. Not sure how I missed the socket options :/

Anyway - I'm getting those errors when trying to connect to a compose.io rabbitmq host that uses AMQPS with letsencrypt certificates. I assumed this would work as is, or at most require me to add the root CA and/or intermediary CAs for letsencrypt. Nothing seems to make a difference though. Any ideas?

@seonixx
Copy link
Author

seonixx commented Feb 21, 2017

@nfantone I just tested and can connect fine when using amqplib, but not with this module. Any idea why this could be interfering with SSL connections?

@nfantone
Copy link
Collaborator

nfantone commented Feb 21, 2017

@seonixx That is odd. Would you mind sharing some code snippets on how you are connecting with both, please? Also, are you using latest versions for this transport and amqplib?

@seonixx
Copy link
Author

seonixx commented Feb 21, 2017

@nfantone Sure.

Working (based on example from amqplib docs):

var amqp = require('amqplib/callback_api');
var url = require('url');

function bail(err, conn) {
    console.error(err);
    if (conn) conn.close(function() {
        process.exit(1);
    });
}

rabbitmqurl = 'amqps://[username]:[password]@[hostname]:15846/mq';
parsedurl = url.parse(rabbitmqurl);

amqp.connect(rabbitmqurl, { servername: parsedurl.hostname }, function(err, conn) {
    if (err !== null) return bail(err, conn);

    //console.log(conn);

    conn.createChannel(function(err, channel) {

        if (err !== null) return bail(err, conn);
        var message = "This is not a message, this is a node tribute to a message";
        var routingKey = "tributes";
        var exchangeName = "postal";

        channel.assertExchange(exchangeName, "direct", {
            durable: true
        }, function(err, ok) {
            if (err !== null) return bail(err, conn);
            channel.publish(exchangeName, routingKey, new Buffer(message))
        });

    });

    //setTimeout(function() { conn.close(); process.exit(0) }, 500);

});

Getting various certificate errors when connecting with this module as follows (for example):

var seneca = require('seneca');

var cryptEntityKey = function () {
    this.add({service: 'crypt', controller: 'entityKey', action: 'set'}, function (msg, respond) {
      
    return respond({'msg': 'Done'});
    });
  };

seneca
.use('seneca-amqp-transport')
.use(cryptEntityKey)
.listen({
  type: 'amqp',
  pin: 'service:crypt,controller:entityKey,action:*',
  url: 'amqps://[username]:[password]@[hostname]:15846/mq'
});

@seonixx
Copy link
Author

seonixx commented Feb 22, 2017

@nfantone - using amqplib 0.5.1 and seneca-amqp-transport 2.1.0.

@nfantone
Copy link
Collaborator

@seonixx Sorry, I'm a bit busy today. Didn't find the time to try your code. Meanwhile, could you do me a favor? Could you try connecting using latest on develop, please?

@seonixx
Copy link
Author

seonixx commented Feb 22, 2017

@nfantone Tried connecting with latest on develop. Same issue unfortunately :(

@nfantone
Copy link
Collaborator

nfantone commented Feb 23, 2017

@seonixx I just ran the examples/listener.js listener over amqps and it worked fine.

Could you try doing the same with your endpoint, please? Just clone the repo and from its directory, execute:

AMQP_URL=amqps://[username]:[password]@[hostname]:15846/mq node examples/listener.js

This will create a seneca.add.cmd:salute queue on the virtual host, mind you. Delete it after the test.

@nfantone
Copy link
Collaborator

nfantone commented Feb 23, 2017

Another thing you can do is read and follow the amqplib guide on SSL. It even provides actions for common errors.

Also, if you are using self-signed certificates, try setting the env var NODE_TLS_REJECT_UNAUTHORIZED=0 before connecting. You can do that by adding:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

in your script before connecting or by setting its value in the terminal before running it:

NODE_TLS_REJECT_UNAUTHORIZED=0 node your-script.js

@seonixx
Copy link
Author

seonixx commented Feb 23, 2017

@nfantone when you say you ran it over AMQPS and it worked fine, what kind of certificates were you using? Self signed? I'm using a hosted compose.io instance that uses letsencrypt certificates.

I get the same error testing with the listener, however it works with process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; set. Obviously this isn't really a solution though as its just ignoring the certificate errors.

Like I said it works fine on amqplib without designating any additional root or intermediary CAs so there must be something different with how this module is handling or parsing certificates.

Any ideas? Thanks for your help so far.

@nfantone
Copy link
Collaborator

nfantone commented Feb 23, 2017

@seonixx Ok, so this is evidently a certificate validation issue. To be completely honest with you, I don't understand how your amqplib script works. If you don't provide a cert in socketOptions that would mean one of two things:

  • You are using a self-signed cert (local network infrastructure, most likely) and you don't really care about validity. In this case, you should set NODE_TLS_REJECT_UNAUTHORIZED = 0. It's not really a hack if you are under this scenario: many small/medium companies don't care/don't want to spend money on certs.
  • The CA cert if already pre-installed in your OS as a "trusted root certificate".

AFAIK, under any other circumstance, you must provide, at least, a cacert.pem file on connection. Otherwise, you'll get errors like the one you're experiencing.

when you say you ran it over AMQPS and it worked fine, what kind of certificates were you using?

An Amazon AWS hosted RabbitMQ instance using a Comodo root CA. This authority is well known and comes installed in most OS nowadays. If you are using macOS, you can check what CAs are installed by default by pointing your browser to file:///System/Library/Security/Certificates.bundle/Contents/Resources/TrustStore.html (see this).

Would you corroborate that your "lestencrypt" CA is under there (it wasn't in my macOS Sierra)? If it is, then there's definitely something odd with the transport. If it's not, then we are going to need to ask @squaremo about amqplib's behaviour.

@seonixx
Copy link
Author

seonixx commented Feb 23, 2017

@nfantone OK I worked out the issue finally. A bit of background...

Letsencrypt issues a server certificate that you verify against ISRG Root X1 CA which is why generally you need dont need to add any additional CAs as most OS/browsers would have this CA installed and trusted by default.

I did a test on the TLS connection and saw something interesting in regards to the issuer of the certificate so I got in touch with compose.io:

Certificate chain
0 s:/CN=dbname-mq
i:/CN=OrgName-70058ddc28bd139d3cf6xxxxxxxxxxx

You'd expect the issuer to be LetsEncrypt.

Now the issue was that you have to pass a servername parameter when using letsencrypt, and amqplib doesn't parse this for you. If you see the amqplib example above, it passes this in the options. My faulty example did not pass the servername in the options. As soon as I added the servername option it worked fine.

Thanks for your time and apologies for not picking up the issue earlier :(

@seonixx seonixx closed this as completed Feb 23, 2017
@nfantone
Copy link
Collaborator

@seonixx Glad to see that you could resolve the issue in the end. Feel free to open another one if something else comes up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants