node-xmpp C2SServer connection issue on registration - node.js

I am using node-xmpp library to communicate with ejabberd server installed on ubuntu.
I have the following script to register and perform actions in the server..
var xmpp = require("node-xmpp");
var c2s = new xmpp.C2SServer({
port: 5223,
domain: 'domain.com'
});
c2s.on('connect', function(client) {
console.log('connect ??? ++++++++');
c2s.on('register', function(opts, cb) {
console.log('REGISTER')
cb(true)
})
client.on('authenticate', function(opts, cb) {
console.log('AUTH' + opts.jid + ' -> ' +opts.password)
cb(null) // cb(false)
})
client.on('disconnect', function() {
console.log('DISCONNECT')
})
});
I am able to see the connect message in the server. But its not invoking the register event.
However i have another ejabberd instance in 5222 port which is working fine for registration from Audium XMPP Client.

Just change in the Register event cb(true) with cb(false)..and should work now :)

xmpp.C2SServer is a building block to use if you are writing your own server.
If you want to connect to an existing XMPP server, you need to use:
import xmpp from 'node-xmpp-client'
const client = new xmpp.Client( ... )
Then follow XEP-0077 to register an account on the server.

Related

Connect node JS server to remote laravel-echo-server channel

I have two servers, server 1 hosting laravel echo and server 2 hosting NodeJS. I want server 2 to be able to listen and transmit on a laravel-echo channel which is in server 1.
Anyone have an idea on how to proceed?
I tested with socket IO but I don't receive the events emitted from laravel echo server.
Here is the code I am using
const io = require('socket.io-client');
const socket = io('http://localhost:6001');
socket.on('*', (data) => {
console.log(data);
});
socket.emit('subscribe', {
channel: 'licence-update-7e32cd49-8714-42bf-804b-d823c5698ecc'
});
socket.on('connect', () => {
socket.emit('subscribe', {
// channel: 'private-channel',
channel: 'licence-update-7e32cd49-8714-42bf-804b-d823c5698ecc'
// auth: Cache.get('private-channel:socket-id')
});
});
socket.on('event-name', (data) => {
console.log(data);
});
the code executes but I do not receive a notification.
I want server 2 to be notified whenever there are actions on server 1 and vice versa.
Works using version socket.io.client version 2.4.0 of laravel-echo-client

How to ensure a single, private ssh connection using ssh2 with socket.io in Meteor

I am using ssh2 and socket.io to enable a real-time ssh connection to a remote server for users of my Meteor 1.8.1 app. The app runs on Ubuntu under Nginx and Phusion Passenger. Here is what the app needs to do:
Each authorised user already has an account on the remote server.
A user will start a session by entering their credentials and clicking a "connect" button in the app.
The user can browse directory listings within their home directory on the remote server.
No user should have access to another user's ssh session.
Their ssh session should be removed from the server when the user clicks a "disconnect" button.
I have the ssh connection working but I can't figure out how to destroy the ssh connection at the end of the user's session. Each time they press disconnect" then "connect", another ssh session is started and the old ssh session is still operational, so each ssh command that is sent is executed multiple times and multiple responses are sent to the browser.
I'm also concerned that the connection isn't secure; in development I'm creating the server with require('http').createServer();. In production, on my Ubuntu server with SSL configured, is it enough to use require('https').createServer(); or is there other configuration required, e.g. of Nginx? Socket.io falls back to older technologies when websocket isn't available; how is that secured?
Main question: why am I seeing duplicate SSH sessions every time the user disconnects and then connects?
Secondary question: where can I find up to date instructions on how to secure socket.io? Or should I give up on socket.io and use WebSocket?
I have read a lot of articles and stack overflow posts, but I'm finding this very confusing and most of the material is out of date. For example socketio-auth is not maintained. I can find almost nothing in the Socket.io documentation on authentication or authorization - there is a handshake entry but it's not clear to me from this whether it's the function I need or how to use it.
Here's my code.
Server
io.on('connection', (socket) => {
console.log('socket id', socket.id); // this shows a new id after disconnect / reconnect
const conn = new SSHClient();
socket.on('disconnect', () => {
console.log('disconnect on server');
conn.end();
});
conn.on('ready', () => {
socket.emit('message', '*** SSH CONNECTION ESTABLISHED ***');
socket.emit('ready', 'ready');
conn.shell((err, stream) => {
stream.write('stty -echo \n'); // don't echo our own command back, or the user's password...
if (err) {
return socket.emit('message', `*** SSH SHELL ERROR: ' ${err.message} ***`);
}
socket.on('path', (path) => {
// path is a request for a directory listing
if (typeof path === 'string') {
const bashCommand = `ls -l ${path} --time-style=full-iso`;
console.log('*** WRITE'); // if you disconnect and reconnect this runs twice. Disconnect and reconnect again, it runs 3 times.
console.log('socket id again', socket.id); // this shows the same new socket id each time
stream.write(`${bashCommand} \n`);
}
});
stream.on('data', (d) => {
socket.emit('data', response); // tell the browser!
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('message', '*** SSH CONNECTION CLOSED ***');
}).on('error', (err) => {
socket.emit('message', `*** SSH CONNECTION ERROR: ${err.message} ***`);
}).connect({
'host': hosturl,
'username': ausername,
'agent': anagent, // just for dev I'm using public / private key from my local machine but this will be replaced with the user's entered credentials
});
}).on('disconnect', () => {
console.log('user disconnected');
});
server.listen(8080);
Client:
const io = require('socket.io-client');
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {};
const myEmitter = new MyEmitter();
const PORT = 8080;
let socket;
myEmitter.on('connectClicked', () => {
if (socket) {
this.connected.set(socket.connected);
}
if (this.connected.get() === false) {
socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
this.connected.set(true);
socket.on('ready', () => {
console.log('ready');
});
// Backend -> Browser
socket.on('message', (data) => {
console.log('socket on message', data);
});
// Backend -> Browser
socket.on('data', (data) => {
console.log('got data', data);
this.parseResponse(data); // client function to handle data, not shown here
});
// Browser -> Backend
myEmitter.on('selectDirectory', () => {
console.log('*** SELECT DIRECTORY');
socket.emit('path', pathArray.join('/')); // path array is set in client code, it is a simple array of directory names
});
socket.on('disconnect', () => {
console.log('\r\n*** Disconnected from backend***\r\n');
this.connected.set(false);
});
});
}
myEmitter.on('disconnectClicked', () => {
socket.disconnect();
});
});
The answer to keeping the ssh connections separate is to maintain a list of current ssh connections and rework the code so that received ssh data is sent only to the browser that corresponds to the incoming message.
I've also given up on socket.io because I can't be confident about security. I'm now using Meteor's inbuilt DDP messaging system via the Meteor Direct Stream Access package. I think this avoids opening up any new points of access to my web server.

how to create interactive ssh terminal and enter commands from the browser using Node JS in a Meteor app

I'm trying to create a web page where the user can authenticate to a remote server via ssh with username/password, and then interact with the remote server.
I'm not looking to create a full interactive terminal: the app server will execute a limited set of commands based on user input and then pass the responses back to the browser.
Different users should interact with different ssh sessions.
My app is built in Meteor 1.8.1, so the back end runs under Node JS, version 9.16.0. It's deployed to Ubuntu using Phusion Passenger.
I have looked at several packages that can create an interactive ssh session but I am missing something basic about how to use them.
For example https://github.com/mscdex/ssh2#start-an-interactive-shell-session
The example shows this code:
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.shell(function(err, stream) {
if (err) throw err;
stream.on('close', function() {
console.log('Stream :: close');
conn.end();
}).on('data', function(data) {
console.log('OUTPUT: ' + data);
});
stream.end('ls -l\nexit\n');
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: require('fs').readFileSync('/here/is/my/key')
});
This example connects to the remote server, executes a command 'ls' and then closes the session. It isn't 'interactive' in the sense I'm looking for. What I can't see is how to keep the session alive and send a new command?
This example of a complete terminal looks like overkill for my needs, and I won't be using Docker.
This example uses socket.io and I'm not sure how that would interact with my Meteor app? I'm currently using Meteor methods and publications to pass information between client and server, so I'd expect to need a "Meteor-type" solution using the Meteor infrastructure?
child_process.spawn works but will only send a single command, it doesn't maintain a session.
I know other people have asked similar questions but I don't see a solution for my particular case. Thank you for any help.
I got this working by following these instructions for creating an interactive terminal in the browser and these instructions for using socket.io with Meteor.
Both sets of instructions needed some updating due to changes in packages:
meteor-node-stubs now uses stream-http instead of http-browserify
https://github.com/meteor/node-stubs/issues/14 so don't use the hack for socket
xterm addons (fit) are now separate packages
xterm API has changed, use term.onData(...) instead of term.on('data'...)
I used these packages:
ssh2
xterm
xterm-addon-fit
socket.io
socket.io-client
and also had to uninstall meteor-mode-stubs and reinstall it to get a recent version that doesn't rely on the Buffer polyfill.
Here's my code.
Front end:
myterminal.html
<template name="myterminal">
<div id="terminal-container"></div>
</template>
myterminal.js
import { Template } from 'meteor/templating';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import './xterm.css'; // copy of node_modules/xterm/css/xterm.css
// xterm css is not imported:
// https://github.com/xtermjs/xterm.js/issues/1418
// This is a problem in Meteor because Webpack won't import files from node_modules: https://github.com/meteor/meteor-feature-requests/issues/278
const io = require('socket.io-client');
Template.fileExplorer.onRendered(function () {
// Socket io client
const PORT = 8080;
const terminalContainer = document.getElementById('terminal-container');
const term = new Terminal({ 'cursorBlink': true });
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
term.open(terminalContainer);
fitAddon.fit();
const socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
console.log('socket connected');
term.write('\r\n*** Connected to backend***\r\n');
// Browser -> Backend
term.onData((data) => {
socket.emit('data', data);
});
// Backend -> Browser
socket.on('data', (data) => {
term.write(data);
});
socket.on('disconnect', () => {
term.write('\r\n*** Disconnected from backend***\r\n');
});
});
});
Server:
server/main.js
const server = require('http').createServer();
// https://github.com/mscdex/ssh2
const io = require('socket.io')(server);
const SSHClient = require('ssh2').Client;
Meteor.startup(() => {
io.on('connection', (socket) => {
const conn = new SSHClient();
conn.on('ready', () => {
console.log('*** ready');
socket.emit('data', '\r\n*** SSH CONNECTION ESTABLISHED ***\r\n');
conn.shell((err, stream) => {
if (err) {
return socket.emit('data', `\r\n*** SSH SHELL ERROR: ' ${err.message} ***\r\n`);
}
socket.on('data', (data) => {
stream.write(data);
});
stream.on('data', (d) => {
socket.emit('data', d.toString('binary'));
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('data', '\r\n*** SSH CONNECTION CLOSED ***\r\n');
}).on('error', (err) => {
socket.emit('data', `\r\n*** SSH CONNECTION ERROR: ${err.message} ***\r\n`);
}).connect({
'host': process.env.URL,
'username': process.env.USERNAME,
'agent': process.env.SSH_AUTH_SOCK, // for server which uses private / public key
// in my setup, already has working value /run/user/1000/keyring/ssh
});
});
server.listen(8080);
});
Note that I am connecting from a machine that has ssh access via public key to the remote server. You may need different credentials depending on your setup. The environment variables are loaded from a file at Meteor runtime.

Connecting RabbitMq to an TCP/IP server

I am using RabbitMq as a Queueing Mechanism for my message handling.
So far, all the queues are generating fine on the localhost but when I moved on to the server. It started showing
connection timeout error
This is my connecion string.
var amqp = require('amqp');
var connection = amqp.createConnection({
url: 'amqp://username:passwprd#server-IP:5672/'
})
connection.on('error',(err)=>{
console.log(err);
});
var options = { autoDelete:false,
durable:false,
expiration:'20000',
};
connection.on('ready',()=>{
connection.queue('queueName',options,(queue)=>{
queue.bind('#');
queue.subscribe({ack:true},message =>{
console.log(message);
});
});
});
Can anyone tell me what i am doing wrong here
I have launched the Ubuntu EC2 instance and using putty utility I have installed rabbitq on the same.
To know the status of rabbitmq service, you can type service rabbitmq-server status
You can refer documentation from Rabbitmq node Rabbitmq
I have tried with below code on aws instance and its working
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(err, conn) {
console.log(conn);
});
Output
Actually I get what you are asking
Just type this code
var connection = amqp.createConnection({
url: 'amqp://localhost'
})
on your server side code and the project will run fine as it is running on my server.
You don't need to specify the username and password or your server-ip.

NodeJS xmpp server

I make the first steps in the node js and xmpp
I need to run at xmpp server on node js for messaging
Here's the process:
I use node-xmpp server https://github.com/astro/node-xmpp
run the example of a server (/examples/c2s.js)
join to server with two clients (clients tested on other servers jabber - it works and the messages are sending through)
Clients have authorization on my server.
But when I send a message from one client to another, the message comes to the server (I see it in the logs)
and that was the message does not come to recipient
I don `t know where to look for the problem
server configuration ? routing ? messaging may be necessary to add yourself ?
help me plz
my server code (by examples)
var xmpp = require('../lib/node-xmpp');
var c2s = new xmpp.C2SServer({
port: 5222,
domain: 'localhost'
});
// On Connect event. When a client connects.
c2s.on("connect", function(client) {
c2s.on("register", function(opts, cb) {
console.log("REGISTER");
cb(true);
});
client.on("authenticate", function(opts, cb) {
console.log("AUTH" + opts.jid + " -> " +opts.password);
cb(null);
});
client.on("online", function() {
console.log("ONLINE");
client.send(new xmpp.Message({ type: 'chat' }).c('body').t("Hello there, little client."));
});
client.on("stanza", function(stanza) {
console.log("STANZA" + stanza);
});
client.on("disconnect", function(client) {
console.log("DISCONNECT");
});
});
I run a server and connect to it by this code
var xmpp = require('../lib/node-xmpp');
var argv = process.argv;
if (argv.length < 6) {
console.error('Usage: node send_message.js <my-jid> <my-password> <my-text> <jid1> [jid2] ... [jidN]');
process.exit(1);
}
var cl = new xmpp.Client({ jid: argv[2], password: argv[3] });
cl.addListener('online',
function() {argv.slice(5).forEach(
function(to) {cl.send(new xmpp.Element('message', { to: to,type: 'chat'}).c('body').t(argv[4]));
});
// nodejs has nothing left to do and will exit
// cl.end();
});
cl.addListener('stanza',
function(stanza) {
if (stanza.is('message') &&
// Important: never reply to errors!
stanza.attrs.type !== 'error') {
console.log("New message");
// Swap addresses...
stanza.attrs.to = stanza.attrs.from;
delete stanza.attrs.from;
// and send back.
cl.send(stanza);
}
});
cl.addListener('error',
function(e) {
console.error(e);
process.exit(1);
});
The short answer: change cb(null) to cb(null, opts).
The long answer:
client.on("authenticate", function(opts, cb) {...}) registers what the server will do when the client tries to authenticate itself. Inside node-xmpp, it will look for the authentication mechanism first and the mechanism will then call the callback and retrieve the authentication results via cb.
By default, the Plain authentication is used. You can check out how it works here: https://github.com/node-xmpp/node-xmpp-server/blob/master/lib/authentication/plain.js. With Plain the opts stores the jid and password.
Then to inform node-xmpp that authentication failed/sucessed, we need to look into mechanism, https://github.com/node-xmpp/node-xmpp-server/blob/master/lib/authentication/mechanism.js, inherited by Plain.
this.authenticate(this.extractSasl(auth), function (err, user) {
if (!err && user) {
self.success(user)
} else {
self.failure(err)
}
})
Here, cb requires two parameters. When err is null and user is non-null, it indicates authentication successes. Otherwise, failed.
I am no expert on neither node.js nor xmpp. But reading your code. I assume the "stanza" is the event where a client sent a message. You asked it to log the message, but you gave no instructions on how to route it to the recipient. You should break down the received message on the server into message body and recipient, and ask your server to send it to the recipient.
Alex you have used C2SServer which connects a stream between a server and a client. When you send a message from one client to another they get to server. Now its responsibility of the sever to relay them back to actual receiver.
One possible solution is to keep client object is a global object corresponding to their jids when client is authenticated, when you get a message for that client you can get that from global variable and route the message to actual client kept in global variable.
You can get the text message & receiver JID from server. Just break the stanza in following ways and put this before error listeners:-
cl.on('stanza', function(stanza) {
if (stanza.is('message') && (stanza.attrs.type !== 'error')) {
var body = stanza.getChild('body');
if (!body) {
return;
}
console.log(stanza.attrs.from+" Says: "+body.children[0]);
}
});
In "authenticate", may an argument not be enough for a callback?
NG:
cb(null);
OK:
cb(null, opts);

Resources