From f45d41a5e9a39da241be3c25fe85ac288980b300 Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Wed, 9 Feb 2022 20:59:37 +0100 Subject: [PATCH] [mirotalksfu] - improve host protection --- app/src/Host.js | 15 ++++++++ app/src/Server.js | 93 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 app/src/Host.js diff --git a/app/src/Host.js b/app/src/Host.js new file mode 100644 index 00000000..d4b52b29 --- /dev/null +++ b/app/src/Host.js @@ -0,0 +1,15 @@ +'use strict'; + +const Logger = require('./Logger'); +const log = new Logger('Host'); + +module.exports = class Host { + constructor(ip, authorized) { + this.auth = new Map(); + this.auth.set(ip, authorized); + //log.debug('AUTH ---> ', this.auth.get(ip)); + } + isAuthorized(ip) { + return this.auth.has(ip); + } +}; diff --git a/app/src/Server.js b/app/src/Server.js index 5e23b70d..259afd9c 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -9,6 +9,7 @@ const config = require('./config'); const path = require('path'); const ngrok = require('ngrok'); const fs = require('fs'); +const Host = require('./Host'); const Room = require('./Room'); const Peer = require('./Peer'); const ServerApi = require('./ServerApi'); @@ -42,6 +43,9 @@ const hostCfg = { const apiBasePath = '/api/v1'; // api endpoint path const api_docs = host + apiBasePath + '/docs'; // api docs +// Authenticated IP by Login +let authHost; + // all mediasoup workers let workers = []; let nextMediasoupWorkerIdx = 0; @@ -49,9 +53,25 @@ let nextMediasoupWorkerIdx = 0; // all Room lists let roomList = new Map(); +// directory +const dir = { + public: path.join(__dirname, '../../', 'public'), +}; + +// html views +const view = { + landing: path.join(__dirname, '../../', 'public/view/landing.html'), + login: path.join(__dirname, '../../', 'public/view/login.html'), + newRoom: path.join(__dirname, '../../', 'public/view/newroom.html'), + notFound: path.join(__dirname, '../../', 'public/view/404.html'), + permission: path.join(__dirname, '../../', 'public/view/permission.html'), + privacy: path.join(__dirname, '../../', 'public/view/privacy.html'), + room: path.join(__dirname, '../../', 'public/view/Room.html'), +}; + app.use(cors()); app.use(compression()); -app.use(express.static(path.join(__dirname, '../../', 'public'))); +app.use(express.static(dir.public)); // Remove trailing slashes in url handle bad requests app.use((err, req, res, next) => { @@ -74,44 +94,45 @@ app.use((err, req, res, next) => { // all start from here app.get(['/'], (req, res) => { if (hostCfg.protected == true) { - res.sendFile(path.join(__dirname, '../../', 'public/view/login.html')); + hostCfg.authenticated = false; + res.sendFile(view.login); } else { - res.sendFile(path.join(__dirname, '../../', 'public/view/landing.html')); + res.sendFile(view.landing); } }); // handle login on host protected app.get(['/login'], (req, res) => { - let ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress; + let ip = getIP(req); log.debug(`request login to host from: ${ip}`, req.query); if (req.query.username == hostCfg.username && req.query.password == hostCfg.password) { hostCfg.authenticated = true; - res.sendFile(path.join(__dirname, '../../', 'public/view/landing.html')); + authHost = new Host(ip, true); + log.debug('LOGIN OK', { ip: ip, authorized: authHost.isAuthorized(ip) }); + res.sendFile(view.landing); } else { - res.sendFile(path.join(__dirname, '../../', 'public/view/login.html')); + log.debug('LOGIN KO', { ip: ip, authorized: false }); + hostCfg.authenticated = false; + res.sendFile(view.login); } }); // set new room name and join app.get(['/newroom'], (req, res) => { + let ip = getIP(req); if (hostCfg.protected == true) { - res.sendFile(path.join(__dirname, '../../', 'public/view/login.html')); + if (allowedIP(ip)) { + res.sendFile(view.newRoom); + } else { + hostCfg.authenticated = false; + res.sendFile(view.login); + } } else { - res.sendFile(path.join(__dirname, '../../', 'public/view/newroom.html')); + res.sendFile(view.newRoom); } }); -// if not allow video/audio -app.get(['/permission'], (req, res) => { - res.sendFile(path.join(__dirname, '../../', 'public/view/permission.html')); -}); - -// privacy policy -app.get(['/privacy'], (req, res) => { - res.sendFile(path.join(__dirname, '../../', 'public/view/privacy.html')); -}); - -// no room name specified to join +// no room name specified to join || direct join app.get('/join/', (req, res) => { if (hostCfg.authenticated && Object.keys(req.query).length > 0) { log.debug('Direct Join', req.query); @@ -122,8 +143,7 @@ app.get('/join/', (req, res) => { let peerVideo = req.query.video; let notify = req.query.notify; if (roomName && peerName && peerAudio && peerVideo && notify) { - res.sendFile(path.join(__dirname, '../../', 'public/view/Room.html')); - return; + return res.sendFile(view.room); } } res.redirect('/'); @@ -136,13 +156,28 @@ app.get('/join/*', (req, res) => { log.debug('redirect:' + req.url + ' to ' + url.parse(req.url).pathname); res.redirect(url.parse(req.url).pathname); } else { - res.sendFile(path.join(__dirname, '../../', 'public/view/Room.html')); + res.sendFile(view.room); } } else { - res.sendFile(path.join(__dirname, '../../', 'public/view/login.html')); + res.redirect('/'); } }); +// if not allow video/audio +app.get(['/permission'], (req, res) => { + res.sendFile(view.permission); +}); + +// privacy policy +app.get(['/privacy'], (req, res) => { + res.sendFile(view.privacy); +}); + +// not match any of page before, so 404 not found +app.get('*', function (req, res) { + res.sendFile(view.notFound); +}); + // #################################################### // API // #################################################### @@ -203,11 +238,6 @@ app.post(['/api/v1/join'], (req, res) => { }); }); -// not match any of page before, so 404 not found -app.get('*', function (req, res) { - res.sendFile(path.join(__dirname, '../../', 'public/view/404.html')); -}); - // #################################################### // NGROK // #################################################### @@ -624,3 +654,10 @@ io.on('connection', (socket) => { return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; } }); + +function getIP(req) { + return req.headers['x-forwarded-for'] || req.socket.remoteAddress; +} +function allowedIP(ip) { + return authHost != null && authHost.isAuthorized(ip); +}