Node.JS Low Level Tls HTTP Proxy Tunnel - node.js

Introduction
I hope to issue raw https requests through a http proxy tunnel. I've had problems because of bad requests by which the server could not understand.
These raw https requests include first connecting to example.com in the proxy, and then posting data to it.
var http = require("http");
var tls = require("tls");
let proxyhost, proxyport;
http.request({
host: proxyhost,
port: proxyport,
method: 'CONNECT',
path: `www.example.com:443`,,
headers
}).on('connect', (res, socket) => {
if (res.statusCode === 200) {
const conn = tls.connect({
socket: socket,
rejectUnauthorized: false,
servername: 'www.example.com'
}, () => {
conn.on('data', (data) => {
console.log("chunk:", data.toString('ascii'));
})
const args = [
'CONNECT www.example.com:443 HTTP/1.1',
'Host: www.example.com:443',
'Proxy-Connection: Keep-Alive',
'',
'POST / HTTP/1.1',
'Host: www.example.com',
'accept-encoding: deflate, gzip, br',
'accept: application/json, text/javascript, */*; q=0.01',
'content-type: application/text',
'origin: https://www.example.com',
'accept-language: en-US,en;q=0.9',
'content-length: 10',
'xxxxxxxxxx' // this is supposed to be the post body
];
// conn.write("GET / HTTP/1.1\r\nconnection: keep-alive\r\nhost: www.example.com\r\n\r\n", "ascii")
conn.write(args.join("\r\n"));
});
} else {
console.log('Could not connect to proxy!');
}
}).end();

Related

Set session id in cookie when consuming request with axios or node request

I'm trying to consume a request with axios, where I have some parameters and also a session id, my session id is this (raw cookie string): JSESSIONID = 0000G7xbL5PbWrvMXfPmLBLyE3u: 17qkn1utc; Path = /; HttpOnly; Domain = external.segurossura.com.br.
Where I get a timeout error. I think the error is in the way I'm passing the Cookie, as I'm not passing it as a session.
const axios = require("axios");
const params = new URLSearchParams();
const response = await axios.post(myurl, params, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Cookie: "JSESSIONID=0000PiVra7dIxpkhWcj0wRG3S01:17o9k4ahg",
},
});
console.log(response);
Error
Error: connect ETIMEDOUT ip:443
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1148:16) {
errno: -110,
code: 'ETIMEDOUT',
syscall: 'connect',
address: 'ip',
port: 443,
config: {
url: 'https://....my url....',
method: 'post',
data: '',
headers: {
Accept: 'application/json, text/plain, */*',
Cookie: 'JSESSIONID=0000PiVra7dIxpkhWcj0wRG3S01:17o9k4ahg',
'User-Agent': 'axios/0.21.4'
},....

Nest js - httpservice add proxy

I have the next proxy configuration (I just tested it in postman and its working)
host: 'http://tsl.proxyurl.com/standard.pac', port: 8080, protocol: https, no username, no password.
And this is my code:
const options = {
data,
'proxy': {
host: 'http://tsl.proxyurl.com/standard.pac',
port: 8080
},
'headers': {
'Authorization': `Basic ${base64Auth}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
};
const endpoint = `https://api.myrurl.com/st/token`;
const response = await this.httpService.post(endpoint, data, options).toPromise();
But every time, I try, I get:
message:'Client network socket disconnected before secure TLS
connection was established'
Someone had a similar issue? How can I fix it?

certificate issue in nodejs https request

I have the following error while making an https get request
{ Error: write EPROTO 101057795:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:openssl\ssl\s23_clnt.c:802:
at _errnoException (util.js:992:11)
at WriteWrap.afterWrite [as oncomplete] (net.js:864:14) code: 'EPROTO', errno: 'EPROTO', syscall: 'write' }
I am trying to make a request to corporate internal resource where proxy is not needed.
const request = require('request')
var token= myToken
request({
method: 'post',
url: 'https://myURL',
data: {
myData
},
headers: { 'Authorization': 'Bearer myToken' },
agentOptions: {
rejectUnauthorized: false,
},
}, function (error, response, body) {
if(error){
console.log('Error: ', error)
} else {
console.log(body)
}
})
I also have strict-ssl=false in my .npmrc.
What I have notices is that I can make the same call curl with no issues.
curl -k1 -XPOST -H "Authorization: Bearer %TOKEN%" "https://%URL% -d #data,json -H "content-type: application/json"
-k1 option in curl seems to fix the issue with the certificate.
What am I doing wrong in JavaScript?
It turned out to be a bug in node version 8. I finally found a solution here - https://github.com/nodejs/node/issues/16196
One needs to add the following into her code:
require("tls").DEFAULT_ECDH_CURVE = "auto"
request({
method: 'post',
url: 'https://myURL',
data: {
myData
},
headers: { 'Authorization': 'Bearer myToken' },
rejectUnauthorized: false,
}, function (error, response, body) {
if(error){
console.log('Error: ', error)
} else {
console.log(body)
}
});
If you don't want the TLS check all over the node project
set process.env.NODE_TLS_REJECT_UNAUTHORIZED=0;

Node.js HTTP2 server Error: socket hang up

Given the latest version of Node.js with experimental HTTP2 support:
$ node -v
v9.2.0
An HTTP2 server:
var options = {
key: getKey(),
cert: getCert(),
allowHTTP1: true
}
var server = http2.createSecureServer(options)
server.on('stream', onstream)
server.on('error', onerror)
server.on('connect', onconnect)
server.on('socketError', onsocketerror)
server.on('frameError', onframeerror)
server.on('remoteSettings', onremotesettings)
server.listen(8443)
function onconnect() {
console.log('connect')
}
function onremotesettings(settings) {
console.log('remote settings', settings)
}
function onframeerror(error) {
console.log('frame error', error)
}
function onsocketerror(error) {
console.log('socket error', error)
}
function onerror(error) {
console.log(error)
}
function onstream(stream, headers) {
console.log('stream')
}
And a request made to it:
var https = require('https')
var options = {
method: 'GET',
hostname: 'localhost',
port: '8443',
path: '/',
protocol: 'https:',
rejectUnauthorized: false,
agent: false
}
var req = https.request(options, function(res){
var body = ''
res.setEncoding('utf8')
res.on('data', function(data){
body += data;
});
res.on('end', function(){
callback(null, body)
})
})
req.end()
It just hangs and eventually says:
Error: socket hang up
at createHangUpError (_http_client.js:330:15)
at TLSSocket.socketOnEnd (_http_client.js:423:23)
at TLSSocket.emit (events.js:164:20)
at endReadableNT (_stream_readable.js:1054:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
If rejectUnauthorized: true is set, then it errors:
Error: self signed certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1036:34)
at TLSSocket.emit (events.js:159:13)
at TLSSocket._finishInit (_tls_wrap.js:637:8)
Not sure what is going wrong and why it won't get to the point of logging stream.
If I go in the browser and visit https://localhost:8443, and click through the warning messages, it does actually log stream and successfully make the request. But haven't been able to get node to make the request.
I would like to treat this as an HTTP1 server, so don't want to use the HTTP2 client to make the request. But tried using that and same thing.
HTTP/1 doesn't share the same request semantics as HTTP/2 so HTTP/1 clients need to be detected and handled in a HTTP/2 server. To support both you need to use the HTTP2 Compatibility API.
The "hang" occurs when a HTTP1 client connects to a HTTP/2 server with allowHTTP1: true set but doesn't handle the HTTP/1 request.
The examples are based on the Node documentation example code.
HTTP/1 and /2 Mixed Server
const http2 = require('http2')
const fs = require('fs')
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
//ca: fs.readFileSync('ca-crt.pem'),
allowHTTP1: true,
}
var server = http2.createSecureServer(options, (req, res) => {
// detects if it is a HTTPS request or HTTP/2
const { socket: { alpnProtocol } } = (req.httpVersion === '2.0')
? req.stream.session
: req
res.writeHead(200, { 'content-type': 'application/json' })
res.end(JSON.stringify({
alpnProtocol,
httpVersion: req.httpVersion
}))
})
server.listen(8443)
HTTP/2 Client
const http2 = require('http2')
const fs = require('fs')
const client = http2.connect('https://localhost:8443', {
ca: fs.readFileSync('ca-crt.pem'),
rejectUnauthorized: true,
})
client.on('socketError', (err) => console.error(err))
client.on('error', (err) => console.error(err))
const req = client.request({ ':path': '/' })
req.on('response', (headers, flags) => {
for (const name in headers) {
console.log('Header: "%s" "%s"', name, headers[name])
}
})
req.setEncoding('utf8')
let data = ''
req.on('data', chunk => data += chunk)
req.on('end', () => {
console.log('Data:', data)
client.destroy()
})
req.end()
Then running:
→ node http2_client.js
(node:34542) ExperimentalWarning: The http2 module is an experimental API.
Header: ":status" "200"
Header: "content-type" "application/json"
Header: "date" "Sat, 02 Dec 2017 23:27:21 GMT"
Data: {"alpnProtocol":"h2","httpVersion":"2.0"}
HTTP/1 Client
const https = require('https')
const fs = require('fs')
var options = {
method: 'GET',
hostname: 'localhost',
port: '8443',
path: '/',
protocol: 'https:',
ca: fs.readFileSync('ca-crt.pem'),
rejectUnauthorized: true,
//agent: false
}
var req = https.request(options, function(res){
var body = ''
res.setEncoding('utf8')
res.on('data', data => body += data)
res.on('end', ()=> console.log('Body:', body))
})
req.on('response', response => {
for (const name in response.headers) {
console.log('Header: "%s" "%s"', name, response.headers[name])
}
})
req.end()
Then running
→ node http1_client.js
Header: "content-type" "application/json"
Header: "date" "Sat, 02 Dec 2017 23:27:08 GMT"
Header: "connection" "close"
Header: "transfer-encoding" "chunked"
Body: {"alpnProtocol":false,"httpVersion":"1.1"}
HTTP/2 Server
Using the plain HTTP/2 Server will work with the http2_client but will "hang" for a http1_client. The TLS connection from a HTTP/1 client will be closed when you remove allowHTTP1: true.
const http2 = require('http2')
const fs = require('fs')
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
allowHTTP1: true,
}
var server = http2.createSecureServer(options)
server.on('error', error => console.log(error))
server.on('connect', conn => console.log('connect', conn))
server.on('socketError', error => console.log('socketError', error))
server.on('frameError', error => console.log('frameError', error))
server.on('remoteSettings', settings => console.log('remote settings', settings))
server.on('stream', (stream, headers) => {
console.log('stream', headers)
stream.respond({
'content-type': 'application/html',
':status': 200
})
console.log(stream.session)
stream.end(JSON.stringify({
alpnProtocol: stream.session.socket.alpnProtocol,
httpVersion: "2"
}))
})
server.listen(8443)
Certs
With the extended intermediate certificate setup detailed in the gist, the complete certificate chain for the CA needs to be supplied to the clients.
cat ca/x/certs/x.public.pem > caxy.pem
cat ca/y/certs/y.public.pem >> caxy.pem
Then in the clients use this ca in the options.
{
ca: fs.readFileSync('caxy.pem'),
}
These examples were run withe the following simple CA setup from this circle.com article:
To simplify the configuration, let’s grab the following CA
configuration file.
wget https://raw.githubusercontent.com/anders94/https-authorized-clients/master/keys/ca.cnf
Next, we’ll create a new certificate authority using this
configuration.
openssl req -new -x509 \
-days 9999 \
-config ca.cnf \
-keyout ca-key.pem \
-out ca-crt.pem
Now that we have our certificate authority in ca-key.pem and
ca-crt.pem, let’s generate a private key for the server.
openssl genrsa \
-out server-key.pem \
4096
Our next move is to generate a certificate signing request. Again to
simplify configuration, let’s use server.cnf as a configuration
shortcut.
wget https://raw.githubusercontent.com/anders94/https-authorized-clients/master/keys/server.cnf
Now we’ll generate the certificate signing request.
openssl req -new \
-config server.cnf \
-key server-key.pem \
-out server-csr.pem
Now let’s sign the request.
openssl x509 -req -extfile server.cnf \
-days 999 \
-passin "pass:password" \
-in server-csr.pem \
-CA ca-crt.pem \
-CAkey ca-key.pem \
-CAcreateserial \
-out server-crt.pem

How to send an HTTP/2 frame to server in Node.js

With the following code:
var req = http2.request({
hostname: 'api.push.apple.com',
protocol: 'https:',
port: 443,
method: 'POST',
path: '/3/device/' + deviceToken,
agent: new http2.Agent({log: logger}),
headers: {
'apns-topic': 'web.com.example'
},
cert: cert,
key: key,
}, function(res) {
res.pipe(process.stdout);
});
req.write(JSON.stringify(post));
req.end();
how do I send a PING frame to check the health of my connection with node-http2 module?
Thanks in advance.
From Node.js HTTP/2 Documentation:
session.ping(Buffer.from('12345678'), (err, duration, payload) => {
if (!err) {
console.log(`Ping acknowledged in ${duration} milliseconds`);
console.log(`With payload '${payload.toString()}'`);
}
});

Resources