removed config in favor of env vars

This commit is contained in:
Dave Horton
2020-02-15 18:42:42 -05:00
parent 0e88982adc
commit a8561cba43
10 changed files with 104 additions and 138 deletions

55
app.js
View File

@@ -1,11 +1,45 @@
const assert = require('assert');
assert.ok(process.env.JAMBONES_MYSQL_HOST &&
process.env.JAMBONES_MYSQL_USER &&
process.env.JAMBONES_MYSQL_PASSWORD &&
process.env.JAMBONES_MYSQL_DATABASE, 'missing JAMBONES_MYSQL_XXX env vars');
assert.ok(process.env.JAMBONES_REDIS_HOST, 'missing JAMBONES_REDIS_HOST env var');
assert.ok(process.env.DRACHTIO_PORT || process.env.DRACHTIO_HOST, 'missing DRACHTIO_PORT env var');
assert.ok(process.env.DRACHTIO_SECRET, 'missing DRACHTIO_SECRET env var');
assert.ok(process.env.JAMBONES_RTPENGINES, 'missing JAMBONES_RTPENGINES env var');
assert.ok(process.env.JAMBONES_FEATURE_SERVERS, 'missing JAMBONES_FEATURE_SERVERS env var');
const Srf = require('drachtio-srf');
const srf = new Srf();
const config = require('config');
const logger = require('pino')(config.get('logging'));
const opts = Object.assign({
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
const logger = require('pino')(opts);
const {
lookupAuthHook,
lookupSipGatewayBySignalingAddress
} = require('jambonz-db-helpers')(config.get('mysql'), logger);
} = require('jambonz-db-helpers')({
host: process.env.JAMBONES_MYSQL_HOST,
user: process.env.JAMBONES_MYSQL_USER,
password: process.env.JAMBONES_MYSQL_PASSWORD,
database: process.env.JAMBONES_MYSQL_DATABASE,
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
}, logger);
// parse rtpengines
srf.locals.rtpEngines = process.env.JAMBONES_RTPENGINES
.split(',')
.map((hp) => {
const arr = /^(.*):(.*)$/.exec(hp.trim());
if (arr) return {host: arr[1], port: parseInt(arr[2])};
});
assert.ok(srf.locals.rtpEngines.length > 0, 'JAMBONES_RTPENGINES must be an array host:port addresses');
// parse application servers
srf.locals.featureServers = process.env.JAMBONES_FEATURE_SERVERS
.split(',')
.map((hp) => hp.trim());
srf.locals.dbHelpers = {
lookupAuthHook,
lookupSipGatewayBySignalingAddress
@@ -13,23 +47,14 @@ srf.locals.dbHelpers = {
const {challengeDeviceCalls} = require('./lib/middleware')(srf, logger);
const CallSession = require('./lib/call-session');
// disable logging in test mode
if (process.env.NODE_ENV === 'test') {
const noop = () => {};
logger.info = logger.debug = noop;
logger.child = () => {return {info: noop, error: noop, debug: noop};};
}
// config dictates whether to use outbound or inbound connections
if (config.has('drachtio.host')) {
srf.connect(config.get('drachtio'));
if (process.env.DRACHTIO_HOST) {
srf.connect({host: process.env.DRACHTIO_HOST, port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET });
srf.on('connect', (err, hp) => {
logger.info(`connected to drachtio listening on ${hp}`);
});
}
else {
logger.info(`listening for drachtio server traffic on ${JSON.stringify(config.get('drachtio'))}`);
srf.listen(config.get('drachtio'));
srf.listen({host: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET});
}
if (process.env.NODE_ENV === 'test') {
srf.on('error', (err) => {

View File

@@ -1,38 +0,0 @@
{
"drachtio": {
"port": 3003,
"secret": "cymru"
},
"rtpengine": {
"host": "127.0.0.1",
"port": 22222
},
"logging": {
"level": "info"
},
"mysql": {
"host": "localhost",
"user": "jambones",
"password": "jambones",
"database": "jambones"
},
"trunks": {
"appserver": ["172.38.0.11"]
},
"transcoding": {
"rtpCharacteristics" : {
"transport protocol": "RTP/AVP",
"DTLS": "off",
"SDES": "off",
"ICE": "remove",
"rtcp-mux": ["demux"]
},
"srtpCharacteristics": {
"transport-protocol": "UDP/TLS/RTP/SAVPF",
"ICE": "force",
"SDES": "off",
"flags": ["generate mid", "SDES-no"],
"rtcp-mux": ["require"]
}
}
}

View File

@@ -1,39 +0,0 @@
{
"drachtio": {
"host": "127.0.0.1",
"port": 9060,
"secret": "cymru"
},
"rtpengine": {
"host": "127.0.0.1",
"port": 12222
},
"logging": {
"level": "debug"
},
"mysql": {
"host": "localhost",
"user": "jambones_test",
"password": "jambones_test",
"database": "jambones_test"
},
"trunks": {
"appserver": ["172.38.0.11"]
},
"transcoding": {
"rtpCharacteristics" : {
"transport protocol": "RTP/AVP",
"DTLS": "off",
"SDES": "off",
"ICE": "remove",
"rtcp-mux": ["demux"]
},
"srtpCharacteristics": {
"transport-protocol": "UDP/TLS/RTP/SAVPF",
"ICE": "force",
"SDES": "off",
"flags": ["generate mid", "SDES-no"],
"rtcp-mux": ["require"]
}
}
}

View File

@@ -0,0 +1,7 @@
{
"transport protocol": "RTP/AVP",
"DTLS": "off",
"SDES": "off",
"ICE": "remove",
"rtcp-mux": ["demux"]
}

View File

@@ -0,0 +1,7 @@
{
"transport-protocol": "UDP/TLS/RTP/SAVPF",
"ICE": "force",
"SDES": "off",
"flags": ["generate mid", "SDES-no"],
"rtcp-mux": ["require"]
}

View File

@@ -1,10 +1,6 @@
const Emitter = require('events');
const config = require('config');
const Client = require('rtpengine-client').Client ;
const rtpengine = new Client();
const offer = rtpengine.offer.bind(rtpengine, config.get('rtpengine'));
const answer = rtpengine.answer.bind(rtpengine, config.get('rtpengine'));
const del = rtpengine.delete.bind(rtpengine, config.get('rtpengine'));
const {getAppserver, isWSS, makeRtpEngineOpts} = require('./utils');
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
const {parseUri, SipError} = require('drachtio-srf');
@@ -17,13 +13,19 @@ class CallSession extends Emitter {
this.res = res;
this.srf = req.srf;
this.logger = logger.child({callId: req.get('Call-ID')});
// TODO: we always using the first rtpengine here
// we should be load balancing, and doing some form of health checking
this.offer = rtpengine.offer.bind(rtpengine, this.srf.locals.rtpEngines[0]);
this.answer = rtpengine.answer.bind(rtpengine, this.srf.locals.rtpEngines[0]);
this.del = rtpengine.delete.bind(rtpengine, this.srf.locals.rtpEngines[0]);
}
async connect() {
this.rtpEngineOpts = makeRtpEngineOpts(this.req, isWSS(this.req), false);
this.rtpEngineResource = {destroy: del.bind(rtpengine, this.rtpEngineOpts.common)};
this.rtpEngineResource = {destroy: this.del.bind(rtpengine, this.rtpEngineOpts.common)};
const obj = parseUri(this.req.uri);
const appServer = getAppserver();
const appServer = getAppserver(this.srf);
let proxy, host, uri;
// replace host part of uri if its an ipv4 address, leave it otherwise
@@ -41,7 +43,7 @@ class CallSession extends Emitter {
try {
// rtpengine 'offer'
debug('sending offer command to rtpengine');
const response = await offer(this.rtpEngineOpts.offer);
const response = await this.offer(this.rtpEngineOpts.offer);
debug(`response from rtpengine to offer ${JSON.stringify(response)}`);
if ('ok' !== response.result) {
this.logger.error(`rtpengine offer failed with ${JSON.stringify(response)}`);
@@ -77,7 +79,7 @@ class CallSession extends Emitter {
localSdpA: async(sdp, res) => {
const opts = Object.assign({sdp, 'to-tag': res.getParsedHeader('To').params.tag},
this.rtpEngineOpts.answer);
const response = await answer(opts);
const response = await this.answer(opts);
if ('ok' !== response.result) {
this.logger.error(`rtpengine answer failed with ${JSON.stringify(response)}`);
throw new Error('rtpengine failed: answer');
@@ -123,7 +125,7 @@ class CallSession extends Emitter {
async _onReinvite(dlg, req, res) {
try {
let response = await offer(Object.assign({sdp: req.body}, this.rtpEngineOpts.offer));
let response = await this.offer(Object.assign({sdp: req.body}, this.rtpEngineOpts.offer));
if ('ok' !== response.result) {
res.send(488);
throw new Error(`_onReinvite: rtpengine failed: offer: ${JSON.stringify(response)}`);
@@ -131,7 +133,7 @@ class CallSession extends Emitter {
const sdp = await dlg.other.modify(response.sdp);
const opts = Object.assign({sdp, 'to-tag': res.getParsedHeader('To').params.tag},
this.rtpEngineOpts.answer);
response = await answer(opts);
response = await this.answer(opts);
if ('ok' !== response.result) {
res.send(488);
throw new Error(`_onReinvite: rtpengine failed: ${JSON.stringify(response)}`);

View File

@@ -1,20 +1,19 @@
const config = require('config');
const rtpCharacteristics = require('../data/rtp-transcoding');
const srtpCharacteristics = require('../data/srtp-transcoding');
let idx = 0;
function isWSS(req) {
return req.getParsedHeader('Via')[0].protocol.toLowerCase().startsWith('ws');
}
function getAppserver() {
const len = config.get('trunks.appserver').length;
return config.get('trunks.appserver')[ idx++ % len];
function getAppserver(srf) {
const len = srf.locals.featureServers.length;
return srf.locals.featureServers[ idx++ % len];
}
function makeRtpEngineOpts(req, srcIsUsingSrtp, dstIsUsingSrtp) {
const from = req.getParsedHeader('from');
const common = {'call-id': req.get('Call-ID'), 'from-tag': from.params.tag};
const rtpCharacteristics = config.get('transcoding.rtpCharacteristics');
const srtpCharacteristics = config.get('transcoding.srtpCharacteristics');
return {
common,
offer: Object.assign({'sdp': req.body, 'replace': ['origin', 'session-connection']}, common,

14
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "sbc-inbound",
"version": "0.1.3",
"version": "0.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -737,9 +737,9 @@
}
},
"drachtio-srf": {
"version": "4.4.27",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.27.tgz",
"integrity": "sha512-jMtIHaJCQrkAPdlPABbDLDeOXzPea+m7HCTQMeFV6kAD5dMEAfgrAq5+BfSPhidjySuI6954FvmQuWZo7aRSlw==",
"version": "4.4.28",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.28.tgz",
"integrity": "sha512-gY/wmH6JFmeEv2/jhwFbky1NYUmDwgIJzjNeGlDiAkQEA6GgcI/CFi5RRHAUzghpczTwXQNDXpARPB8QDWX1JA==",
"requires": {
"async": "^1.4.2",
"debug": "^3.1.0",
@@ -1758,9 +1758,9 @@
}
},
"jambonz-db-helpers": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/jambonz-db-helpers/-/jambonz-db-helpers-0.2.4.tgz",
"integrity": "sha512-qfMKvXv//UDGFveOmeC3Xq2jMvTP7Y1P4F3EPf7VAgD10/ipozLRdEx+o3HlyF9wOeP3syha9ofpnel8VYLGLA==",
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/jambonz-db-helpers/-/jambonz-db-helpers-0.3.2.tgz",
"integrity": "sha512-j7AEgts+Bj1CFPiM0estFmWmdDTZKWbkeIPY1QT3BR0cLClzjqo9fmdCzLoDtk/NWMy7IPNEQpVHzEejxFHq9g==",
"requires": {
"debug": "^4.1.1",
"mysql2": "^2.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "sbc-inbound",
"version": "0.1.3",
"version": "0.2.0",
"main": "app.js",
"engines": {
"node": ">= 10.16.0"
@@ -20,7 +20,7 @@
},
"scripts": {
"start": "node app",
"test": "NODE_ENV=test node test/ | ./node_modules/.bin/tap-spec",
"test": "NODE_ENV=test JAMBONES_MYSQL_HOST=localhost JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ | ./node_modules/.bin/tap-spec",
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
"jslint": "eslint app.js lib"
},
@@ -28,8 +28,8 @@
"config": "^3.2.4",
"debug": "^4.1.1",
"drachtio-fn-b2b-sugar": "0.0.12",
"drachtio-srf": "^4.4.27",
"jambonz-db-helpers": "^0.2.4",
"drachtio-srf": "^4.4.28",
"jambonz-db-helpers": "^0.3.2",
"jambonz-http-authenticator": "0.0.7",
"pino": "^5.14.0",
"rtpengine-client": "^0.0.8"

View File

@@ -1,5 +1,6 @@
/* SQLEditor (MySQL (2))*/
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `call_routes`;
@@ -15,14 +16,16 @@ DROP TABLE IF EXISTS `sip_gateways`;
DROP TABLE IF EXISTS `voip_carriers`;
DROP TABLE IF EXISTS `applications`;
DROP TABLE IF EXISTS `accounts`;
DROP TABLE IF EXISTS `applications`;
DROP TABLE IF EXISTS `service_providers`;
DROP TABLE IF EXISTS `webhooks`;
SET FOREIGN_KEY_CHECKS = 1;
CREATE TABLE IF NOT EXISTS `call_routes`
(
`call_route_sid` CHAR(36) NOT NULL UNIQUE ,
@@ -61,26 +64,26 @@ CREATE TABLE IF NOT EXISTS `voip_carriers`
PRIMARY KEY (`voip_carrier_sid`)
) ENGINE=InnoDB COMMENT='A Carrier or customer PBX that can send or receive calls';
CREATE TABLE IF NOT EXISTS `webhooks`
(
`webhook_sid` CHAR(36) NOT NULL UNIQUE ,
`url` VARCHAR(255) NOT NULL,
`method` ENUM("get","post") NOT NULL DEFAULT 'post',
`username` VARCHAR(255),
`password` VARCHAR(255),
PRIMARY KEY (`webhook_sid`)
) COMMENT='An HTTP callback';
CREATE TABLE IF NOT EXISTS `phone_numbers`
(
`phone_number_sid` CHAR(36) UNIQUE ,
`number` VARCHAR(255) NOT NULL UNIQUE ,
`number` VARCHAR(32) NOT NULL UNIQUE ,
`voip_carrier_sid` CHAR(36) NOT NULL,
`account_sid` CHAR(36),
`application_sid` CHAR(36),
PRIMARY KEY (`phone_number_sid`)
) ENGINE=InnoDB COMMENT='A phone number that has been assigned to an account';
CREATE TABLE IF NOT EXISTS `webhooks`
(
`webhook_sid` CHAR(36) NOT NULL UNIQUE ,
`url` VARCHAR(1024) NOT NULL,
`method` ENUM("GET","POST") NOT NULL DEFAULT 'POST',
`username` VARCHAR(255),
`password` VARCHAR(255),
PRIMARY KEY (`webhook_sid`)
) COMMENT='An HTTP callback';
CREATE TABLE IF NOT EXISTS `lcr_carrier_set_entry`
(
`lcr_carrier_set_entry_sid` CHAR(36),
@@ -106,7 +109,7 @@ PRIMARY KEY (`sip_gateway_sid`)
CREATE TABLE IF NOT EXISTS `applications`
(
`application_sid` CHAR(36) NOT NULL UNIQUE ,
`name` VARCHAR(255) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`account_sid` CHAR(36) NOT NULL COMMENT 'account that this application belongs to',
`call_hook_sid` CHAR(36) COMMENT 'webhook to call for inbound calls to phone numbers owned by this account',
`call_status_hook_sid` CHAR(36) COMMENT 'webhook to call for call status events',
@@ -122,7 +125,7 @@ CREATE TABLE IF NOT EXISTS `service_providers`
`service_provider_sid` CHAR(36) NOT NULL UNIQUE ,
`name` VARCHAR(64) NOT NULL UNIQUE ,
`description` VARCHAR(255),
`root_domain` VARCHAR(255) UNIQUE ,
`root_domain` VARCHAR(128) UNIQUE ,
`registration_hook_sid` CHAR(36),
PRIMARY KEY (`service_provider_sid`)
) ENGINE=InnoDB COMMENT='A partition of the platform used by one service provider';
@@ -130,11 +133,11 @@ PRIMARY KEY (`service_provider_sid`)
CREATE TABLE IF NOT EXISTS `accounts`
(
`account_sid` CHAR(36) NOT NULL UNIQUE ,
`name` VARCHAR(255) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`sip_realm` VARCHAR(132) UNIQUE COMMENT 'sip domain that will be used for devices registering under this account',
`service_provider_sid` CHAR(36) NOT NULL COMMENT 'service provider that owns the customer relationship with this account',
`registration_hook_sid` CHAR(36) COMMENT 'webhook to call when devices underr this account attempt to register',
`device_calling_hook_sid` CHAR(36) COMMENT 'webhook to call when devices under this account place outbound calls',
`device_calling_application_sid` CHAR(36) COMMENT 'application to use for outbound calling from an account',
`is_active` BOOLEAN NOT NULL DEFAULT true,
PRIMARY KEY (`account_sid`)
) ENGINE=InnoDB COMMENT='An enterprise that uses the platform for comm services';
@@ -157,7 +160,6 @@ ALTER TABLE `voip_carriers` ADD FOREIGN KEY account_sid_idxfk_2 (`account_sid`)
ALTER TABLE `voip_carriers` ADD FOREIGN KEY application_sid_idxfk_1 (`application_sid`) REFERENCES `applications` (`application_sid`);
CREATE INDEX `webhooks_webhook_sid_idx` ON `webhooks` (`webhook_sid`);
CREATE INDEX `phone_numbers_phone_number_sid_idx` ON `phone_numbers` (`phone_number_sid`);
CREATE INDEX `phone_numbers_voip_carrier_sid_idx` ON `phone_numbers` (`voip_carrier_sid`);
ALTER TABLE `phone_numbers` ADD FOREIGN KEY voip_carrier_sid_idxfk (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`);
@@ -166,6 +168,7 @@ ALTER TABLE `phone_numbers` ADD FOREIGN KEY account_sid_idxfk_3 (`account_sid`)
ALTER TABLE `phone_numbers` ADD FOREIGN KEY application_sid_idxfk_2 (`application_sid`) REFERENCES `applications` (`application_sid`);
CREATE INDEX `webhooks_webhook_sid_idx` ON `webhooks` (`webhook_sid`);
ALTER TABLE `lcr_carrier_set_entry` ADD FOREIGN KEY lcr_route_sid_idxfk (`lcr_route_sid`) REFERENCES `lcr_routes` (`lcr_route_sid`);
ALTER TABLE `lcr_carrier_set_entry` ADD FOREIGN KEY voip_carrier_sid_idxfk_1 (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`);
@@ -198,4 +201,4 @@ ALTER TABLE `accounts` ADD FOREIGN KEY service_provider_sid_idxfk_1 (`service_pr
ALTER TABLE `accounts` ADD FOREIGN KEY registration_hook_sid_idxfk_1 (`registration_hook_sid`) REFERENCES `webhooks` (`webhook_sid`);
ALTER TABLE `accounts` ADD FOREIGN KEY device_calling_hook_sid_idxfk (`device_calling_hook_sid`) REFERENCES `webhooks` (`webhook_sid`);
ALTER TABLE `accounts` ADD FOREIGN KEY device_calling_application_sid_idxfk (`device_calling_application_sid`) REFERENCES `applications` (`application_sid`);