Commit d4a06ebc authored by Matheus Horstmann's avatar Matheus Horstmann 🐴

Start agent only

Signed-off-by: Matheus Horstmann's avatarMatheus Horstmann <mch15@inf.ufpr.br>
parents
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
*.swp
*.swo
pids
logs
results
node_modules
bower_components
build
tmp
npm-debug.log
config.js
sessions.db
reports/out
reports/build
.git
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
bower_components
build
tmp
npm-debug.log
config.js
app/config.json
sessions.db
reports/out
# Avoid the use of floating tags like: latest, boron, argon and carbon
ARG VERSION=6.17.1
FROM node:$VERSION
LABEL autor="C3SL - Centro de Computação Científica e Software Livre"
# Node was not designed to run as PID 1 (process in docker run with PID 1).
# Its recommended to use the flag --init or use tine to start node process
# in docker
# Add Tini
ENV TINI_VERSION="v0.16.1"
ENV WORKSPACE="/home/node/app/"
ENV NODE_ENV="production"
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/node/app/node_modules/.bin"
ENV SIMMC_SECRET="secret"
ENV SIMMC_ALERT_PATH="/home/node/app/tmp/alerts/"
ENV SIMMC_AGENT_OUTPUT="/home/node/app/tmp/agent"
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json $WORKSPACE
RUN chmod +x /tini &&\
chown -R node:node $WORKSPACE
USER node
WORKDIR $WORKSPACE
RUN npm install &&\
mkdir -p "/home/node/app/tmp" &&\
chmod 777 "/home/node/app/tmp"
# Bundle app source
# COPY copy files as root so chown must be used
USER root
COPY . .
RUN find . -user root | xargs chown node:node
USER node
VOLUME ["/home/node/app/tmp"]
EXPOSE 3000
HEALTHCHECK CMD curl -f http://localhost:3000/api/ping || exit 1
# Instead of using npm start to start the container, directly put the command
# that npm start executes.
# First off this reduces the number of processes running inside of your container
# Secondly it causes exit signals such as SIGTERM and SIGINT to be received by
# the Node.js process instead of npm swallowing them.
ENTRYPOINT ["/tini", "-g", "--"]
CMD ["sh", "-c", "./scripts/docker_init.sh" ]
This diff is collapsed.
var config = module.exports = {};
var webservice = '$SIMMC_DOMAIN';
config.db_config = {
user: '$SIMMC_DB_USER',
password: '$SIMMC_DB_PASSWORD',
database: '$SIMMC_DB_NAME',
host: '$SIMMC_DB_HOST',
port: $SIMMC_DB_PORT
};
config.fqdn = webservice;
config.secret = '$SIMMC_SECRET';
config.agent = {
version: '$SIMMC_AGENT_VERSION',
webservice: webservice,
outputDir: '$SIMMC_AGENT_OUTPUT'
};
version: '3.3'
services:
simmc:
image: marula.c3sl.ufpr.br:5000/c3sl/simmc:latest
build: .
container_name: simmc
depends_on:
- db
volumes:
- type: tmpfs
target: /tmp
- type: tmpfs
target: /home/node/app/build
- type: tmpfs
target: /home/node/app/reports/build
- type: tmpfs
target: /home/node/app/tmp
environment:
NODE_ENV: ${NODE_ENV}
SIMMC_DB_USER: ${SIMMC_DB_USER}
SIMMC_DB_NAME: ${SIMMC_DB_NAME}
SIMMC_DB_HOST: ${SIMMC_DB_HOST}
SIMMC_DB_PORT: ${SIMMC_DB_PORT}
SIMMC_DB_PASSWORD: ${SIMMC_DB_PASSWORD}
SIMMC_SECRET: ${SIMMC_SECRET}
SIMMC_DOMAIN: ${SIMMC_DOMAIN}
SIMMC_AGENT_VERSION: ${SIMMC_AGENT_VERSION}
ports:
- "3000:3000"
restart: unless-stopped
read_only: 'true'
security_opt:
- no-new-privileges
db:
image: postgres:9.6
container_name: db
ports:
- "5432:5432"
restart: unless-stopped
security_opt:
- no-new-privileges
volumes:
- ~/.docker-volumes/agent-database/db:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${SIMMC_DB_USER}
POSTGRES_PASSWORD: ${SIMMC_DB_PASSWORD}
#!/bin/bash
cur=$(readlink -f $(dirname $0))
export PATH="$cur/node_modules/.bin:$PATH"
$*
\ No newline at end of file
/*
* Copyright (C) 2012 Centro de Computacao Cientifica e Software Livre
* Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
*
* This file is part of simmc
*
* simmc is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
var os = require('os');
var cluster = require('cluster');
var bunyan = require('bunyan');
var cuid = require('cuid');
var fs = require('fs');
// Serialize a request (remove unuseful information from requests,
// log only the necessary).
function requestSerializer(req) {
if ((typeof(req) !== 'object') || (req === null)) {
return req;
}
var headers = req.headers || {};
return {
method: req.method,
url: req.url,
headers: { // selective choice of headers (no cookies)
"host": headers["host"],
"connection": headers["connection"],
"cache-control": headers["cache-control"],
"accept": headers["accept"],
"user-agent": headers["user-agent"],
"referer": headers["referer"],
"accept-encoding": headers["accept-encoding"],
"accept-language": headers["accept-language"]
},
remoteAddress: req.ips[0] || req.ip || null
};
}
// Serialize a reponse (remove unuseful information from responses
// and calculate the reponse time).
function responseSerializer(res) {
if ((typeof(res) !== 'object') || (res === null)) {
return res;
}
// Calculate the response time
var responseTime;
if (res.start) {
responseTime = (new Date() - res.start).toString() + " ms";
}
return {
statusCode: res.statusCode,
responseTime: responseTime
}
}
var defaultLogger;
module.exports = function(mode) {
if(mode === "default") {
// Make sure the logs dir exist
try {
fs.mkdirSync('logs');
}
catch (e) {
if ( e.code != 'EEXIST' )
throw e;
}
// Create the default logger
defaultLogger = bunyan.createLogger({
name: 'default',
streams: [{
level: "info",
type: 'file',
path: 'logs/default.log',
},
{
level: "debug",
type: 'file',
path: 'logs/debug.log',
}],
serializers: {
req: requestSerializer,
res: responseSerializer
}
});
if (!cluster.isMaster) {
defaultLogger = defaultLogger.child({ worker: cluster.worker.id });
}
return defaultLogger;
}
else if (mode === "expressAccess") {
// Export the access logger (log express requests)
return function (req, res, next) {
// insert the current time into the response
// (for later calculation of reponse time).
res.start = new Date();
// Insert a log object into the request.
// The routes can use this object to log messages and all
// of them can be traced to the request (by req_id).
req.log = defaultLogger.child({ req_id: cuid.slug() });
// Log the request
req.log.debug({req: req}, 'request');
// Continue to the next handler
next();
// Log the response
req.log.debug({res: res}, 'response');
};
}
else if (mode === "expressError") {
// Export the express error logger (log errors during request processing)
return function (err, req, res, next) {
if (err) {
defaultLogger.error({err: err}, 'Error while processing request');
}
next();
};
}
else {
defaultLogger = bunyan.createLogger({
name: 'stdout',
stream: process.stdout,
serializers: {
req: requestSerializer,
res: responseSerializer
}
});
if (!cluster.isMaster) {
defaultLogger = defaultLogger.child({ worker: cluster.worker.id });
}
return defaultLogger;
}
};
var pg = require('pg'),
copyFrom = require('pg-copy-streams').from,
fs = require('fs'),
_ = require('lodash');
var tokenPattern = /\$[a-zA-Z]([a-zA-Z0-9_]*)\b/g;
function numericFromNamed(sql, parameters) {
var fillableTokens = Object.keys(parameters);
var matchedTokens = _.uniq(_.map(sql.match(tokenPattern), function (token) {
return token.substring(1); // Remove leading dollar sign
}));
var fillTokens = _.intersection(fillableTokens, matchedTokens).sort();
var fillValues = _.map(fillTokens, function (token) {
return parameters[token];
});
var unmatchedTokens = _.difference(matchedTokens, fillableTokens);
if (unmatchedTokens.length) {
var missing = unmatchedTokens.join(", ");
throw new Error("Missing Parameters: " + missing);
}
var interpolatedSql = _.reduce(fillTokens, function (partiallyInterpolated, token, index) {
var replaceAllPattern = new RegExp('\\$' + fillTokens[index] + '\\b', "g");
return partiallyInterpolated.replace(replaceAllPattern, '$' + (index+1));
}, sql);
var out = {};
out.sql = interpolatedSql;
out.values = fillValues;
return out;
}
function namedPatch(client) {
var originalQuery = client.query;
if (originalQuery.patched) return client;
originalQuery = originalQuery.bind(client);
var patchedQuery = function(config, values, callback) {
if (arguments.length === 1) {
return originalQuery(config);
}
else if (arguments.length === 2 && _.isFunction(values)) {
return originalQuery(config, values);
}
else if (_.isArray(values)) {
return originalQuery(config, values, callback);
} else {
var reparameterized = numericFromNamed(config, values);
return originalQuery(reparameterized.sql, reparameterized.values, callback);
}
};
client.query = patchedQuery;
client.query.patched = true;
return client;
}
exports.pool = function (config) {
return function (req, res, next) {
req.db = {
connect: function (callback) {
pg.on('error', function(err) {
return;
});
pg.connect(config, function (err, client, done) {
if (err) {
setTimeout(req.db.connect, 5000, callback);
return;
}
namedPatch(client);
callback(null, new Connection(client), done);
});
}
};
next();
};
};
function Connection(client) {
this.query = function (query, params, callback) {
return client.query(query, params, callback);
};
this.execute = function (query, params) {
var $this = this;
return function (callback) {
$this.query(query, params, function (err) {
callback(err);
});
};
};
this.queryFromFile = function(file, params, callback) {
var queries_dir = __dirname + "/../queries/";
fs.readFile(queries_dir + file, 'utf8', function (err, data) {
if (err) {
return callback(err);
}
if (typeof params === 'undefined') {
params = [];
}
client.query(data, params, callback);
});
};
this.executeFromFile = function(file, params) {
var $this = this;
return function (callback) {
$this.queryFromFile(file, params, function (err) {
callback(err);
});
};
};
this.copyFrom = function (query, callback) {
var stream = client.query(copyFrom(query));
stream.on('end', callback);
stream.on('error', callback);
return stream;
};
this.transaction = function (callback) {
client.query('BEGIN;', function (err) {
callback(err);
});
};
this.commit = function (callback) {
client.query('COMMIT;', function (err) {
callback(err);
});
};
this.rollback = function (callback) {
client.query('ROLLBACK;', function (err) {
callback(err);
});
};
this.auth = function (user, ip, action, comment) {
return function (callback) {
if (!(user && user.id))
return callback('not_logged_in');
client.query('SELECT auth.user_action($1, $2, $3, $4);',
[user.id, ip, action, comment],
function (err) {
callback(err);
});
};
};
};
/*
* Copyright (C) 2012-2017 Centro de Computacao Cientifica e Software Livre
* Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
*
* This file is part of simmc
*
* simmc is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
var async = require('async');
var exec = require('child_process').exec;
function isSubSet (array1, array2) {
return array1.every(function(item1) {
return array2.some(function(item2) {
return item1 == item2;
});
});
}
function dateFormat (date) {
if (!date) {
return null;
}
return date.getFullYear() + '-' +
("0" + (date.getMonth() + 1)).slice(-2) + '-' +
("0" + date.getDate()).slice(-2);
}
function timeFormat (date) {
if (!date) {
return null;
}
return ("0" + date.getHours()).slice(-2) + ':' +
("0" + date.getMinutes()).slice(-2) + ':' +
("0" + date.getSeconds()).slice(-2);
}
function buildAgent(id_point, config, os, proxy, callback) {
var buildScript = '',
proxyParamsWin = '',
proxyParamsLinux = '',
outputDir = '',
agentName = '';
var cmdline = '';
var params = '';
if (isNaN(id_point)) {
return callback({error: 'invalid_id_point'});
}
if (proxy) {
// the replace method removes whitespaces that are not allowed in this fields
var host = (proxy.host || '').replace(/ /g, ''),
port = (proxy.port || '').replace(/ /g, ''),
user = (proxy.user || '').replace(/ /g, ''),
pass = (proxy.pass || '').replace(/ /g, '');
// check if the port number is actually a number or an empty string
if ( (port !== '') && (isNaN(parseInt(port),10)) ) {
return callback({error: 'invalid_proxy_port'});
}
proxyParamsLinux = '-pch="'+host+'" -pcp="'+port+'" -pcu="'+user+'" -pcpssd"'+pass+'"';
proxyParamsWin = '--Dpch="'+host+'" --Dpcp="'+port+'" --Dpcu="'+user+'" --Dpcpssd"'+pass+'"';
}
if (os === 'windows') {
var optName = 'agent-' + id_point;
buildScript = __dirname + '/../node_modules/.bin/innosetup-compiler ' +
'z:' + __dirname + '/../agent/generate_agent.iss --verbose' ;
outputLocation = 'z:' + config.outputDir + '/../agent';
params = ' --Ddest="' + outputLocation +
'" --Dversion="' + config.version +
'" --Dws="' + config.webservice +
'" --DoutputName="' + optName +
'" --Didp="' + id_point + '" ' + proxyParamsWin;
agentName = optName + '.exe';
}
else {
buildScript = __dirname + '/../agent/generate_agent.sh';
agentName = 'agent-' + id_point + '.run';
outputLocation = config.outputDir + '/' + agentName;
params = ' -dest="' + outputLocation +
'" -ver="' + config.version +
'" -ws="' + config.webservice +
'" -idp="' + id_point + '" ' + proxyParamsLinux;
}
cmdline = buildScript + params;
exec(cmdline, function (err, stdout, stderr) {
return callback(err, agentName);
});
}
var routes = {
"net_usage": {
validation: function(body) {
if(!body) {
return false;
}
else {
var keys = [
'id_point',
'mac_address',
'bytes_received',
'bytes_transmitted',
'collect_date',
'collect_time',
'packets_received',
'packets_transmitted',
];
return isSubSet(keys, Object.keys(body));
}
},
parser: function(body) {
var time = body.collect_date + " " + body.collect_time;
return [
dateFormat(new Date()),
body.id_point,
body.mac_address,
(body.collect_date)? dateFormat(new Date(body.collect_date)): null,
(body.collect_time)? timeFormat(new Date(time)): null,
body.bytes_received,
body.packets_received,
body.bytes_transmitted,
body.packets_transmitted,
];
},
query: 'collect/net_usage.sql',
field: 'data_net'
},
"inventory": {
validation: function(body) {
if(!body) {
return false;
}
else {
var keys = [
'id_point',
'agent_version',
'amount_users',
'disk1_model',
'disk1_size',
'disk1_used',
'disk2_model',
'disk2_size',
'extra_hds',
'mac_address',
'machine_type',
'memory',
'os_distro',
'os_kernel',
'os_type',
'processor'
];
return isSubSet(keys, Object.keys(body));
}
},
parser: function(body) {
return [
body.id_point,
(body.machine_type === "server") ? 1 : 0,
body.mac_address,
body.agent_version,
body.os_type,
body.os_distro,
body.os_kernel,
body.processor,
body.memory,
body.disk1_model,
body.disk1_size,
body.disk1_used,
body.disk2_model,
body.disk2_size,
null,
body.extra_hds,
null,
body.amount_users
];
},
query: 'collect/inventory.sql',
field: 'data_inventory'
}
};