njh / ruby-mqtt

Pure Ruby gem that implements the MQTT protocol, a lightweight protocol for publish/subscribe messaging.
http://www.rubydoc.info/gems/mqtt
MIT License
541 stars 135 forks source link

Secure connection to AWS IoT platform #97

Closed DaKaZ closed 7 years ago

DaKaZ commented 7 years ago

Hello, thank you for this great gem.

We were running our own MQTT broker but now are considering using the AWS IoT platform. This platform uses a custom certificate approach that looks like it SHOULD work with this client, but I cannot get it to work. Here is the code I am using:

        client = MQTT::Client.new
        client.client_id = "rails_#{'%09d' % rand(10 ** 9)}"
        client.ssl = true
        client.port = 8883
        client.host = '<my_endpoint>.iot.us-east-1.amazonaws.com'
        client.ca_file   = Rails.root.join('lib/certs/iot/root.pem.crt').to_s
        client.cert_file = Rails.root.join('lib/certs/iot/certificate.pem.crt').to_s
        client.key_file  = Rails.root.join('lib/certs/iot/private.pem.key').to_s
        client.connect()

And this gives this error:

MQTT::ProtocolException: Failed to read byte from socket

I read in another issue that this is a generic message for "server disconnected". Any ideas?

Per AWS's documentation, I verified my certificates are working correctly:

openssl s_client -connect <my_endpoint>.iot.us-east-1.amazonaws.com:8443 -CAfile lib/certs/iot/root.pem.crt -cert lib/certs/iot/certificate.pem.crt -key lib/certs/iot/private.pem.key 

CONNECTED(00000003)

Any suggestions or ideas? Has anyone else had success connecting to AWS IoT with certificates?

njh commented 7 years ago

I have not used AWS IoT with ruby-mqtt, sorry. When SSL fails, it is a real pain to work out what is going wrong 🤕

Will wait and see if anyone else has has had some success...

cdurante commented 7 years ago

I have used the gem successfully with AWS in the past, but I connected via the alternate method:


# Create a hash with the connection parameters
conn_opts = {
    host: AWS_IOT_ENDPOINT,
    port: 8883,
    ssl: true,
    cert_file: 'mqtt-cert/certificate.pem.crt',
    key_file: 'mqtt-cert/private.pem.key',
    ca_file: 'mqtt-cert/rootCA.pem'
}

Thread.new do
    MQTT::Client.connect(conn_opts) do |c|
          puts 'MQTT Daemon Started'
          # This will bock until messages arrive to the topic
          c.get('$aws/things/+/shadow/update/accepted') do |topic, message|
              puts "#{topic}: #{message}"
          end
    end
end
DaKaZ commented 7 years ago

@cdurante how is that method different or "alternate" from the code I posted? Is there any chance you can confirm that still works for you? Do you have any policies associated with your certificate at aws?? Thanks, I appreciate the intel...

cdurante commented 7 years ago

I guess I meant to say syntactically alternate, but yes it still works.

Try using the full-access policy while developing, and then back them off to only the bare essentials once you're ready.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
DaKaZ commented 7 years ago

Awesome - that was my problem, the policy with my certificate as too restrictive. Thank you so much @cdurante !!