From 68432e7ab7cb0034abeef736aedd6146ac77dd3d Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Fri, 25 Jan 2019 14:54:21 -0500 Subject: [PATCH] Adds dev flag (#962) * Adds dev mode when running with docker, as the local IP address is different from the dashboard all the security features would light up. This --dev flag let anyone run the dashboard locally easily * Update index.js --- Dockerfile | 2 ++ Parse-Dashboard/app.js | 19 ++++++++++--------- Parse-Dashboard/index.js | 31 +++++++++++++++++++++++++++---- README.md | 16 ++++++++++------ 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d0f04ee50..beab1baee8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,4 +30,6 @@ COPY --from=base /src/package*.json /src/ # Copy compiled src dirs COPY --from=base /src/Parse-Dashboard/ /src/Parse-Dashboard/ +USER node + ENTRYPOINT ["node", "Parse-Dashboard/index.js"] diff --git a/Parse-Dashboard/app.js b/Parse-Dashboard/app.js index 6cba205e9a..52e747a368 100644 --- a/Parse-Dashboard/app.js +++ b/Parse-Dashboard/app.js @@ -88,16 +88,17 @@ module.exports = function(config, options) { req.connection.remoteAddress === '127.0.0.1' || req.connection.remoteAddress === '::ffff:127.0.0.1' || req.connection.remoteAddress === '::1'; - if (!requestIsLocal && !req.secure && !options.allowInsecureHTTP) { - //Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext - return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' }); - } + if (!options.dev && !requestIsLocal) { + if (!req.secure && !options.allowInsecureHTTP) { + //Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext + return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' }); + } - if (!requestIsLocal && !users) { - //Accessing the dashboard over the internet can only be done with username and password - return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' }); + if (!users) { + //Accessing the dashboard over the internet can only be done with username and password + return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' }); + } } - const authentication = req.user; const successfulAuth = authentication && authentication.isAuthenticated; @@ -139,7 +140,7 @@ module.exports = function(config, options) { //They didn't provide auth, and have configured the dashboard to not need auth //(ie. didn't supply usernames and passwords) - if (requestIsLocal) { + if (requestIsLocal || options.dev) { //Allow no-auth access on localhost only, if they have configured the dashboard to not need auth return res.json(response); } diff --git a/Parse-Dashboard/index.js b/Parse-Dashboard/index.js index 3645fadf81..782a783884 100644 --- a/Parse-Dashboard/index.js +++ b/Parse-Dashboard/index.js @@ -16,6 +16,7 @@ const program = require('commander'); program.option('--appId [appId]', 'the app Id of the app you would like to manage.'); program.option('--masterKey [masterKey]', 'the master key of the app you would like to manage.'); program.option('--serverURL [serverURL]', 'the server url of the app you would like to manage.'); +program.option('--dev', 'Enable development mode. This will disable authentication and allow non HTTPS connections. DO NOT ENABLE IN PRODUCTION SERVERS'); program.option('--appName [appName]', 'the name of the app you would like to manage. Optional.'); program.option('--config [config]', 'the path to the configuration file'); program.option('--host [host]', 'the host to run parse-dashboard'); @@ -35,6 +36,7 @@ const mountPath = program.mountPath || process.env.MOUNT_PATH || '/'; const allowInsecureHTTP = program.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; const cookieSessionSecret = program.cookieSessionSecret || process.env.PARSE_DASHBOARD_COOKIE_SESSION_SECRET; const trustProxy = program.trustProxy || process.env.PARSE_DASHBOARD_TRUST_PROXY; +const dev = program.dev; if (trustProxy && allowInsecureHTTP) { console.log("Set only trustProxy *or* allowInsecureHTTP, not both. Only one is needed to handle being behind a proxy."); @@ -52,6 +54,25 @@ let configUserId = program.userId || process.env.PARSE_DASHBOARD_USER_ID; let configUserPassword = program.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD; let configSSLKey = program.sslKey || process.env.PARSE_DASHBOARD_SSL_KEY; let configSSLCert = program.sslCert || process.env.PARSE_DASHBOARD_SSL_CERT; + +function handleSIGs(server) { + const signals = { + 'SIGINT': 2, + 'SIGTERM': 15 + }; + function shutdown(signal, value) { + server.close(function () { + console.log('server stopped by ' + signal); + process.exit(128 + value); + }); + } + Object.keys(signals).forEach(function (signal) { + process.on(signal, function () { + shutdown(signal, signals[signal]); + }); + }); +} + if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) { if (configServerURL && configMasterKey && configAppId) { configFromCLI = { @@ -114,14 +135,15 @@ p.then(config => { const app = express(); - if (allowInsecureHTTP || trustProxy) app.enable('trust proxy'); + if (allowInsecureHTTP || trustProxy || dev) app.enable('trust proxy'); config.data.trustProxy = trustProxy; - let dashboardOptions = { allowInsecureHTTP: allowInsecureHTTP, cookieSessionSecret: cookieSessionSecret }; + let dashboardOptions = { allowInsecureHTTP, cookieSessionSecret, dev }; app.use(mountPath, parseDashboard(config.data, dashboardOptions)); + let server; if(!configSSLKey || !configSSLCert){ // Start the server. - const server = app.listen(port, host, function () { + server = app.listen(port, host, function () { console.log(`The dashboard is now available at http://${server.address().address}:${server.address().port}${mountPath}`); }); } else { @@ -130,13 +152,14 @@ p.then(config => { var privateKey = fs.readFileSync(configSSLKey); var certificate = fs.readFileSync(configSSLCert); - const server = require('https').createServer({ + server = require('https').createServer({ key: privateKey, cert: certificate }, app).listen(port, host, function () { console.log(`The dashboard is now available at https://${server.address().address}:${server.address().port}${mountPath}`); }); } + handleSIGs(server); }, error => { if (error instanceof SyntaxError) { console.log('Your config file contains invalid JSON. Exiting.'); diff --git a/README.md b/README.md index 1b834f7432..fc2151506d 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,13 @@ npm install -g parse-dashboard You can launch the dashboard for an app with a single command by supplying an app ID, master key, URL, and name like this: ``` -parse-dashboard --appId yourAppId --masterKey yourMasterKey --serverURL "https://example.com/parse" --appName optionalName +parse-dashboard --dev --appId yourAppId --masterKey yourMasterKey --serverURL "https://example.com/parse" --appName optionalName ``` You may set the host, port and mount path by supplying the `--host`, `--port` and `--mountPath` options to parse-dashboard. You can use anything you want as the app name, or leave it out in which case the app ID will be used. +NB: the `--dev` parameter is disabling production-ready security features, do not use this parameter when starting the dashboard in production. This parameter is useful is you are running on docker. + After starting the dashboard, you can visit http://localhost:4040 in your browser: ![Parse Dashboard](.github/dash-shot.png) @@ -451,16 +453,18 @@ You can provide a list of locales or languages you want to support for your dash ## Run with Docker -It is easy to use it with Docker. First build the image: +The official docker image is published on [docker hub](https://hub.docker.com/r/parseplatform/parse-dashboard) + +Run the image with your ``config.json`` mounted as a volume ``` -docker build -t parse-dashboard . +docker run -d -p 8080:4040 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parseplatform/parse-dashboard --dev ``` -Run the image with your ``config.json`` mounted as a volume +You can also pass the appId, masterKey and serverURL as arguments: ``` -docker run -d -p 8080:4040 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parse-dashboard +docker run -d -p 4040:4040 parseplatform/parse-dashboard --dev --appId $APP_ID --masterKey $MASTER_KEY --serverURL $SERVER_URL ``` By default, the container will start the app at port 4040 inside the container. However, you can run custom command as well (see ``Deploying in production`` for custom setup). @@ -468,7 +472,7 @@ By default, the container will start the app at port 4040 inside the container. In this example, we want to run the application in production mode at port 80 of the host machine. ``` -docker run -d -p 80:8080 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parse-dashboard --port 8080 +docker run -d -p 80:8080 -v host/path/to/config.json:/src/Parse-Dashboard/parse-dashboard-config.json parse-dashboard --port 8080 --dev ``` If you are not familiar with Docker, ``--port 8080`` will be passed in as argument to the entrypoint to form the full command ``npm start -- --port 8080``. The application will start at port 8080 inside the container and port ``8080`` will be mounted to port ``80`` on your host machine.