Having a TCP connection behind a URL with a path Nodejs - javascript

I want to have an HTTP server which allows me to establish a TCP connection with a remote computer whenever I specify a given path inside the URL in a browser.
For example... let's say that I have a computer with a public IP address and a hostname as the server (mydomain.com:9000) and a remote computer with a local IP address (192.168.0.1) connected to the server. Somehow I want to establish a TCP connection between a client and the remote computer. And to make it easier just enter an URL with a path (mydomain.com:9000/remote). NOT WITH ANOTHER PORT! Also, I want the connection to be done if and only if that path is entered.
To illustrate a little better how the system works:
_________ _________ _________
| | internet | | local network | |
|_______| ----------> |_______| ----------> |_______|
____|____ <---------- ____|____ <---------- ____|____
client server remote computer
sends http request checks path gets http request
mydomain.com:9000/remote establishes TCP connection handles request
I made some code with node js to make sure that the connection was possible. And it is, now I only have to be able to check the path and establish the connection only then. My code is a simple TCP tunneling proxy and it works fine. But when I try to implement the path it doesn't.
//Import modules
const net = require('net');
const http = require('http');
let inpath = false;
// Create proxy server
const proxy = http.createServer( function(req,res){
console.log(req.url);
//Check path
if(req.url == '/remote')
inpath = true;
});
proxy.on('connection', client => {
if(inpath)
{
const remote = new net.Socket();
//Establish connection
remote.connect(80,'192.168.0.100');
client.on('error', err => {
console.log('Error: ' + err);
proxy.close();
});
//Port-forwarding
client.pipe(remote);
remote.pipe(client);
}
});
proxy.listen(9000);
Is there a way to do this?

In your code, you'll find that the connection event is firing well before your request handler callback is being called. This is because a TCP connection (for which the connection event is triggered) occurs from a client connecting before the client makes its HTTP request. Additionally, what you have isn't really going to work reliably because HTTP keep-alive may mean that multiple requests will come down a single TCP connection.
To fix this, you need to move all of that code you have in your connection event up to your HTTP request handler.
You'll run into other problems though, in that now your code is effectively handling the HTTP request/response, so you'll need to parse and rebuild the request/response data when communicating with the upstream server.
If you truly do wish to handle this at the TCP level, don't use http.createServer(). Use net instead and create a normal TCP server. Then, parse the request data yourself and if you find the certain path you want in the request, make the proxying TCP connection, and be sure to send down the existing buffers so that the upstream server can get the full HTTP request.

Related

Node.js Restrict HTTP server to be accessible only on 127.0.0.1

I have created a ws websocket server in nodejs and a http server that is listening on a specific port and host is 127.0.0.1. WebSocket server connection is established on the upgrade event emitted by the http server.
For security purpose I want the server to be accessible only on localhost/127.0.0.1 and not from 0.0.0.0 IP.
Example:
ws://0.0.0.0:5050 - Should not accept connections
ws://127.0.0.1:5050 - Should accept connections
How can I restrict the server to be only reachable from localhost and not from any other IP(including 0.0.0.0)?
const server = http.createServer();
const wss = new WebSocket.Server({ noServer: true });
server.listen(5050, '127.0.0.1');
server.on('upgrade', function (request, socket, head) {
wss.handleUpgrade(request, socket, head, function (ws) {
//conditional checks
wss.emit('connection', ws, request);
})
})
Can somebody please direct me to the proper way of doing this.
if(req.socket.remoteAddress !== '127.0.0.1'){
res.send('403 Access Denied');
res.end();
} else {
// allow access
}
0.0.0.0 isn't an IP address. It is a non-routable meta-address used to designate an invalid, unknown or non-applicable target.
You might have a client that treats requests to 0.0.0.0 as being intended for localhost, but that isn't a security problem since your server still won't be accessible outside of the current machine.
Your existing code, where you only listen on 127.0.0.1 is sufficient.
Access and security that you are talking about are supposed to be done at the firewall of your instance hosting nodejs server.
However, if you still want to implement it on your nodejs layer you can dig the request object there you will find the IP address from where the request is coming, and based on that you can take your action.
Hope it helps, Thank you

Socket state pending forever in Javascript and Node

// client.js
const webSocket = new WebSocket('wss://127.0.0.1:8081');
webSocket.addEventListener('open', (message) => {
alert(message);
})
const text = document.getElementById('text');
const button = document.getElementById('button');
button.onclick = () => {
webSocket.send(text.value);
}
// server.js
import { Server } from 'net';
const server = new Server();
server.on('connection', (socket) => {
console.log(socket);
socket.on('data', (data) => {
})
});
server.listen(8081, '127.0.0.1', () => {
console.log('Server running at port 8081');
});
The problem that I'm facing is:
When I load the page the socket is printed in the console of the server (I can get the remote address and remote port)
No matter what I do, the connection in the client is always pending. I tried Chrome, Brave, Mozilla and Safari and no one seems to work.
What am I missing? I tried to not send the message until the connection is ready, but I never get that state in my PC. The alert in the client never pops, even if I establish the onopen property without an event.
Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
at button.onclick
A webSocket client must connect to a webSocket server. webSocket is a specific connection algorithm (that starts with an http request and then switches to the webSocket protocol) and a specific data format. So, a webSocket client must have a webSocket server to connect to. Your server is a generic TCP server that does not support webSocket connections.
The client will initially establish a TCP socket to your server, then send an http request over that socket, and then it will sit there waiting for an appropriate response to the http request it sent. Since your server never sends that response, it will just sit there waiting until eventually it will timeout and close the socket. This is why you see it stuck in the pending state. It's still waiting for the rest of the webSocket connection sequence to happen and it never does.
If you're curious how the webSocket connection scheme and data format work, you can see a nice description of it here.
Relevant StackOverflow answers:
How does WebSockets server architecture work?
What's the difference between WebSocket and plain socket communication?
Do websocket implementations use http protocol internally?
Overview of webSocket connection scheme
How WebSocket server handles multiple incoming connection requests?

Keep Node JS connection opened and write (GET Request)

I was trying to implement a NODE JS get method where I could encode in the url parameters and send back responses like in Server Sent Events.
For example, when I used:
curl -D- 'http://localhost:8030/update'
The server would return a message, and then keep the connection opened to return more messages (like Push).
I was using require('connect'), I tried with require('express') but can't get it working.
Here's part of my code:
var http = require('http');
var connect = require('express');
var app = connect();
app.use(bodyParser.urlencoded({ extended: false }))
.use(bodyParser.json()) // JSON
.use(cors(corsOpts))
.get('/update', updateMiddleware);
var server = http.createServer(app);
server.listen("twserver.alunos.dcc.fc.up.pt", 8030);
function updateMiddleware(req, res) {
res.setHeader('Connection', 'keep-alive');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.writeHead(200);
setTimeout(function() {
res.write("this is an event");
res.flushHeaders();
}, 1000);
setTimeout(function() {
res.write("this is another event");
}, 2000);
// should print without ending
}
EDIT: I found it was working, but only in chrome. In terminal, I only receive it after waiting a long time, and messages come like in chunks.
You can't use a single HTTP request to listen for multiple event data. If you are really stuck with HTTP (i.e. WebSocket or WebRTC is not an option) then the technique you are looking for is called long polling. This basically works this way:
Client sends request to server
Server waits until an event happens (or until a specific but not too long timeout, so the client application does not throw a timeout error for the request)
Server responses with a complete http response, containing the details of the event
Client receives the event details and immediately sends another request to listen to further events.
This method really takes advantage of HTTP Keep-Alive
EDIT:
For me it looks like your code does not follow the protocol of server sent events. Here is a tutorial: Server-Sent Events in nodejs.
Following another tutorial on MDN about Server-Sent Events, the structure of the messages should be the following:
: this is a test stream
data: some text
data: another message
data: with two lines
Note that the data to be sent must be followed by a double new-line \n\n.
In general, http endpoints in Express aren't supposed to do things like that. If you want live event data, the best way is to use a web socket.
That being said, this thread has an example on how to force Express to do this.
socket.io or Webrtc is the best choice

NodeJS and web sockets: Check if socket origin is the same as the web socket server

Edit: I'm sending data from app A to app B over web socket, where A is a router app for something else, and B is my web app. When B receives the data, it should send it to any client viewing its home page, also over web socket. But, since the roter app and the home page clients are connected to the same web socket server, I don't know which connections are to the clients viewing the home page, and which connections are to other stuff, like my router. Only the home page clients should receive the data.
I basically want to pass the logging data recieved from my router to my home page in real time so I can view it.
========
I have an express app that server a simple html page. It runs this script:
var host = window.document.location.host.replace(/:.*/, '');
var ws = new WebSocket('ws://' + host + ':5000');
ws.onmessage = function (event) {
console.log(JSON.parse(event.data));
};
In the nodejs backend, I have a simple ws server running, listening for connections:
var WebSocketServer = require("ws").Server;
module.exports.init = function(server) {
var wss = new WebSocketServer({ server: server });
wss.on('connection', function (ws) {
...
});
};
I get connections from, at the moment, 2 different locations.
A router app I have running that is sending this web app logging messages.
The html-page that this web app is serving.
I want to pipe the data from the router app to my html page, but to do that I need to know which of my connections I need to pipe the data to. I can in theory have many connection, but only one of them, at least for now, should be passed the data after it is recieved.
I thought I could compare the origin of the web socket connection to the domain of the web server the web socket server ran on.
I can get the origin of the connection like this: ws.upgradeReq.headers.origin. That will return e.g: localhost:5000. But I don't know the domain name where my web socket server is running. I've tried to google, and it seems like to get the domain name, I need to get it from an http request. What I am looking for is something that just gives me the name, without having to wait for an http request.
I've tried os.hostname(), but it doesn't give me the results I need.
I've also tried server.address(), where server is var server = require("http").createServer(app);, but that gives me this: { address: '::', family: 'IPv6', port: 5000 }.
Isn't there just a way to get the host and port? Can I somehow use the address part above to get the host name?
The web app will probably run on Heroku.
Based on your recent comments, it sounds like each browser client that connects a webSocket should just tell your server what web page it is looking at with an initial message and the server should keep track of that for each active connection.
In socket.io (built on top of webSockets), you could just connect to the /homepage namespace and then that server could broadcast to all sockets connected to that namespace. You could, of course, implement that type of functionality yourself with a plain webSocket.
Then, your server would not only have a list of connected sockets, but could also know what page they were all from. That would allow you to broadcast based on current page. Your server-to-server webSocket would not have sent a message that it's from the home page, so it would not be tagged as such and you could avoid sending to it.
You might find socket.io easier to use for all of this. In additon to namespaces on both client and server, it also gives you automatic reconnection from browsers, a simpler message passing system, server-side broadcast to namsepaces and so on.

How to use node-http-proxy as a proxy server for multiple secure servers

I currently have a HTTPS web server listening on port 443 on my host machine.
My goal is to set up another HTTPS web server on the same host machine, change ports on both web servers, and then set up a proxy server using node-http-proxy listening on port 443 instead. The proxy server then delegates requests based on custom logic to the servers on other ports.
Below is the proxy server I adapted from one I successfully use when proxying plain HTTP requests on port 80. However, when I try to run this code the browser displays the message 'Secure Proxy Server unable to handle your request at this time.' and console logs '[Error: UNABLE_TO_VERIFY_LEAF_SIGNATURE]' It does make it to the point where it tries to proxy the request to the server listening on a different port.
var sugar = require('sugar')
var url = require('url')
var https = require('https')
var httpProxy = require('http-proxy')
var fs = require('fs')
//configure proxy
var ssl proxy = httpProxy.createProxyServer({
ssl: {
key: fs.readFileSync('/cert/server.key', 'utf-8'),
cert: fs.readFileSync('/cert/mydomain.crt', 'utf-8')
}
})
sslproxy.on(
'error',
function(err, req, res) {
console.log(err)
res.end('Secure Proxy Server unable to handle your request at this time.')
}
)
//configure and start server that uses proxy
var credentials = {
key: fs.readFileSync('/cert/server.key','utf-8'),
cert: fs.readFileSync('/cert/mydomain.crt', 'utf-8')
}
var sslserver = https.createServer(
credentials,
function(req, res) {
console.log("Received request on secure proxy server")
var target = url.parse(req.url)
if(target.pathname.startsWith('/version1')) {
console.log("Forwarding request to port 444")
sslproxy.web(req, res, {target: 'https://localhost:444'})
} else {
console.log("Forwarding request to 445")
sslproxy.web(req, res, {target: 'https://localhost:445'})
}
}
)
sslserver.listen(443)
Couple thoughts:
I tried using node-ssl-root-cas as indicated in the answer to another question, but nothing appeared to change. My SSL certificate is from Network Solutions.
The targets for my proxy are localhost:444 and localhost:445 because those ports are not open externally and cannot be. Not sure if the localhost in the host name is affecting the https proxy.
Try this: process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
And look at this:
// AUTHENTICATION MODES
//
// There are several levels of authentication that TLS/SSL supports.
// Read more about this in "man SSL_set_verify".
//
// 1. The server sends a certificate to the client but does not request a
// cert from the client. This is common for most HTTPS servers. The browser
// can verify the identity of the server, but the server does not know who
// the client is. Authenticating the client is usually done over HTTP using
// login boxes and cookies and stuff.
//
// 2. The server sends a cert to the client and requests that the client
// also send it a cert. The client knows who the server is and the server is
// requesting the client also identify themselves. There are several
// outcomes:
//
// A) verifyError returns null meaning the client's certificate is signed
// by one of the server's CAs. The server know's the client idenity now
// and the client is authorized.
//
// B) For some reason the client's certificate is not acceptable -
// verifyError returns a string indicating the problem. The server can
// either (i) reject the client or (ii) allow the client to connect as an
// unauthorized connection.
//
// The mode is controlled by two boolean variables.
//
// requestCert
// If true the server requests a certificate from client connections. For
// the common HTTPS case, users will want this to be false, which is what
// it defaults to.
//
// rejectUnauthorized
// If true clients whose certificates are invalid for any reason will not
// be allowed to make connections. If false, they will simply be marked as
// unauthorized but secure communication will continue. By default this is
// false.
//
Both, solution and additional infos, are from here: Node.js HTTPS 400 Error - 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'

Categories