Issues connecting to mosquitto broker with node mqtt client via SSL/TLS - node.js

Helllo, I created a mosquitto broker via the eclipse docker image and recently followed this guide to add SSL/TLS support: http://www.steves-internet-guide.com/mosquitto-tls/.
When I am sshed in the VPS which is running the broker, I can use the command:
mosquitto_pub -h VPS_NAME -t test/topic -p 8883 --cafile ca.crt -m message -u BROKER_USERNAME -P BROKER_PASSWORD
and it publishes all fine and dandy. However when I run the same command on a local computer, I get the error:
'Unable to connect (Lookup error.).
I don't get any new logs from the broker container, so I think it's not even reaching the container. However when I run:
mosquitto_pub -h BROKER_IP_ADRESS -t test/topic -p 8883 --cafile ca.crt -m message -u BROKER_USERNAME -P BROKER_PASSWORD
I do get a response which is Error: A TLS error occured, and on my docker logs I get:
1583004287: New connection from LOCAL_IP_ADDRESS on port 8883.
1583004287: OpenSSL Error: error:14037438:SSL routines:ACCEPT_SR_KEY_EXCH:tlsv1 alert internal error
1583004287: OpenSSL Error: error:140370E5:SSL routines:ACCEPT_SR_KEY_EXCH:ssl handshake failure
1583004287: Socket error on client <unknown>, disconnecting.
I am only able to get a sucessful publish send when I add the --insecure command to the publish however I want to make sure the client knows that it's talking to the right server so I don't think this is the right solution.
In the end I want to run an mqtt client on a node application, I've tried this piece of code:
const fs = require('fs');
const optionsz = {
ca: [ fs.readFileSync(__dirname + '/ca.pem') ],
host: 'BROKER_IP_ADDRESS',
servername: 'VPS_NAME',
port: 8883,
rejectUnauthorized : false,
username : 'BROKER_USERNAME', // mqtt credentials if these are needed to connect
password : 'BROKER_PASSWORD',
clientId : 'test',
// Necessary only if the server's cert isn't for "localhost".
checkServerIdentity: () => { return null; },
};
class MqttHandler {
constructor() {
this.mqttClient = null;
};
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, optionsz);
...
when I run this i keep getting disconnect events, and on my docker logs i get:
1583004505: New connection from LOCAL_IP_ADDRESS on port 8883.
1583004505: OpenSSL Error: error:140260FC:SSL routines:ACCEPT_SR_CLNT_HELLO:unknown protocol
1583004505: Socket error on client <unknown>, disconnecting.
I am really confused on how to even tackle this issue, I've been able to connect to a broker without SSL/TLS protection, but I wanted to make my device communication more secure.
Thank you for your time!

2 separate problems here.
Looks like you don't have a valid DNS entry for your VPS. mosquitto_pub is failing because it can't resolve the name to an IP address. It works with the --insecure and the IP address because you are telling mosquitto_pub to ignore the fact that the CN or SANs in the brokers certificate doesn't include the IP address only the name.
You are trying to connect with raw MQTT not MQTT over TLS, you need to use a URL not just a hostname or the first argument of the connect() function. e.g.
this.mqttClient = mqtt.connect("mqtts://" + this.host, optionsz);
To be honest you need to fix both of these to get things working properly.
To fix 1 you need to sort your DNS entries out so you have a valid fully qualified hostname that points to your VPS and matches the certificate you've deployed there.

Related

Not able to connect to EC2 instance in AWS via python/psycopg2

I am having trouble connecting to aws via python. I have a macOSX operating system.
What i did:
I created an ec2 instance and chose an operating system (ubuntu) and downloaded postgresssql in the remote server. Then i created securitygroups where i added the following configuration:
type:
ssh
protocol:
tcp
port: 22
source: custom, 0.0.0.0/0
Then i added another rule:
postgresql
TCP
5432
custom
my_computer_ip_address 71.???.??.???/32
where i added question marks just to hide the address. but its in that format.
Now, aws had me create a .pem file in order to query from the database. I downloaded this pem file into a secret location.
When i go to my local machine, go to my terminal and type:
ssh -i "timescale.pem" ubuntu#ec2-??-???-??-???.compute-1.amazonaws.com"
i am able to connect. I also went to my dbeaver and created a new connection and set up a connection where i am using an ssh tunnel and a public key to read the 'timescale.pem' file i created. I then go to main and type my username and password:
username: postgres
database: example
password: mycustompassword
and i am able to connect with no issues.
Now, when I go to python with psycopg2 library, i am just unable to connect at all. I have gone through all the examples here in stackoverflow and none of them have helped me. Heres what i am using to connect to aws from python:
path_to_secret_key = os.path.expanduser("~/timescale.pem")
conn = psycopg2.connect(
dbname='example',
user='postgres',
password='pass123',
host='ec2-??-??-??-???.compute-1.amazonaws.com',
port='22',
sslmode='verify-full',
sslrootcert=path_to_secret_key)
I then get this error:
connection to server at "ec2-34-???-??-???.compute-1.amazonaws.com" (34.201.??.???), port 22 failed: could not read root certificate file "/Users/me/timescale.pem": no certificate or crl found
Ok...then i switched ports and added '5432' and get this warning:
connection to server at "ec2-??-???-??-???.compute-1.amazonaws.com" (34.???.??.212), port 5432 failed: Connection refused
Is the server running on that host and accepting TCP/IP connections?
When i ssh into my terminal and type: netstat -nl |grep 5432 i get the following:
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN
unix 2 [ ACC ] STREAM LISTENING 812450 /var/run/postgresql/.s.PGSQL.5432
Can someone please help? Thanks
The .pem file is for connecting to the EC2 instance over SSH, on port 22. PostgreSQL is running on port 5432. You don't use the .pem file for database connections, only for ssh connections. You need to change your Python script to use port='5432', to connect directly to the PostgreSQL service running on the EC2 instance.
This seems to work now:
from sshtunnel import SSHTunnelForwarder
mypkey = paramiko.Ed25519Key.from_private_key_file(path_to_secret_key)
tunnel = SSHTunnelForwarder(
('remote_public_port', 22),
ssh_username='ubuntu',
ssh_pkey=mypkey,
remote_bind_address=('localhost', 5432))
tunnel.start()
conn = psycopg2.connect(
dbname='example_db',
user='postgres',
password='secret123',
host='127.0.0.1',
port=tunnel.local_bind_port)

Websocket Autobahn Python client: how to connect to server using server and client certificates?

A websocket client (using Autobahn/Python and Twisted) needs to connect to a websocket server: the client needs to present its client certificate to the server and the client needs to check the server's certificate. These certificates have been created, for instance, during setup of a Kubernetes minikube installation. In particular:
server certificate ~/.minikube/ca.crt (in X509 format from what I understand).
client certificate ~/.minikube/client.crt with key ~/.minikube/client.key.
I've checked that I can successfully use these certificates+key to issue Kubernetes remote API calls using curl.
From Autobahn's echo_tls/client.py example I understand that I may need to use a ssl.ClientContextFactory(). ssl here refers to the pyopenssl package that twisted automatically imports.
However, I cannot figure out how to pass the certificates to the factory?
How do I tell the websocket factor to present the client certificate to the server?
How do I tell the websocket to check the server's certificate in order to detect MITM attacks?
After some trial and error I've now arrived at this solution below. To help others I'll not only show code, but also a reference setup to test drive the example code.
First, install minikube, then start a minikube instance; I've tested with minikube 1.0.0, which then runs Kubernetes 1.14 which was current at the time of this writing. Then start a simple websocket server that just shows what is sent to it and will sent back any input you made to the connected websocket client.
minikube start
kubectl run wsserver --generator=run-pod/v1 --rm -i --tty \
--image ubuntu:disco -- bash -c "\
apt-get update && apt-get install -y wget && \
wget https://github.com/vi/websocat/releases/download/v1.4.0/websocat_1.4.0_ssl1.1_amd64.deb && \
dpkg -i webso*.deb && \
websocat -vv -s 0.0.0.0:8000"
Next comes the Python code. It attempts to connect to the wsserver we've just started via Kubernetes' remote API from the minikube, using the remote API as its reverse proxy. The minikube setup usually uses mutual SSL/TLS authentication of client and server, so this is a "hard" test here. Please note that there are also other methods, such as server certificate and bearer token (instead of a client certificate).
import kubernetes.client.configuration
from urllib.parse import urlparse
from twisted.internet import reactor
from twisted.internet import ssl
from twisted.python import log
from autobahn.twisted.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS
import sys
if __name__ == '__main__':
log.startLogging(sys.stdout)
class EchoClientProto(WebSocketClientProtocol):
def onOpen(self):
print('onOpen')
self.sendMessage('testing...\n'.encode('utf8'))
def onMessage(self, payload, isBinary):
print('onMessage')
if not isBinary:
print('message %s' % payload.decode('utf8'))
def onClose(self, wasClean, code, reason):
print('onClose', wasClean, code, reason)
print('stopping reactor...')
reactor.stop()
# Select the Kubernetes cluster context of the minikube instance,
# and see what client and server certificates need to be used in
# order to talk to the minikube's remote API instance...
kubernetes.config.load_kube_config(context='minikube')
ccfg = kubernetes.client.configuration.Configuration._default
print('Kubernetes API server CA certificate at %s' % ccfg.ssl_ca_cert)
with open(ccfg.ssl_ca_cert) as ca_cert:
trust_root = ssl.Certificate.loadPEM(ca_cert.read())
print('Kubernetes client key at %s' % ccfg.key_file)
print('Kubernetes client certificate at %s' % ccfg.cert_file)
with open(ccfg.key_file) as cl_key:
with open(ccfg.cert_file) as cl_cert:
client_cert = ssl.PrivateCertificate.loadPEM(cl_key.read() + cl_cert.read())
# Now for the real meat: construct the secure websocket URL that connects
# us with the example wsserver inside the minikube cluster, via the
# remote API proxy verb.
ws_url = 'wss://%s/api/v1/namespaces/default/pods/wsserver:8000/proxy/test' % urlparse(ccfg.host).netloc
print('will contact: %s' % ws_url)
factory = WebSocketClientFactory(ws_url)
factory.protocol = EchoClientProto
# We need to attach the client and server certificates to our websocket
# factory so it can successfully connect to the remote API.
context = ssl.optionsForClientTLS(
trust_root.getSubject().commonName.decode('utf8'),
trustRoot=trust_root,
clientCertificate=client_cert
)
connectWS(factory, context)
print('starting reactor...')
reactor.run()
print('reactor stopped.')
The tricky part here when attaching the client and server certificates using optionsForClientTLS is that Twisted/SSL expects to be told the server's name we're going to talk to. This is also needed to inform virtual servers which one of their multiple server certificates they need to present -- before there will be any HTTP headers!
Unfortunately, this is now ugly territory -- and I would be glad to get feedback here! Simply using urlparse(ccfg.host).hostname works on some minikube instances, but not on others. I haven't yet figured out why seemingly similar instances behave differently.
My current workaround here is to simply use the CN (common name) of the subject from the server's certificate. Maybe a more robust way might be to only resort to such tactics when the URL for the remote API server uses an IP address literal and not a DNS name (or at least a label).
Alas, run the Python 3 code above python3 wssex.py. If the script correctly connects, then you should see a log message similar to 2019-05-03 12:34:56+9600 [-] {"peer": "tcp4:192.168.99.100:8443", "headers": {"sec-websocket-accept": ...
Additionally, the websocket server that you've started before should show log messages such as [INFO websocat::net_peer] Incoming TCP connection from Some(V4(172.17.0.1:35222)), and some more.
This then is proof that the client script has successfully connected to minikube's remote API via a secure websocket, passing authentication and access control, and is now connected to the (insecure) websocket demo server inside minikube.

mosquitto_sub Error:A TLS error occurred but is ok whit --insecure

I'm building a mqtt server. I used the mosquitto with the TLS on the server as a broker.
I encountered this problem:
I created the ca.crt, server certificate, server key, client certificate, client key via generate-CA.sh
I can connect the broker and publish and subscribe msg via MQTT.fx, but when I tried to connect the broker with the mosquitto_sub, it came out Error:A TLS error occurred on the client PC(ubuntu), at the same time, the server prints
New connection from xx.xx.xx.xx on port 8883.
Openssl Error: error:14094416:SSL routines:SSL3_READ_BYTES:sslv3 alert certificate unknown
Openssl Error: error:140940E5:SSL routines:SSL3_READ_BYTES:ssl handshake failure
my command used is:
mosquitto_sub -p 8883 -i test -t mqtt -h 150.xx.xx.xx --cafile ca.crt --cert xx.crt --key xx.key in which, the 150.xx.xx.xx is the IP of my broker.
when I used the option --insecure with the command above, the problem disappeared.
so I think it is the server hostname which leads to this problem.
In the mosquitto_sub command the option -h specifies the hostname, but i need to use this parameter to point to the IP address of my broker, so how could i specify the hostname of my server??
Old question but perhaps this might help someone:
If the --insecure option makes it work, you have a certificate problem. What hostname did you set whilst signing the certificate? What does openssl s_client -showcerts -connect 150.xx.xx.xx:8883 say?
Related: although it should be possible to use SSL certs for your servers using public IP addresses (see Is it possible to have SSL certificate for IP address, not domain name?), I'd recommend not doing this and just using DNS, even if this means server.localdomain and/or editing your /etc/hosts file if necessary.

GET https://mydomain.server:6001/socket.io/socket.io.js net::ERR_CONNECTION_REFUSED

I'm trying to work with socket.io first time.
I'm using Laravel 5.4, Laravel Echo and NodeJs server for Laravel Echo broadcasting with Socket.io..
I managed to make it work on my local as it supposed to work. But on server I get
an error
GET https://mydomain.server:6001/socket.io/socket.io.js net::ERR_CONNECTION_REFUSED
I do run the server before. I write correct address.
May be browser doesn't have access to that port or something?
Also I tried to access to the file with
curl -X GET https://mydomain.server:6001/socket.io/socket.io.js command.
But got
curl: (35) gnutls_handshake() failed: The TLS connection was non-properly terminated.
I found out the solution.
Just set protocol, sslCertPath and sslKeyPath parameters in laravel-echo-server.json
"protocol": "https",
"sslCertPath": "path_to/server.crt",
"sslKeyPath": "path_to/server.key"
May be this will help you.
Firstly when you are connecting to socket and site is having ssl installed then use :
socket = io.connect('http://example.com:port', {'force new connection': true, 'secure': true});
Include above part in your script part on client side.
Secondly on server side in your for eg : server.js say use
var https = require('https');
var server = https.createServer({
key : fs.readFileSync('./sslkey.key'),
cert : fs.readFileSync('./ssl_certificate.crt')
},app);

Using Navicat to login to Postgresql via SSH - what are the correct settings?

I am trying to log into PostgreSQL on my EC2 server via SSH using Navicat.
I get the following error message:
"80070007: SSH Tunnel: Socket error on connecting. WSAGetLastError return 10061($274D)"
On the server, the "role" postgres already exists, and there is already a database called postgres. I have assigned a password to postgres (using ALTER NAME command via Putty).
The SSH settings I am using in Navicat are:
Port: 5432
User Name: [admin user name]
Authentication Method: Public Key
The Connection settings are:
Host Name: localhost
Port: 3306
Initial Database: postgres
User Name: postgres
Password: [password]
When I connect to the MySQL server on the same machine, the settings are exactly the same except for:
SSH to Port 22
User Name (for connection): root (with corresponding password)
I have tried the SSH to port 22, in which case the error message is:
"could not connect to server: Connection refused (0x0000274D/10061)
Is the server running on host "localhost" and accepting TCP/IP connections on port 60122?
received invalid response to SSL negotiation:4"
Any ideas on what settings I need to change to get this to work?
Your config seems to be very wrong.
ssh port should be not 5432, but 22 (ssh default).
postgresql port should be not 3306 (this is actually MySQL), but 5432 (postgres default)
To verify your setup, try ssh-ing into your EC2 instance manually.
After you ssh in, check if you can execute "telnet localhost 5432".
If you see an error immediately, that would mean that postgres server is not running.
If you see nothing - this is good sign and means that Postgres is running.
You can quit from this by Ctrl-], q, Enter.
Note that EC2 instances may require you to use ssh public key authentication (not a password). In this case, you will have to find option in Navicat to provide such a key.

Resources