Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eavesdocker

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eavesdocker - npm Package Compare versions

Comparing version 0.1.2 to 0.1.3

lib/inspector/favicon.png

8

CHANGELOG.md

@@ -8,2 +8,10 @@ # Eavesdocker Changelog

## [v0.1.3] - 2021-07-14
### Added
- endpoint to list all active containers
- SSE endpoint to check container events and log entries in real-time
- logic to handle swarm events via redis pubsub and cache
- monitoring UI
## [v0.1.2] - 2021-04-01

@@ -10,0 +18,0 @@

83

lib/api.js

@@ -0,4 +1,15 @@

const { v4: uuidv4 } = require('uuid');
const fs = require('fs').promises;
module.exports = function({ post }){
const { sanitizeContainer } = require('./transport');
async function readFile(name, type, { res }){
const data = await fs.readFile(__dirname + '/inspector/' + name, 'utf8');
res.type(type).end(data);
}
module.exports = function({ post, get }){
get('/', readFile.bind(null, 'page.html', 'text/html'));
post('/transport/:name/message', function({ transports, res, params, body }){

@@ -8,10 +19,11 @@

let json;
try{
var json = typeof body == 'object' ? body : JSON.parse(body);
json = typeof body == 'object' ? body : JSON.parse(body);
}
catch(err){
catch(_err){
json = { message: body };
}
let t = transports[params.name];
const t = transports[params.name];
t.push(t.json ? json : body);

@@ -22,2 +34,65 @@

get('/containers', async function({ cookies, node, redis, cluster, swarm, containers, res, conf }){
const dash = conf.eavesdocker.dashboard || {};
const secret = dash.secret || '';
res.unauthorized(cookies.eavesdocker != secret);
const cs = containers;
Object.keys(cs).forEach(c => cs[c].node = node);
if(conf.eavesdocker.swarm){
const prefix = 'eavesdocker:' + cluster + ':';
for(const nid in swarm){
const all = await redis.hgetall(prefix + nid + ':containers');
Object.keys(all).forEach(c => all[c].node = swarm[nid]);
containers = { ...containers, all };
}
}
res.json(Object.values(cs).map(sanitizeContainer));
});
post('/auth', function({ body, res, conf }){
const dash = conf.eavesdocker.dashboard || {};
const secret = dash.secret || '';
res.unauthorized(body != secret);
res.cookie('eavesdocker', secret, {
maxAge: 60 * 60 * 24 * 7 * 1000,
secure: true,
httpOnly: true,
sameSite: 'Strict'
}).end();
}),
get('/live', function({ req, res, sseClients, log, cookies, conf }){
const dash = conf.eavesdocker.dashboard || {};
const secret = dash.secret || '';
res.unauthorized(cookies.eavesdocker != secret);
res.type('text/event-stream');
res.set('Connection', 'keep-alive');
res.set('Cache-Control', 'no-cache');
res.write(`data: online\n\n`);
const id = uuidv4();
sseClients[id] = res;
log.debug(`SSE Client ${id} is online`);
req.on('close', () => {
delete sseClients[id];
log.debug(`SSE Client ${id} is gone`);
});
});
get('/stylesheet.css', readFile.bind(null, 'stylesheet.css', 'text/css'));
get('/scripts.js', readFile.bind(null, 'scripts.js', 'application/javascript'));
get('/favicon.png', readFile.bind(null, 'favicon.png', 'image/png'));
}

25

lib/container.js

@@ -11,14 +11,17 @@ const { routeMessage } = require('./transport');

let c = await this.global.docker.call('/containers/' + id + '/json');
const c = { id };
c.stack = labels['com.docker.stack.namespace'] || labels['com.docker.compose.project'] || '';
c.service = labels['com.docker.swarm.service.name'] || labels['com.docker.compose.service'] || '';
c.service = c.service.replace(c.stack + '_', '');
c.node = this.global.node;
c.tasks = matchTasks.call(this, c, labels);
c.number = (labels['com.docker.swarm.task.name'] ||
labels['com.docker.compose.container-number'] || '1').replace(/.*?\./, '')
.replace(/\..*$/, '');
c.tasks = matchTasks.call(this, c);
if(c.tasks == 0)
return;
c.id = id;
c.cid = '\'' + c.stack + '\' > ' + c.service + ' (' + id.substr(0, 8) + ')';
// : id.substr(0, 32);

@@ -28,5 +31,8 @@ try{

this.global.containers[id] = c;
this.global.emitter.emit('start', c);
this.log.debug('Attached to %s with %s tasks', c.cid, c.tasks.length);
}
catch(err){
catch(_err){
// console.log(err);

@@ -41,2 +47,5 @@ this.log.warn('Failed to attach to %s', c.cid);

return;
this.global.emitter.emit('die', { id });
this.global.containers[id].stream.destroy();

@@ -48,4 +57,4 @@ this.log.debug('Removed container %s', this.global.containers[id].cid);

function parseEvent(data){
let event = JSON.parse(data);
let actionType = event.Action + ' ' + event.Type;
const event = JSON.parse(data);
const actionType = event.Action + ' ' + event.Type;
if(actionType == 'start container')

@@ -58,3 +67,3 @@ addContainer.call(this, { Labels: event.Actor.Attributes, Id: event.id });

function destroyLoggers(){
for(let id in this.global.containers)
for(const id in this.global.containers)
this.global.containers[id].stream.destroy();

@@ -61,0 +70,0 @@ }

@@ -0,2 +1,4 @@

const { EventEmitter } = require('events');
const Nodecaf = require('nodecaf');
const redis = require('nodecaf-redis');

@@ -8,6 +10,77 @@ const { addContainer, parseEvent, destroyLoggers } = require('./container');

const api = require('./api');
async function connectToSwarm(global, conf, log){
const info = await global.docker.call('/info');
global.node = { name: info.Name, id: info.Swarm.NodeID };
global.cluster = info.Swarm.Cluster ? info.Swarm.Cluster.ID : 'local';
global.swarm = {};
try{
global.redis = await redis(conf.redis, 'eavesdocker:' + global.cluster, function(_c, msg){
msg = JSON.parse(msg);
if(msg.node.id == global.node.id)
return;
else if(msg.type == 'node-ping' && msg.node.id in global.swarm){
clearTimeout(global.swarm[msg.node.id].tto);
global.swarm[msg.node.id].tto = setTimeout(() =>
delete global.swarm[msg.node.id], 10e3);
}
else if(msg.type == 'node-ping')
global.swarm[msg.node.id] = {
...msg.node,
ttl: setTimeout(() => delete global.swarm[msg.node.id], 10e3)
};
else if(msg.type in { start: 1, die: 1, log: 1 })
global.emitter.emit('swarm-' + msg.type, msg);
});
}
catch(err){
log.warn({ err });
return setTimeout(() => connectToSwarm(global, conf, log), 3000);
}
const sendSwarmEvent = function(msg){
const ev = `event: ${msg.type}\ndata: ${JSON.stringify({
...msg.data, node: msg.node })}\n\n`;
Object.values(global.sseClients).forEach(res => res.write(ev));
};
global.emitter.on('swarm-log', sendSwarmEvent);
global.emitter.on('swarm-start', sendSwarmEvent);
global.emitter.on('swarm-die', sendSwarmEvent);
const pub = function(type, data){
global.redis.publish('eavesdocker:' + global.cluster,
JSON.stringify({ data, node: global.node, type }));
};
global.pingger = setInterval(() => pub('node-ping'), 9e3);
const containersKey = 'eavesdocker:' + global.cluster + ':' + global.node.id + ':containers';
global.emitter.on('start', function(c){
pub('start', c);
global.redis.hset(containersKey, c.id, JSON.stringify(c));
});
global.emitter.on('die', function(id){
pub('die', id);
global.redis.hdel(containersKey, id);
});
global.emitter.on('log', data => pub('log', data));
global.redis.del(containersKey);
}
module.exports = () => new Nodecaf({
conf: __dirname + '/default.toml',
async startup({ global, conf }){
api,
async startup({ global, conf, log }){
global.containers = {};

@@ -26,9 +99,32 @@ global.transports = {};

await createTasks.call(this);
let containers = await global.docker.call('/containers/json');
for(let c of containers)
global.emitter = new EventEmitter();
global.sseClients = {};
const sendEvent = function(type, data){
const ev = `event: ${type}\ndata: ${JSON.stringify({ ...data, node: global.node })}\n\n`;
Object.values(global.sseClients).forEach(res => res.write(ev));
};
global.emitter.on('start', sendEvent.bind(null, 'start'));
global.emitter.on('die', sendEvent.bind(null, 'die'));
global.emitter.on('log', sendEvent.bind(null, 'log'));
if(conf.eavesdocker.swarm && conf.redis)
await connectToSwarm(global, conf, log);
const containers = await global.docker.call('/containers/json');
for(const c of containers)
await addContainer.call(this, c);
},
async shutdown({ global }){
global.events.off('data', parseEvent.bind(this));
async shutdown({ global, conf }){
if(conf.eavesdocker.swarm){
clearInterval(global.pingger);
await global.redis.close();
}
global.emitter.removeAllListeners();
global.events.removeAllListeners();
global.events.destroy();

@@ -35,0 +131,0 @@ destroyLoggers.call(this);

@@ -16,3 +16,3 @@

let queue = [];
let it = setInterval(async function(){
const it = setInterval(async function(){
queue.length > 0 && await coll.insertMany(queue);

@@ -59,4 +59,10 @@ queue = [];

function sanitizeContainer(c){
return { id: c.id, stack: c.stack, service: c.service, number: c.number,
node: c.node };
}
module.exports = {
sanitizeContainer,
createTransport,

@@ -67,20 +73,26 @@

let json;
try{
var json = JSON.parse(entry);
json = JSON.parse(entry);
assert(typeof json == 'object');
}
catch(err){
catch(_err){
json = { message: String(entry).replace(/\r?\n$/, ''), time: new Date() };
}
for(let task of container.tasks){
let input = task.transform(container, json);
for(let transport of task.transports)
for(const task of container.tasks){
const input = task.transform(container, json);
for(const transport of task.transports)
transport.push(input);
}
this.global.emitter.emit('log', {
source: sanitizeContainer(container),
entry: json
});
},
async createTransports(){
for(let name in this.conf.eavesdocker.transports){
let t = this.conf.eavesdocker.transports[name];
for(const name in this.conf.eavesdocker.transports){
const t = this.conf.eavesdocker.transports[name];
await createTransport.call(this, name, t);

@@ -91,3 +103,3 @@ }

async closeTransports(){
for(let name in this.global.transports)
for(const name in this.global.transports)
await this.global.transports[name].close();

@@ -94,0 +106,0 @@ }

{
"name": "eavesdocker",
"version": "0.1.2",
"version": "0.1.3",
"main": "lib/main.js",

@@ -12,4 +12,5 @@ "scripts": {

"nodecaf": "^0.11.5",
"nodecaf-redis": "0.0.1",
"toml": "^3.0.0"
"nodecaf-redis": "0.0.3",
"toml": "^3.0.0",
"uuid": "^8.3.2"
},

@@ -16,0 +17,0 @@ "devDependencies": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc