Skip to content

Commit 3946101

Browse files
committed
Merge pull request #66 from joeferner/wilcard
Wilcard certificates
2 parents 0632832 + 99fa497 commit 3946101

File tree

7 files changed

+183
-126
lines changed

7 files changed

+183
-126
lines changed

README.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,26 @@ __Arguments__
161161
* hostname - Requested hostname.
162162
* callback - The function to be called when certificate files' path were already computed.
163163

164-
__Example__
164+
__Example 1__
165165

166166
proxy.onCertificateRequired = function(hostname, callback) {
167167
return callback(null, {
168168
keyFile: path.resolve('/ca/certs/', hostname + '.key'),
169169
certFile: path.resolve('/ca/certs/', hostname + '.crt')
170-
});
170+
});
171+
};
172+
173+
__Example 2: Wilcard certificates__
174+
175+
proxy.onCertificateRequired = function(hostname, callback) {
176+
return callback(null, {
177+
keyFile: path.resolve('/ca/certs/', hostname + '.key'),
178+
certFile: path.resolve('/ca/certs/', hostname + '.crt'),
179+
hosts: ["*.mydomain.com"]
180+
});
171181
};
172182

183+
173184
<a name="proxy_onCertificateMissing" />
174185
### proxy.onCertificateMissing = function(ctx, files, callback)
175186

@@ -181,10 +192,10 @@ __Arguments__
181192
* hostname - The hostname which requires certificates
182193
* data.keyFileExists - Whether key file exists or not
183194
* data.certFileExists - Whether certificate file exists or not
184-
* files - missing files names (`files.keyFile` and `files.certFile`)
195+
* files - missing files names (`files.keyFile`, `files.certFile` and optional `files.hosts`)
185196
* callback - The function to be called to pass certificate data back (`keyFileData` and `certFileData`)
186197

187-
__Example__
198+
__Example 1__
188199

189200
proxy.onCertificateMissing = function(ctx, files, callback) {
190201
console.log('Looking for "%s" certificates', ctx.hostname);
@@ -200,6 +211,17 @@ __Example__
200211
// });
201212
};
202213

214+
__Example 2: Wilcard certificates__
215+
216+
proxy.onCertificateMissing = function(ctx, files, callback) {
217+
return callback(null, {
218+
keyFileData: keyFileData,
219+
certFileData: certFileData,
220+
hosts: ["*.mydomain.com"]
221+
});
222+
};
223+
224+
203225
<a name="proxy_onRequest" />
204226
### proxy.onRequest(fn) or ctx.onRequest(fn)
205227

@@ -424,6 +446,10 @@ __Example__
424446
onWebSocketClose: function(ctx, code, message, callback) { },
425447
});
426448

449+
node-http-mitm-proxy provide some ready to use modules:
450+
- `Proxy.gunzip` Gunzip response filter (uncompress gzipped content before onResponseData and compress back after)
451+
- `Proxy.wildcard` Generates wilcard certificates by default (so less certificates are generated)
452+
427453
<a name="context"/>
428454
## Context
429455

bin/mitm-proxy.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ if (args.help) {
2020
}
2121

2222
var proxy = require('../lib/proxy')();
23-
proxy.onError(function(ctx, err) {
23+
proxy.onError(function(ctx, err, errorKind) {
2424
if (!args.silent) {
25-
console.error('proxy error:', err);
25+
console.error(errorKind, err);
2626
}
2727
});
2828
proxy.listen(args);

examples/wildcard.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
var port = 8081;
4+
5+
var Proxy = require('../');
6+
var proxy = Proxy();
7+
8+
proxy.use(Proxy.wildcard);
9+
10+
proxy.onError(function(ctx, err, errorKind) {
11+
// ctx may be null
12+
var url = (ctx && ctx.clientToProxyRequest) ? ctx.clientToProxyRequest.url : '';
13+
console.error(errorKind + ' on ' + url + ':', err);
14+
});
15+
16+
proxy.listen({ port: port });
17+
console.log('listening on ' + port);

lib/ca.js

Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,6 @@ var CAextensions = [{
4848
sslCA: true,
4949
emailCA: true,
5050
objCA: true
51-
}, {
52-
name: 'subjectAltName',
53-
altNames: [{
54-
type: 6, // URI
55-
value: 'http://example.org/webid#me'
56-
}, {
57-
type: 7, // IP
58-
ip: '127.0.0.1'
59-
}]
6051
}, {
6152
name: 'subjectKeyIdentifier'
6253
}];
@@ -127,7 +118,6 @@ var CA = function (caFolder) {
127118
} catch (e) {
128119
this.generateCA();
129120
}
130-
this.currentlyGenerating = {};
131121
};
132122

133123
CA.prototype.randomSerialNumber = function () {
@@ -169,7 +159,9 @@ CA.prototype.loadCA = function () {
169159
};
170160
};
171161

172-
CA.prototype.generateServerCertificateKeys = function (hostname, cb) {
162+
CA.prototype.generateServerCertificateKeys = function (hosts, cb) {
163+
if (typeof(hosts) === "string") hosts = [hosts];
164+
var mainHost = hosts[0];
173165
var keysServer = pki.rsa.generateKeyPair(1024);
174166
var certServer = pki.createCertificate();
175167
certServer.publicKey = keysServer.publicKey;
@@ -180,52 +172,26 @@ CA.prototype.generateServerCertificateKeys = function (hostname, cb) {
180172
var attrsServer = ServerAttrs.slice(0);
181173
attrsServer.unshift({
182174
name: 'commonName',
183-
value: hostname
175+
value: mainHost
184176
})
185177
certServer.setSubject(attrsServer);
186178
certServer.setIssuer(this.CAcert.issuer.attributes);
187-
certServer.setExtensions(ServerExtensions);
179+
certServer.setExtensions(ServerExtensions.concat([{
180+
name: 'subjectAltName',
181+
altNames: hosts.map(function(host) {
182+
return {type: 2, value: host};
183+
})
184+
}]));
188185
certServer.sign(this.CAkeys.privateKey, Forge.md.sha256.create());
189186
var certPem = pki.certificateToPem(certServer);
190187
var keyPrivatePem = pki.privateKeyToPem(keysServer.privateKey)
191188
var keyPublicPem = pki.publicKeyToPem(keysServer.publicKey)
192-
FS.writeFile(this.certsFolder + '/' + hostname + '.pem', certPem);
193-
FS.writeFile(this.keysFolder + '/' + hostname + '.key', keyPrivatePem);
194-
FS.writeFile(this.keysFolder + '/' + hostname + '.public.key', keyPublicPem);
189+
FS.writeFile(this.certsFolder + '/' + mainHost.replace(/\*/g, '_') + '.pem', certPem);
190+
FS.writeFile(this.keysFolder + '/' + mainHost.replace(/\*/g, '_') + '.key', keyPrivatePem);
191+
FS.writeFile(this.keysFolder + '/' + mainHost.replace(/\*/g, '_') + '.public.key', keyPublicPem);
195192
cb(certPem, keyPrivatePem);
196193
};
197194

198-
CA.prototype.getServerCertificateKeys = function (hostname, cb) {
199-
if (this.currentlyGenerating.hasOwnProperty(hostname)) {
200-
this.currentlyGenerating[hostname].push(cb);
201-
} else {
202-
this.currentlyGenerating[hostname] = [cb];
203-
FS.stat(this.certsFolder + '/' + hostname + '.pem', function (err, stat) {
204-
if (typeof err !== 'undefined') {
205-
this.generateServerCertificateKeys(hostname, function (cert, key) {
206-
// possible race condition here...
207-
var tmp = this.currentlyGenerating[hostname];
208-
tmp.forEach(function (c) {
209-
c(cert, key);
210-
});
211-
delete this.currentlyGenerating[hostname];
212-
}.bind(this));
213-
} else {
214-
FS.readFile(this.certsFolder + '/' + hostname + '.pem', function (err, dataCert) {
215-
FS.readFile(this.keysFolder + '/' + hostname + '.key', function (err, dataKey) {
216-
// possible race condition here...
217-
var tmp = this.currentlyGenerating[hostname];
218-
tmp.forEach(function (c) {
219-
c(dataCert, dataKey);
220-
});
221-
delete this.currentlyGenerating[hostname];
222-
}.bind(this));
223-
}.bind(this));
224-
}
225-
}.bind(this));
226-
}
227-
};
228-
229195
CA.prototype.getCACertPath = function () {
230196
return this.certsFolder + '/ca.pem';
231197
};

lib/middleware/wildcard.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
/**
4+
* group1: subdomain
5+
* group2: domain.ext
6+
* exclude short domains (length < 4) to avoid catching double extensions (ex: net.au, co.uk, ...)
7+
*/
8+
const HOSTNAME_REGEX = /^(.+)(\.[^\.]{4,}(\.[^\.]{1,3})*\.[^\.]+)$/;
9+
10+
module.exports = {
11+
onCertificateRequired: function (hostname, callback) {
12+
var rootHost = hostname;
13+
if (HOSTNAME_REGEX.test(hostname)) {
14+
rootHost = hostname.replace(/^[^\.]+\./, '');
15+
}
16+
return callback(null, {
17+
keyFile: this.sslCaDir + '/keys/_.' + rootHost + '.key',
18+
certFile: this.sslCaDir + '/certs/_.' + rootHost + '.pem',
19+
hosts: ['*.' + rootHost, rootHost]
20+
});
21+
return this;
22+
}
23+
};

0 commit comments

Comments
 (0)