Skip to content

Commit

Permalink
Use CMD for SSL generator, Update Docs, Add option.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ezra Sharp committed Feb 9, 2017
1 parent 196d73d commit e059e78
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 72 deletions.
77 changes: 45 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,31 @@ Powered by [Hoxy](https://github.com/greim/hoxy) the Web-hacking proxy API for n
## Requirements

- `Node.js` >= 6.9.0.
- `OpenSSL` (Optional).

`OpenSSL` is not required, but you can use it if you want to.

## Install

`npm install` will pull in the dependencies and generate the SSL certificate.
**Clone the Repo**
`git clone git@github.com:nicekiwi/development-proxy.git`

**Install the dependencies**
`npm install`.

## Usage

By default the development-proxy listens on HTTPS for maximum compatibility.

## Useage
**Generate SSL Certificate**
`npm run generate` or `node index.js --generate`.

Run with `npm start`.
If you want to use `OpenSSL` instead of `node-forge` to generate the certificate, run `node index.js --generate=openssl` instead.

Setup your system proxy to point HTTP and HTTPS to localhost and port `8889` (by default). If you used a custom port, you would need set that here too.
**Start the Proxy**
`npm start` or `node index.js`.

**Setup your system's proxy**
Setup your system proxy to point `HTTP` and `HTTPS` to `localhost` and port `8889` (by default). If you set a custom port in your `./config.json` file, you would need set that here too.

## Config

Expand All @@ -28,54 +42,53 @@ The following options are available in `./config.josn`.
| Option | Type | Default | Description |
| :-- | :-- | :-- | :-- |
| `port` | Number | `8889` | The port to listen on. |
| `generator` | String | `"forge"` | Which generator to use to create the SSL keys, `forge` or `openssl`. |
| `certificate` | String | `forge` | Which certificate to use, `forge` or `openssl`. |
| `logToFile` | Boolean | `false` | Weather or not to save the log to file `./logs/DD-MM-YYYY.log`. |
| `replaceRemote ` | Boolean | `false` | Weather or not to replace the remote file with the local file, if `false` and the file is not found locally; the proxy will return the remote file. If `true` and the file is not found locally; the proxy will return a 404 response. |
| `files ` | Array | `[]` | The files to replace. |
| `strict` | Boolean | `true` | `true` will return a 404 response if the local file is not found, `false` will return the remote file instead. |
| `localRoot` | String | `/` | *(Optional)* The absolute path to where local files should be served from, defaults to the local drive's root, e.g. `/` or `c:/`. |
| `files` | Array | `[]` | The files to replace. |

**Files Array Object:**

| Option | Type | Description |
| :-- | :-- | :-- |
| `remote` | String | The remote file to intercept. |
| `local` | String | The absolute path from the disk drive's root, e.g. `/` or `c:/` of the local file to return. |

### Example

...,
"files": [
{
remote: "https://www.example.com/js/app-1.js?v=33",
local: "c:/files/app-1.js"
},
| `remote` | String | The remote file to intercept *(can use basic pattern matching)*. |
| `local` | String | Relative to the localRoot *(if set)* or to the absolute path from the disk drive's root, e.g. `/` or `c:/` of the local file to return. |

#### Example

{
"port": 8889,
"certificate": "forge",
"logToFile": true,
"strict": true,
"localRoot": "C:/Users/Ezra/Documents/Developer",
"files": [
{
remote: "https://www.example.com/js/app-2.js*",
local: "c:/files/app-2.js"
"remote":"https://nice.kiwi/assets/stylesheets/app.css",
"local":"/nice-kiwi-blog/assets/stylesheets/app.css"
},
{
"remote":"https://nice.kiwi/assets/javascript/app.js",
"local":"/nice-kiwi-blog/assets/javascript/app.js"
}
]


### HTTPS
]
}

Most sites should be using SSL by now, so we listen with HTTP by default.

A private key and certificate were created for you during installation. If you do not see them in `./certificate`, run `npm install` again.

By default `openssl` is not required and the certificate is created with `node-forge`, but you can set the installer to use `openssl` in the conifg file if you prefer.

### Trust the Certificate

To avoid warnings about self-signed certificates, you must instruct your system to trust the certificate. You should only need to do this once (assuming you only generate the certificate once).

#### Windows

`// ToDo`
[Manage Trusted Root Certificates in Windows 10 / 8](http://www.thewindowsclub.com/manage-trusted-root-certificates-windows)

#### Mac

1. Open the `./certificate` directory.
2. Double click the file with the extension `.crt.pem` to add it to the Keychain Access app under the `System` keychain.
3. Then find it by the name "development.proxy" and double click it to edit it
3. Then find it by the name `development.proxy` and double click it to edit it
4. Under `Trust` > `When using this certificate` set it to `Always Trust`.
5. Close the Keychain Access app.
6. Restart your browser and the certificate should be trusted.
Expand Down
5 changes: 3 additions & 2 deletions config.example.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"port": 8889,
"generator": "forge",
"certificate": "forge",
"logToFile": false,
"replaceRemote": false,
"strict": true,
"localRoot": "/",
"files": [
{
"remote":"path/to/remote/file",
Expand Down
16 changes: 14 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,17 @@
require('babel-core/register');
require('babel-polyfill');

// Load App
require('./src/app.js');
// get cmd arguments (if any)
const yargs = require('yargs').argv;

// pick an option
switch (yargs.generate) {
case 'openssl':
require('./src/install-openssl.js');
break;
case true:
require('./src/install-forge.js');
break;
default:
require('./src/app.js');
}
8 changes: 0 additions & 8 deletions install.js

This file was deleted.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"main": "index.js",
"author": "Ezra Sharp <ezra@kiwidev.co.nz>",
"scripts": {
"install": "node install.js",
"update": "node install.js",
"generate": "node index.js --generate",
"start": "node index.js"
},
"repository": {
Expand All @@ -24,11 +23,14 @@
"babel-core": "^6.22.1",
"babel-polyfill": "^6.22.0",
"babel-preset-env": "^1.1.8",
"file-exists": "^3.0.1",
"hoxy": "^3.2.1",
"moment": "^2.17.1",
"node-forge": "^0.6.48",
"readline-sync": "^1.4.6",
"upath": "^1.0.0",
"winston": "^2.3.1"
"winston": "^2.3.1",
"yargs": "^6.6.0"
},
"babel": {
"presets": [
Expand Down
48 changes: 30 additions & 18 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,53 @@
import fs from 'fs';
import hoxy from 'hoxy';
import upath from 'upath';
import fileExists from 'file-exists';
import logger from './logger';

// Load Config
import config from '../config.json';

const path = './certificate';
const filename = `${config.generator}-root-ca`;
// validate config
const options = {
port: config.port || 8889,
certificate: config.certificate || 'forge',
logToFile: config.logToFile || false,
strict: config.strict || false,
localRoot: config.localRoot || '/',
files: config.files || []
};

// Get the private key and certificate
const key = fs.readFileSync(`${path}/${config.generator}-root-ca.key.pem`);
const cert = fs.readFileSync(`${path}/${config.generator}-root-ca.crt.pem`);
const path = upath.join(__dirname, '../certificate');
const filename = `${options.certificate}-root-ca`;

if(!key || !cert) {
logger.error('You must create keys first.');
// Get the private key and certificate
if(!fileExists.sync(`${path}/${filename}.crt.pem`)) {
logger.error('A Certificate file was not found in ./certificate.');
process.exit(1);
}

if(!config.files.length) {
logger.error('No files defined in ./config.json');
if(!options.files.length) {
logger.error('No files have been defined in ./config.json');
process.exit(1);
}

// Create Hoxy instance
const proxy = hoxy.createServer({
certAuthority: { key, cert }
certAuthority: {
key: fs.readFileSync(`${path}/${filename}.key.pem`),
cert: fs.readFileSync(`${path}/${filename}.crt.pem`)
}
});

// Setup Error Events
proxy.on('error', (e) => {
switch (e.code) {
case 'EACCES':
logger.error(`Use of port ${config.port} requires elevated privileges.`);
logger.error(`Use of port ${options.port} requires elevated privileges.`);
process.exit(1);
break;
case 'EADDRINUSE':
logger.error(`Port ${config.port} is already in use.`);
logger.error(`Port ${options.port} is already in use.`);
process.exit(1);
break;
default:
Expand All @@ -47,7 +58,7 @@ proxy.on('error', (e) => {
});

// Setup Files
config.files.map((file, i) => {
options.files.map((file, i) => {

// Intercept files
proxy.intercept({
Expand All @@ -56,17 +67,18 @@ config.files.map((file, i) => {
}, (req, resp, cycle) => {

const path = upath.normalize(file.local);
const strategy = config.replaceRemote ? 'replace' : 'overlay';
const docroot = upath.normalize(options.localRoot);
const strategy = options.strict ? 'replace' : 'overlay';

logger.info(`Requested: ${file.remote}`);
logger.info(`Returned: ${path}`);
logger.info(`Returned: ${upath.join(docroot, path)}`);

return cycle.serve({ path, strategy });
return cycle.serve({ path, strategy, docroot });
});

});

// Start Hoxy
proxy.listen(config.port, () => {
logger.info(`Development Proxy listening on https://localhost:${config.port}.`);
proxy.listen(options.port, () => {
logger.info(`Development Proxy listening on https://localhost:${options.port}.`);
});
18 changes: 18 additions & 0 deletions src/check-existing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fileExists from 'file-exists';
import readlineSync from 'readline-sync';
import logger from './logger';

export default (path) => {

// Check if previous certificate exists
if(fileExists.sync(path)) {

// if so ask to overwrite it
if(!readlineSync.keyInYN('Certificate already exists, overwrite?')) {
logger.info('Certificate has not been over-written.');
process.exit(1);
} else {
logger.info('OK, Overwriting previous Certificate.');
}
}
};
11 changes: 8 additions & 3 deletions src/install-forge.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
// Load Deps
import fs from 'fs';
import forge from 'node-forge';
import checkExisting from './check-existing';
import logger from './logger';

logger.info('Generating Keys..');
const path = './certificate';
const filename = 'forge-root-ca';

logger.info('Generating Certificate - Via Forge');

// Check if previous certificate exists
checkExisting(`${path}/${filename}.crt.pem`);

const pki = forge.pki;
const keys = pki.rsa.generateKeyPair(2048);
const cert = pki.createCertificate();
const path = './certificate';
const filename = 'forge-root-ca';

const attrs = [
{ name: 'commonName', value: 'development.proxy' },
Expand Down
6 changes: 5 additions & 1 deletion src/install-openssl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Load Deps
import async from 'async';
import { exec } from 'child_process';
import checkExisting from './check-existing';
import logger from './logger';

const filename = 'openssl-root-ca';
Expand All @@ -23,7 +24,10 @@ const execute = (command, callback) => {
});
};

logger.info('Generating Keys..');
// Check if previous certificate exists
checkExisting(`${path}/${filename}.crt.pem`);

logger.info('Generating Certificate - Via OpenSSL');

async.mapSeries(commands, execute, (err, results) => {

Expand Down
7 changes: 4 additions & 3 deletions src/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ const logger = new (winston.Logger)({
});

// Setup Logging to file
if(config.logToFile === true) {
logger.add(winston.transports.File, { filename: `./logs/${moment().format('DD-MM-YYYY')}.log` });
logger.info('(Logging to file enabled)');
if(config.logToFile && config.logToFile === true) {
const filename = `./logs/${moment().format('DD-MM-YYYY')}.log`;
logger.add(winston.transports.File, { filename });
logger.info(`Saving log output to: ${filename}.`);
}

export default logger;

0 comments on commit e059e78

Please sign in to comment.