aws-amplify / aws-sdk-ios

AWS SDK for iOS. For more information, see our web site:
https://aws-amplify.github.io/docs
Other
1.68k stars 885 forks source link

mqtt connectUsingWebSocket failing despite ATS endpoint #1200

Closed owenhay closed 5 years ago

owenhay commented 5 years ago

Describe the bug In iOS 12.1+ mqtt connectUsingWebSocket is failing due to an SSL handshake failure, despite using the -ats endpoint. The only thing that is unique in my code is that our endpoint and identiy pool are in different regions (this worked fine before iOS 12.1).

To Reproduce Steps to reproduce the behavior: iotSample.zip

  1. Open the sample project in Xcode
  2. make sure to pod install first
  3. I can personally provide our identity pool id and endpoint, but I didn't want to post them in a public forum.

Which AWS service(s) are affected? AWS-iOT

Expected behavior connectUsingWebSocket should succeed But instead returns a connection error:

2019-01-25 10:47:47.214597-0800 iotSample[1289:66420] CFNetwork SSLHandshake failed (-9807) 2019-01-25 10:47:47.214713-0800 iotSample[1289:66420] TCP Conn 0x281fec540 SSLHandshake failed (-9807) 2019-01-25 10:47:47.216903-0800 iotSample[1289:67403] mqtt Connection Error

details on the error: https://developer.apple.com/documentation/security/1400161-sslhandshake _kCFStreamErrorCodeKey=-9807 errSSLXCertChainInvalid—The peer has an invalid certificate chain; for example, signature verification within the chain failed, or no certificates were found.

Environment(please complete the following information):

Device Information (please complete the following information):

sample code for people that don't want to open the projecrt:

//
//  ViewController.swift
//  iotSample
//
//  Created by Owen Hay on 1/25/19.
//  Copyright © 2019 Owen Hay. All rights reserved.
//

import UIKit
import AWSCore
import AWSIoT
import AWSMobileClient

class ViewController: UIViewController {

    var iotDataManager: AWSIoTDataManager!
    var iotManager: AWSIoTManager!
    var iot: AWSIoT!

    let IOT_ENDPOINT = "xxxxxxxxxxxxxx-ats.iot.us-west-2.amazonaws.com"
    let ASWIoTDataManager = "MyIotDataManager"

    override func viewDidLoad() {
        super.viewDidLoad()

        let credentialsProvider = AWSCognitoCredentialsProvider.init(regionType: AWSRegionType.USEast1, identityPoolId: "us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx")
        let iotConfiguration = AWSServiceConfiguration(region: AWSRegionType.USWest2, credentialsProvider: credentialsProvider)

        iotConfiguration?.baseURL = URL(string:IOT_ENDPOINT)
        AWSServiceManager.default()?.defaultServiceConfiguration = iotConfiguration

        iotManager = AWSIoTManager.default()
        iot = AWSIoT.default()
        AWSIoTDataManager.register(with: iotConfiguration!, forKey: ASWIoTDataManager)

        self.connect()
    }

    func mqttEventCallback( _ status: AWSIoTMQTTStatus )
    {
        switch(status)
        {
        case .connecting:
            NSLog("mqtt connecting")
        case .connected:
            NSLog("mqtt connected")

        case .disconnected:
            NSLog("mqtt disconnected")

        case .connectionRefused:
            NSLog("mqtt Connection Refused")

        case .connectionError:
            NSLog("mqtt Connection Error")

        case .protocolError:
            NSLog("mqtt Protocol Error")

        default:
            NSLog("mqtt Unknown State")
        }

    }

    func connect() -> Void {

            iotDataManager = AWSIoTDataManager(forKey: ASWIoTDataManager)

            let uuid = UUID().uuidString
            iotDataManager?.connectUsingWebSocket(withClientId: uuid, cleanSession: true, statusCallback: mqttEventCallback)
    }

}
scb01 commented 5 years ago

@owenhay

The endpoint and identity pool being in different regions doesn't affect anything and it will work. Looking at your code sample, it appears that the endpoint is not being properly set and you are still using the non-ATS endpoint.

Can you change your code as follows and remove the line where you are setting the baseURL.

   let iotConfiguration = AWSServiceConfiguration(
            region: AWSRegionType.USWest2,
            endpoint: AWSEndpoint(urlString: IOT_ENDPOINT),
            credentialsProvider: credentialsProvider)

Try this and let me know how you fare.

owenhay commented 5 years ago

New error (the ats endpoint is responding in our android app if that's helpful):

2019-01-25 12:50:55.712423-0800 iotSample[1812:90396] mqtt connecting 2019-01-25 12:50:55.717788-0800 iotSample[1812:90395] [] nw_connection_get_connected_socket [C4] Client called nw_connection_get_connected_socket on unconnected nw_connection 2019-01-25 12:50:55.717943-0800 iotSample[1812:90395] TCP Conn 0x28004a940 Failed : error 0:-65554 [-65554] 2019-01-25 12:50:55.720601-0800 iotSample[1812:90396] mqtt Connection Error

owenhay commented 5 years ago

Also, thank you for the prompt attention :-)

owenhay commented 5 years ago

added http:// to my endpoint constant and it's connecting now. I think I'm unblocked. thank you!

scb01 commented 5 years ago

Excellent! I will go ahead and close out this issue. Please feel free to post back here/create a new issue if you run into problems.

owenhay commented 5 years ago

For someone else who researches this issue. Here is what we had to change:

  1. specify the ats endpoint with https OLD: let IOT_ENDPOINT = "xxxxxxxxxxxxxx.iot.us-west-2.amazonaws.com" NEW: let IOT_ENDPOINT = "https://xxxxxxxxxxxx-ats.iot.us-west-2.amazonaws.com"

  2. specify the endpoint as part of creating the configuration, instead of using a baseurl: OLD:

       let iotConfiguration = AWSServiceConfiguration(region: AWSRegion, credentialsProvider: credentialsProvider)
        iotConfiguration?.baseURL = URL(string:IOT_ENDPOINT)

    NEW:

        let iotConfiguration = AWSServiceConfiguration(
            region: AWSRegionType.USWest2,
            endpoint: AWSEndpoint(urlString: IOT_ENDPOINT),
            credentialsProvider: credentialsProvider)
just4give commented 4 years ago

Hello, I am facing similar problem today and as per above suggestion, I added https to the endpoint but all I am seeing is "AWS IoT connection error". Below is my code. Can someone please help or suggest something?

    let identityPoolId = "us-east-1:xxxxxxxx"
    let IOT_ENDPOINT = "https://xxxxxxx-ats.iot.us-east-1.amazonaws.com"
    let ASWIoTDataManager = "MyIotDataManager"

func setupAWSIoT(){
        // Create AWS credentials and configuration
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.USEast1,
        identityPoolId:identityPoolId)

        let configuration = AWSServiceConfiguration(region:.USEast1, credentialsProvider:credentialsProvider)

        AWSServiceManager.default().defaultServiceConfiguration = configuration

        let iotDataConfiguration = AWSServiceConfiguration(region: .USEast1,
                                                           endpoint: AWSEndpoint(urlString: IOT_ENDPOINT),
                                                           credentialsProvider: credentialsProvider)

        AWSIoTDataManager.register(with: iotDataConfiguration!, forKey: ASWIoTDataManager)

        let uuid = UUID().uuidString

        let dataManager = AWSIoTDataManager(forKey: ASWIoTDataManager)
        dataManager.connectUsingWebSocket(withClientId: uuid,
                                          cleanSession: true,
                                          statusCallback: mqttEventCallback)
      }

func mqttEventCallback(_ status: AWSIoTMQTTStatus ) {

    switch status {
        case .connecting: print("Connecting to AWS IoT")
        case .connected:
            print("Connected to AWS IoT")
            // Register subscriptions here
            // Publish a boot message if required
        case .connectionError: print("AWS IoT connection error")
        case .connectionRefused: print("AWS IoT connection refused")
        case .protocolError: print("AWS IoT protocol error")
        case .disconnected: print("AWS IoT disconnected")
        case .unknown: print("AWS IoT unknown state")
        default: print("Error - unknown MQTT state")
        }
    }

<< Cloud Watch Log >>

{
    "timestamp": "2020-07-02 03:15:08.414",
    "logLevel": "ERROR",
    "traceId": "2c5c363a-22aa-21ec-c0e5-6bb6b2366406",
    "accountId": "xxxxxx",
    "status": "Failure",
    "eventType": "Connect",
    "protocol": "MQTT",
    "clientId": "xxxxx",
    "principalId": "xxxxx:CognitoIdentityCredentials",
    "sourceIp": "xxxx",
    "sourcePort": 55136,
    "reason": "AUTHORIZATION_FAILURE",
    "details": "Authorization Failure"
}

I added AWSIotFullAccess to cognito auth and unauth roles.

just4give commented 4 years ago

It has been resolved. I was using cognito identity pool which was created by Amplify. I guess roles of that pool had some conflict. I created a new cognito identity pool specific for IoT messages, added AWSIoTDataAccess policy to both auth and unauth roles and it worked.