Search code examples
nginxsslgoogle-cloud-platformpaho

Connect to mqtt.googleapis.com:8883 via proxy and another domain


For some reasons our infra blocks mqtt.googleapis.com. That's why was deployed nginx proxy with such configuration

stream {
    upstream google_mqtt {
            server mgtt.googleapis.com:8883;
    }
    server {
            listen 8883;
            proxy_pass google_mqtt;
    }
}

Also it has external IP with domain name fake.mqtt.com

Using example here I'm testing connectivity. If script to run against mgtt.googleapis.com:8883 everything works fine. But if domain switch to fake.mqtt.com got an error:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'fake.mqtt.com' 

For client implementation was used paho.mqtt.client.

Auth to mqtt broker realized with JWT.

def create_jwt(project_id, private_key_file, algorithm):
    token = {
        # The time that the token was issued at
        "iat": datetime.datetime.utcnow(),
        # The time the token expires.
        "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=20),
        # The audience field should always be set to the GCP project id.
        "aud": project_id,
    }

    # Read the private key file.
    with open(private_key_file, "r") as f:
        private_key = f.read()

    print(
        "Creating JWT using {} from private key file {}".format(
            algorithm, private_key_file
        )
    )
    return jwt.encode(token, private_key, algorithm=algorithm)

Set JWT

client.username_pw_set(
            username='unused',
            password=create_jwt(project_id, private_key_file, algorithm))

TLS configuration:

client.tls_set(ca_certs='roots.pem', tls_version=ssl.PROTOCOL_TLSv1_2,)

Could you advise what to configure on nginx/paho-client side and is it working solution at all?

Or may be 3party brokers can connect to mqtt.googleapis.com? (from information i read here and on another resources - no)


Solution

  • You can not just arbitrarily change the domain name if you are just stream proxying, it needs to match the one presented in the certificate by the remote broker or as you have seen it will not validate.

    You can force the client to not validate the server name by setting client.tls_insecure_set(True) but this is a VERY bad idea and should only be used for testing and never in production.