How can two internal Cloud Run node.js microservices successfully communicate via gRPC? - node.js

New to Google Cloud Run and trying to have two node.js microservices communicate internally via gRPC.
The client interface:
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
The client code:
const client: MyClient = new MyClient('my-service-abcdefgh3a-ew.a.run.app:443', grpc.credentials.createSsl());
The server code:
const server = new grpc.Server();
server.addService<IMyServer>(MyService, new MyServer());
server.bind(`0.0.0.0:${process.env.PORT}`, grpc.ServerCredentials.createInsecure());
server.start();
The server is set to listen to 443.
The above seems to work when the service is open to public requests but doesn't work when you have the server set as internal. Any ideas?

You have to add the credentials in the request metadata. Here an example
...
// Create a client for the protobuf spec
const client = new protoObj.Greeter(HOST, grpc.credentials.createInsecure());
// Build gRPC request
const metadata = new grpc.Metadata();
metadata.add('authorization', `Bearer ${JWT_AUTH_TOKEN}`);
// Execute gRPC request
client.sayHello({name: GREETEE}, metadata, (err, response) => {...
Second question, how to get the JWT_AUTH_TOKEN. Here the documentation of Cloud Run to do this. But not completely, simply get the token and use it in the metadata of the request
...
request(tokenRequestOptions)
.then((token) => {
// add the token to the metadata
});
// Make the call
...

Related

AWS Greengrass V2 Node Publishing problem with aws-iot-sdk-v2 JS

For the past few days I've been trying to solve the problem of publishing a message from Lambda to the AWS cloud, using Greengrass v2.
The code in python was even provided in the documentation, only had to be slightly reworked.
When it comes to SDK v2 JS in documentation there is only minimal mention about publish function in AWS-CRT library.
I tried to create code using components from this library, but it looks like the library also requires a script with parameters.
This is my code that requires installation of aws-iot-sdk-v2 js.
const iotsdk = require("aws-iot-device-sdk-v2");
const mqtt = iotsdk.mqtt;
const os = require("os");
const util = require("util");
const GROUP_ID = process.env.GROUP_ID;
const THING_NAME = process.env.AWS_IOT_THING_NAME;
const THING_ARN = process.env.AWS_IOT_THING_ARN;
(topic = "gg/message"),
(payload = JSON.stringify({ message: util.format("ping") }));
function greengrassHelloWorldRun() {
mqtt.MqttClientConnection.prototype.publish(topic, payload);
}
console.log(topic);
console.log(payload);
setInterval(greengrassHelloWorldRun, 5000);
exports.handler = function (event, context) {
console.log("event: " + JSON.stringify(event));
console.log("context: " + JSON.stringify(context));
};
I get errors about arguments and NAPI.
The same errors also appear when using this function as lambda component in greengrass logs
Maybe someone has some example how to publish some message on topic using Node lambda with sdk v2.
After contacting AWS Support I know it is impossible.
AWS doesn't support mqttProxy IPC for SDK V2 JS yet.
ChristopherTal
I'm also using the Greengrass SDKs for JS and indeed they're still a work in progress. But I was able to send messages to the IoTCore from Greengrass using the JS SDKs.
A few things to mention:
You seem to use the aws-iot-device-sdk-v2 SDK which is for things
The aws-greengrass-core-sdk npm package is made for components
It is important to differ between things and components and decide who's doing what.
To send data to IoTCore from a thing, you need indeed to use MQTT. On the deployment page on the Greengrass console, you need to revise the deployment and add following components:
MQTT Broker
MQTT Bridge
Client device auth
This way your thing connects to the local MQTT Broker through the client device auth component and the MQTT Bridge decides how the traffic is routed. You can read all info on the links above.
I even realised this using the standard mqtt npm package. You need to create a certificate and a thing using lambda or the console and use those certificates to access the broker.
const mqtt = require('mqtt')
const fs = require('fs')
const ca = fs.readFileSync(locationOfTheCA)
const key = fs.readFileSync(locationOfThePrivateKey)
const cert = fs.readFileSync(locationOfTheCertificate)
console.log('Welcome to MQTT Connector')
const client = mqtt.connect('mqtts://localhost:8883', {
clientId: 'yourThingNameHere',
ca: ca,
key: key,
cert: cert
})
client.on('connect', function () {
console.log('Connected to MQTT')
/* client.subscribe('$aws/*', function (err) {
if (!err) {
//client.publish('presence', 'Hello mqtt')
}
})*/
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
})
Hopefully this helps you out!
Warm regards
Hacor

Get my Action’s server URL in (JavaScript) fulfilment code

I am using actionssdk and I build my Action fulfilments using Javascript and node.js + Express.
I am looking for a way to get the url (protocol + host name + port) of the server where the fulfilment is hosted.
Is there a simple way to do this? E.g. in the MAIN intent? Is there some conv-property I can use? Can I get hold of a req-parameter in the MAIN-intent, from which I can deduct hostname etc?
const express = require('express');
const expressApp = express();
const { actionssdk, ... } = require('actions-on-google');
const app = actionssdk({
ordersv3: true,
clientId: ...
});
expressApp.post('/fulfilment', app);
app.intent('actions.intent.MAIN', (conv) => {
let myUrl: string = ... // ???????
...
});
(background: obviously I know myself to where I deployed my fulfilment code. But I have a reusable template for fulfilment code in which I want to refer to the host url, and I do not want to type that in manually each time I develop a new Action).
You can get access to the request object in a middleware via Framework Metadata which is by default of type BuiltinFrameworkMetadata which contains objects used by Express
For example, you can use it like this, which will be ran before each request:
app.middleware((conv, framework) => {
console.log(framework.express.request.headers.host)
})

Websockets token authentication using middleware and express in node.js

I use node.js, express and express-ws that is based on ws
Express-ws allows to create express-like endpoints for websockets.
I am looking for a solution to authenticate users in websocket connections, based on a token. Since my ws server is based on an HTTP one
const wsHttpServer = http.createServer();
wsHttpServer.listen(5001);
const expressWs = require('express-ws')(app , wsHttpServer);
and since the ws connection is based on an HTTP one that gets upgraded to a ws, WHY I cannot pass a token in my ws that the express route checks, like any other one? My logic is, send the token, check it, if it is ok, proceed to upgrade to a ws connection. So, I can reuse the token-middleware solution that I have in my HTTP connections.
In node
My ws server
const wsHttpServer = http.createServer();
wsHttpServer.listen(5001);
const expressWs = require('express-ws')(app , wsHttpServer);
//set the route
app.use('/ws', require('./routes/wsroute'));
In that route, I would like to use the token.validate() middleware -that in HTTP connections, checks the Authorization header
router.ws('/user/:name/:id', token.validate(), (ws, req) => {
console.log('ws route data : ',vessel, req.params.name, req.params.id);
});
In my client
const socket = new WebSocket('ws://localhost',{
path: '/user/Nick/25/',
port: 5001, // default is 80
protocol : "echo-protocol", // websocket protocol name (default is none)
protocolVersion: 13, // websocket protocol version, default is 13
keepAlive: 60,
headers:{ some:'header', 'ultimate-question':42 } // websocket headers to be used e.g. for auth (default is none)
});
this errors Failed to construct 'WebSocket': The subprotocol '[object Object]' is invalid
I also tried
const socket = new WebSocket('ws://localhost:5001/user/Nick/25', ["Authorization", localStorage.getItem('quad_token')]);
I dont get any errors, but I dont know how to get the Authorization "header" in node
I could
just send const socket = new WebSocket(currentUrl); with some data and include a valid token in that data. But to check it, I have to allow the connection first. I dont want that, I would like to use a middleware solution that automatically checks a token and allows or not to continue.
Questions
Please help me understand:
1 Is it possible to use a token-based, middleware-based solution in ws?
2 How to set a header with a token in a ws connection?
3 How to get that token in node?
1) In my experience there is no available express.js middleware and the solution i found requires to listen to the upgrade event on your http server and blocking access to your socket connection before it reaches ws routes.
2) Your browser will not allow setting additional headers during websocket connection on the client side. It will send though the cookies so you can make use of express-session to authorize on your server first the user, a cookie will be set on the browser and that cookie will be sent over during the websocket connection.
3) You can do like in this answer Intercept (and potentially deny) web socket upgrade request Copying the code here from there for your own perusal.
**wsHttpServer**.on('upgrade', function (req, socket, head) {
var validationResult = validateCookie(req.headers.cookie);
if (validationResult) {
//...
} else {
socket.write('HTTP/1.1 401 Web Socket Protocol Handshake\r\n' +
'Upgrade: WebSocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n');
socket.close();
socket.destroy();
return;
}
//...
});
As outlined here, it seems that it is not possible for a standard browser websocket client to handle a http error response to an upgrade request. Thus what I ended up using was something like this:
HTTPserver.on('upgrade' (req, sock, head) => {
if (req.url === wsRoute) {
webSocketServer.handleUpgrade(req, sock, head, ws => {
const authenticated = validateToken(req.headers.cookie) // your authentication method
if (!authenticated) {
ws.close(1008, 'Unauthorized') // 1008: policy violation
return
}
webSocketServer.emit('connection', ws, req)
})
} else {
sock.destroy()
}
}
This way we accept the connection first before closing it with an appropriate code and reason, and the websocket client is able to process this close event as required.
On your client side, you should pass an array of strings instead of object, but you must set a header for your HTTP response with a key and value:
key : headeSec-WebSocket-Protocol
value : corresponding protocol used in front.

How does one correctly set up a server based deepstream RPC provider?

I am building a SOA with deepstream and I want to use a deepstream client server to perform API-KEY based look ups that the user should not know. How do I actually set up an RPC client provider? I have looked in the deepstream docs and on google, but there is not a full code example on how to do this. I have created a file like below and run it with node. The output I get is below it:
var deepstream = require('deepstream.io-client-js')
const client = deepstream('localhost:6020').login()
console.log('Starting up')
client.on('error', (error,event,topic) => {
console.log(error, event, topic);
})
client.on('connectionStateChanged', connectionState => {
console.log(connectionState);
})
client.login({username: 'USER', password: 'PASSWORD'}, (success, data) => {
if (success) {
client.rpc.provide('the-rpc', function( data, response ){
response.send(data);
});
} else {
console.log(data);
}
})
--
Starting up
AWAITING_CONNECTION
As you can see it runs the code, but does not actually connect to the deepstream server. I already have the deepstream server running, and a browser client that connects to it, so the config is correct. Please help!
I think your issue is based on the fact your trying to connect node via the webport. Try using port 6021 instead for tcp ( used by the node client ).
const client = deepstream('localhost:6021').login()
You should also only call .login() once, so the line would be:
const client = deepstream('localhost:6021')
We are working on a 2.0 release coming out very soon which will remove tcp entirely and only require a single port to make life easier in terms of deployment and performance.

Returning 401 error for all routes with hapijs

We're using Hapi JS for our rest server. We store the authentication tokens for the users on Redis. Now, if for some reason node loses connection with Redis, we need to return 401 Authorization failed error to all the clients from all the routes so the clients can logout automatically.
So, is there a way to return 401 from all routes without changing the code in the route handler functions?
You can make use of the Hapi server extension event 'onRequest'.
var hapi = require('hapi');
var Boom = require('boom');
var server = new hapi.Server();
//Configure your server
//Add an extension point
server.ext('onRequest', function (request, reply) {
var status;
//Check status of redis instance
if (status) {
//Redis is running, continue to handler
return reply.continue();
} else {
//Redis is down, reply with error
return reply(Boom.unauthorized('Auth server is down'));
}
});
This is probably not how you will verify the status your redis instance, but I hope you get the point.
One can look up various other extension points here.
You should do this in the auth plugin used by your app. Take a look at the hapi-auth-basic implementation: https://github.com/hapijs/hapi-auth-basic/blob/master/lib/index.js
If you look in the scheme you must define an authenticate method which takes the request and reply. This is where you should check redis for an auth token. If the connection is not available you should
return reply(Boom.unauthorized('Authorization failed', 'Basic'));
Hope this helps.

Resources