MQTT TLS connection - javascript

I would like to connect a test MQTT-Client to my Node.js application as a MQTT-Broker. I am using the aedes library from moscajs
My MQTT-Client is the tool "MQTT-Explorer" and here is my Node.js application:
const fs = require('fs');
const aedes = require('aedes')();
const options = {
key: fs.readFileSync('certs/server_key.pem'),
cert: fs.readFileSync('certs/server_cert.pem'),
};
// const server = require('net').createServer(aedes.handle);
const server = require('tls').createServer(options, aedes.handle);
const PORT = 8881;
server.listen(PORT, () => {
console.log(`server is up and running: PORT [${PORT}] - ID [${aedes.id}]`);
});
I can connect without any problems to PORT=1881 with const server = require('net').createServer(aedes.handle) and I also can connect to PORT=8881 with const server = require('tls').createServer(options, aedes.handle)
With the Tool xca-2.4.0.msi XCA 2.4.0 I have created a ca.pem CERTIFICATE File and a CERTIFICATE server_cert.pem and a server_key.pem PRIVATE KEY (signed from ca.pem) as a Server. The key for CA and the Server are different:
For my MQTT-Client, under ADVANCED, CERTIFICATES, SERVER CERTIFICAT (CA) I selected the ca.pem File. If I select "Encryption", it works. But if select "validate certificate", error: Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the certs list
Unfortunately I don't know what I'm doing wrong, thanks in advance :(

MQTT Explorer is built using Node.js and the MQTT library MQTT.js. As per this issue:
Node.js requires the IP address to be in the subjectAltNames for the cert and not in the CN. Maybe MQTT.fx isn't requiring that, but it should.
and:
If your server's certificate says CN=localhost in the Subject field, connect using localhost and not 127.0.0.1 and it should work. If it says CN=127.0.0.1, you have to make a new one because Node.js won't validate the IP address unless it's in the SAN extension. There is a way to work around it in code (I think it's an option called checkServerIdentity), but I would prefer to fix my certificate if I had this problem.
A rationale for the approach taken in Node is set out in this answer which includes the following quote from RFC2818: HTTP Over TLS
:
In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.
As you are using MQTT over TLS (as opposed to HTTP Over TLS) you could argue that the above does not apply but, given that the main use of the TLS library is for HTTP traffic, it makes sense that it confirms to the RFC by default.
You have a few options including:
Use a hostname (e.g. localhost) rather then an IP when creating the certificate/connecting.
Add the IP as a subjectAltName
Modify the library to use a noop checkServerIdentity (see this answer).
Use another application for testing (not really recommended as some applications will work and others will not). The issue quoted above mentions that MQTT.fx works.

Related

Problem in communicating with Binance exchange in NodeJS web socket

When I execute the following piece of code in the app.js file
const WebSocket = require('ws');
const ws = new WebSocket('wss://stream.binance.com:9443/ws/!miniTicker#arr');
ws.on('message', function (data){
//const result = JSON.parse(data);
console.log(data);
});
I get the following error and I cannot receive data.
10.10.34.34 (what your DNS server resolved stream.binance.com to) is a local IP address! You are not even reaching Binance. It's probably a captive portal or some firewall.
Open that IP address in the browser and check whether you get some sort of web interface that gives you a clue about what is blocking your request. You can also try changing your DNS server to Google (8.8.8.8) or some other public DNS server instead of relying on your router.
Update:
In your comment you mentioned your DNS server is 178.22.122.100 (i.e., shecan.ir). If I try to resolve the domain with that server, I also get nonsensical results (10.10.34.34 and d0::11). According to this book, this is part of Iranian censorship, and your ISP would handle this IP address to show a page about this domain being blocked if you'd open it in a browser.
So, an alternative public DNS server without censorship such as 8.8.8.8, 1.1.1.1 or 9.9.9.9 should help. If this doesn't work then those DNS servers are also blocked by your ISP and you would need to use a VPN or other means of bypassing government censorship.

Making Request from Localhost:3000 says NETWORK FAIL to React Native Project

I had watched a series of tutorials online and had followed lot of steps. I had to change my IP address to my machine IP, but yet network error. even, I had to change it to 127.0.0.1. Am just so tired, I need help badly. I am using a project on the PHPMyAdmin database, which I use to fetch the result using express and node MySQL. I did all I could I even used USB tethering, hotspot, and all but none seems to work
const GetUserData =()=>{
axios.get("http://127.0.0.1:3000/users").then(
(response)=>{
console.log(response.data);
}).catch(err =>{
console.log(err);
})
};
useEffect(() => {
GetUserData()
},[])
Can you add the error message, please? If you face CORS you can fix it by:
var cors = require('cors')
var app = express()
app.use(cors())
Your machine has multiple IP addresses, even with one network device.
Of them:
127.0.0.1 - local IP address. It is not on any network device, and it should not be there.
192.168.x.y - network IP address. It is configured on the network device (manually or automatically).
You don't need to install 127.0.0.1 on a network device. 127.0.0.1 is already your machine, even without any network devices.
Also, to be able to make requests to 127.0.0.1:3000, some service must already listen on this port.

How to authenticate ngrok password automatically using Python and or Javascript?

I am using ngrok to expose my localhost on a raspberry pi, and ngrok is password protected. I am using Django on another computer outside my network to access a webpage(simple server) on this raspberry pi. I don't want to manually type in the username and password to ngrok.
How can I automatically fill in the ngrok user/password, which is a popup in chrome asking for user and password?
What I've tried:
I first tried using JS in my template, just using a fetch request:
https://user:password#myngrokdomain.ngrok.io but chrome threw an error in the console saying I can't pass in credentials in plain text like this, rightfully so...
I then tried to use python requests:
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
r = client.post(loginURL, data=login_data)
This returned a 401 access denied
r.headers + r.reason returns:
401 Unauthorized Unauthorized {'Content-Length': '16', 'Content-Type': 'text/plain', 'Www-Authenticate': 'Basic realm="ngrok"', 'Date': 'Tue, 16 Mar 2021 15:22:15 GMT'}
The authentication method used by ngrok is called HTTP Basic Auth. To use it with the requests library you pass login and password as a tuple in the auth argument:
r = client.post(loginURL, auth=(UN, PWD))
Docs: Basic Authentication — Requests
Try doing a get on the login page first. Perhaps it's setting some cookies that it expects to be present on the post :
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
client.get(loginURL )
r = client.post(loginURL, data=login_data)
You need to distinguish whether you're making the request from a browser or from a server application.
Browser
If you're doing it from a browser you're hitting a CORS issue and ngrok doesn't support that when providing --auth. From the docs:
you cannot use ngrok's -auth option. ngrok's http tunnels allow you to specify basic authentication credentials to protect your tunnels. However, ngrok enforces this policy on all requests, including the preflight OPTIONS requests that are required by the CORS spec. In this case, your application must implement its own basic authentication
In this case, your only option is to implement authentication in your application instead of using ngrok's --auth.
Server
If you're sending the request from a server application you won't get into any CORS issue but you need to provide the Basic Authentication credentials properly.
Say you have your application exposed via ngrok at http://myapp.ngrok.io protected via --auth user:pass.
In plain Node.js you would do something like this:
const http = require('http')
http.get('http://myapp.ngrok.io', { auth: 'user:pass' }, res => {
const chunks = []
res.on('data', chunk => {
chunks.push(chunk)
})
res.on('end', () => {
console.log(Buffer.concat(chunks).toString('utf-8'))
})
})
Note that to hit the https url you would use Node's https module instead of http, or a higher level library that handles that for you, like node-fetch.
In Python you can do something similar and this question will probably get you on the right path.

Getting server certificates from https/ws server automatically

I'm hosting a website on git pages that opens a video stream on certain clicks over a secure websocket (wss) using this library: github.com/websockets/ws. When connecting video from an http page over a simple websocket (ws) everything was fine, but I realized the github page will be hosted over https and firefox does not allow unsecured websocket connections over https, wss is required.
To do that I followed the steps on the websockets ws page and the websocket server is sort of "hiding" behind an https server that does the certificate handshake. The problem is this: If I use my web page normally and open the link that starts a wss connection to the video it doesn't work, the server doesn't know it exists. If I first visit the server directly via IP and port using an https://IP:999 link it retrieves the certs and from then on the website works indefinitely.
Is there a way to do this naturally that doesn't require me trying to visit a headless server to do the cert handshake? I just want to open a wss connection but the overhead of having to visit a server directly for certs seems a bit bizarre.
The server setup looks like this:
const server = https.createServer({
cert: fs.readFileSync('./cert/cert.pem'),
key: fs.readFileSync('./cert/key.pem'),
}, function (req, res) {
console.log(new Date() + ' ' +
req.connection.remoteAddress + ' ' +
req.method + ' ' + req.url);
res.writeHead(200);
res.end("hello foobarbackend\n");
});
this.wsServer = new ws.Server({
server
})
this.wsServer.on("connection", (socket, request) => {
return this.onSocketConnect(socket, request)
})
server.listen(9999, '0.0.0.0');
Once the certs are retrieved via https://:9999 I can then play videos no problem on that browser at wss://:9999, I must be missing something.
The question here is answered entirely by the world of SSL/TLS. The issue here was that secure browsers will pretty much silently reject WSS:// connections to servers with self signed certificates. My certificates were self signed.
This is why the user would have to navigate to the server IP directly over HTTPs first and then accept the warnings. From there it's business as usual.
What needed to be done was register a domain name for the IP the server was located on (Droplet). Then certbot was used to generate real certificates (key, cert) for the domain. I replaced the cert.pem and key.pem above with the true generated ones. Domain name can be anything like mywebsitewhatever.app.
Now on the client side you can open up a connection to wss://mywebsitewhatever.app:9999 and the browser will accept it automatically and things work. No warnings or navigation to a warning page to accept.

Only allow computers on the same network using Express-ip-filter

So I'm using localtunnel to expose my ports over the internet, but I only want to let devices on the same network as the server access the server.
I'm using express-ip-filter to filter away anything that's on a different network. I tried a few things: first I tried using 192.168.1.0/24 as the only ips that could access the website, but that didn't work, as it didn't let anything in. I then tried using the ip I got from WhatsMyIp, but that wouldn't let any device in. I then found out that express-ip-filter spits out a message saying that a certain ip was not allowed and, on every device, independently on the network it was connected to, the address was 127.0.0.1. I tried confirming by only allowing 127.0.0.1, and then every device could access the server. Why would ip-filter only get 127.0.0.1 as ip? Here's my code as a reference:
// Init dependencies
var express = require('express'),
ipfilter = require('express-ipfilter').IpFilter
app = express()
// Blacklist the following IPs
var ips = ['192.168.1.0/24']
// Create the server
app.use(ipfilter(ips, { mode: "allow" }))
app.get('/', function (req, res) {
res.send('Hi')
})
app.listen(8080, () => console.log('Up'))
From my limited understanding of localtunnel it seems like it proxies users requests to you via the localtunnel software which causes all users to have the same IP. In laymans terms:
User connects to your site through localtunnel
localtunnel copies the users request and sends it to your computer
Your application receives the request but it looks like all traffic is coming from localtunnel because it's incredibly difficult if not impossible for localtunnel to imitate someone else's IP.
Why use localtunnel at all if you only want devices on the same network to connect, you don't need to do any port forwarding or DNS setup if you just want to access another machine on the same local network.
If you really do need to tunnel connections then there is a solution, not with localtunnel(Which as far as i can tell does not use forwading headers, although if someone knows if they do ill change my answer) but using https://ngrok.com instead which does exactly the same thing but also sends a little extra bit of data in every request which tells the application what the clients actual IP is.
Install ngrok
Run ngrok http -subdomain=(the subdomain you want) 80
Edit your application code to find the real client IP
var findProxyIP = function(req) {
var realIP = req.header('x-forwarded-for');
return realIP;
}
app.use(ipfilter(ips, {
mode: "allow",
detectIP: findProxyIP
}));
ngrok is much more complex and has a lot more features compared to localtunnel, however, it is freemium software and its free plan is quite limiting.

Categories

Resources