tlsv1 alert unknown ca error when connecting to Crossbar server with Python

I’ve been trying to make a simple WAMP RPC based application using Autobahn(Python) and Crossbar. The script connects and everything works when I try using an unsecured WebSocket connection, but I am not able to figure out what I am doing wrong when connecting using SSL Certificates.

Crossbar config.json:-

    {
    "$schema": "https://raw.githubusercontent.com/crossbario/crossbar/master/crossbar.json",
    "version": 2,
    "controller": {
    },
    "workers": [
        {
            "type": "router",
            "realms": [
                {
                    "name": "name_1",
                    "roles": [
                        {
                            "name": "anonymous",
                            "permissions": [
                                {
                                    "uri": "",
                                    "match": "prefix",
                                    "allow": {
                                        "call": true,
                                        "register": true,
                                        "publish": true,
                                        "subscribe": true
                                    },
                                    "disclose": {
                                        "caller": false,
                                        "publisher": false
                                    },
                                    "cache": false
                                }
                            ]
                        }
                    ]
                }
            ],
            "transports": [
                {
                    "type": "websocket",
                    "endpoint": {
                        "type": "tcp",
                        "port": 8080,
                        "tls": {
                            "key": "path/to/letsencrypt/keys/privkey.pem",
                            "certificate": "path/to/letsencrypt/keys/cert.pem",
                            "chain_certificates": ["path/to/letsencrypt/keys/chain.pem"],
                            "ca_certificates": [
                                "isrgrootx1.pem",
                                "letsencryptauthorityx1.pem",
                                "letsencryptauthorityx2.pem"
                            ],
                            "ciphers": "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS"
                        }
                    },
                    "url": "wss://[domain]",
                    "serializers": ["json"],
                    "auth": {
                        "ticket": {
                            "type": "static",
                            "principals": {
                                "user_1": {
                                    "ticket": "ticket_1",
                                    "role": "anonymous"
                                }
                            }
                        }
                    },
                    "options": {
                        "allowed_origins": ["*"],
                        "allow_null_origin": true,
                        "enable_webstatus": true,
                        "max_frame_size": 1048576,
                        "max_message_size": 1048576,
                        "auto_fragment_size": 65536,
                        "fail_by_drop": true,
                        "open_handshake_timeout": 2500,
                        "close_handshake_timeout": 1000,
                        "auto_ping_interval": 10000,
                        "auto_ping_timeout": 5000,
                        "auto_ping_size": 4,
                        "compression": {
                            "deflate": {
                                "request_no_context_takeover": false,
                                "request_max_window_bits": 13,
                                "no_context_takeover": false,
                                "max_window_bits": 13,
                                "memory_level": 5
                            }
                        }
                    }
                },
                {
                    "type": "websocket",
                    "endpoint": {
                        "type": "tcp",
                        "port": 8081
                    },
                    "url": "ws://[domain]",
                    "serializers": ["json"],
                    "options": {
                        "allowed_origins": ["*"],
                        "allow_null_origin": true,
                        "enable_webstatus": false,
                        "max_frame_size": 1048576,
                        "max_message_size": 1048576,
                        "auto_fragment_size": 65536,
                        "fail_by_drop": true,
                        "open_handshake_timeout": 2500,
                        "close_handshake_timeout": 1000,
                        "auto_ping_interval": 10000,
                        "auto_ping_timeout": 5000,
                        "auto_ping_size": 4,
                        "compression": {
                            "deflate": {
                                "request_no_context_takeover": false,
                                "request_max_window_bits": 13,
                                "no_context_takeover": false,
                                "max_window_bits": 13,
                                "memory_level": 5
                            }
                        }
                    }
                }
            ]
        }
    ]
}

Python script:-

import os, sys

from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks

from autobahn.twisted.wamp import ApplicationSession, ApplicationRunner
from autobahn.wamp.types import PublishOptions


PRINCIPAL = "user_1"
PRINCIPAL_TICKET = "ticket_1"

class ClientSession(ApplicationSession):
    def onConnect(self):
        print("Client session connected. Starting WAMP-Ticket authentication on realm '{}' as principal '{}' ..".format(self.config.realm, PRINCIPAL))
        self.join(self.config.realm, ["ticket"], PRINCIPAL)

    def onChallenge(self, challenge):
        if challenge.method == "ticket":
            print("WAMP-Ticket challenge received: {}".format(challenge))
            return PRINCIPAL_TICKET
        else:
            raise Exception("Invalid authmethod {}".format(challenge.method))

    @inlineCallbacks
    def onJoin(self, details):
        print("Client session joined: {}".format(details))
        print("\nHooray! We've been successfully authenticated with WAMP-Ticket using static configuration!\n")

        ## call a procedure we are allowed to call (so this should succeed)
        ##
        try:
            res = yield self.call('com.example.add2', 2, 3)
            print("call result: {}".format(res))
        except Exception as e:
            print("call error: {}".format(e))

        ## (try to) register a procedure where we are not allowed to (so this should fail)
        ##
        try:
            reg = yield self.register(lambda x, y: x * y, 'com.example.mul2')
        except Exception as e:
            print("registration failed (this is expected!) {}".format(e))

        ## publish to a couple of topics we are allowed to publish to.
        ##
        for topic in [
            'com.example.topic1',
            'com.foobar.topic1']:
            try:
                yield self.publish(topic, "hello", options = PublishOptions(acknowledge = True))
                print("ok, event published to topic {}".format(topic))
            except Exception as e:
                print("publication to topic {} failed: {}".format(topic, e))

        ## (try to) publish to a couple of topics we are not allowed to publish to (so this should fail)
        ##
        for topic in [
            'com.example.topic2',
            'com.foobar.topic2']:
            try:
                yield self.publish(topic, "hello", options = PublishOptions(acknowledge = True))
                print("ok, event published to topic {}".format(topic))
            except Exception as e:
                print("publication to topic {} failed (this is expected!) {}".format(topic, e))

        self.leave()

    def onLeave(self, details):
        print("Client session left: {}".format(details))
        self.disconnect()

    def onDisconnect(self):
        print("Client session disconnected.")
        reactor.stop()

        
runner = ApplicationRunner(url='wss://[domain]:8080', realm='name_1')
runner.run(ClientSession)

Crossbar trace:-

[Router      32589 crossbar.router.protocol.WampWebSocketServerProtocol] connection accepted from peer tcp4:[ip_address]:5259
[Router      32589 crossbar.router.protocol.WampWebSocketServerProtocol] Connection made to tcp4:[ip_address]:5259
[Router      32589 crossbar.router.protocol.WampWebSocketServerProtocol] SSL error: tlsv1 alert unknown ca (in ssl3_read_bytes)
[Router      32589 crossbar.router.protocol.WampWebSocketServerProtocol] _connectionLost: [Failure instance: Traceback: <class 'OpenSSL.SSL.Error'>: [('SSL routines', 'ssl3_read_bytes', 'tlsv1 alert unknown ca')]

Python error: SSL error: certificate verify failed (in tls_process_server_certificate)

Initially, I did not have CA certificates in the Crossbar config (no changes in the errors). I figured that I should add them because it may have something to do with this so I got them from https://letsencrypt.org/2015/06/04/isrg-ca-certs.html (not sure if this is correct). Also tried from https://letsencrypt.org/certificates/, but this went over my head (wth do I even need?).

I think that it’s because the CA sent by the machine trying to connect is not recognised by the server.

Now, I can’t figure out how to change either the CA or the Certificates that are being sent by the script and even if I had another way (I have some alternative scripts I can try but they’re for plain WebSocket) then what should I change it to.

Help, please?