add SSL support

closes #2
This commit is contained in:
freewil 2013-03-08 08:09:10 -05:00
parent 36417a5657
commit 1ac9e7d201
7 changed files with 155 additions and 21 deletions

View File

@ -2,19 +2,40 @@ MOCHA=./node_modules/.bin/mocha
BOX=test/testnet-box
test:
$(MAKE) test-ssl-no
sleep 15
$(MAKE) clean
$(MAKE) test-ssl
test-ssl-no:
$(MAKE) start
sleep 15
$(MAKE) run-test
$(MAKE) stop
test-ssl:
$(MAKE) start-ssl
sleep 15
$(MAKE) run-test-ssl
$(MAKE) stop-ssl
start:
$(MAKE) -C $(BOX) start
start-ssl:
$(MAKE) -C $(BOX) start B1_FLAGS=-rpcssl=1 B2_FLAGS=-rpcssl=1
stop:
$(MAKE) -C $(BOX) stop
stop-ssl:
$(MAKE) -C $(BOX) stop B1_FLAGS=-rpcssl=1 B2_FLAGS=-rpcssl=1
run-test:
$(MOCHA)
$(MOCHA) --invert --grep SSL
run-test-ssl:
$(MOCHA) --grep SSL
clean:
$(MAKE) -C $(BOX) clean

View File

@ -16,12 +16,6 @@ object, or you may call the API directly using the `cmd` method.
### Create client
```js
var bitcoin = require('bitcoin');
var client = new bitcoin.Client('localhost', 8332, 'username', 'password');
```
### Create client with single object
```js
var client = new bitcoin.Client({
host: 'localhost',
port: 8332,
@ -62,3 +56,27 @@ client.cmd(batch, function(err, address) {
console.log('Address:', address);
});
```
## SSL
See [Enabling SSL on original client](https://en.bitcoin.it/wiki/Enabling_SSL_on_original_client_daemon).
If you're using this to connect to bitcoind across a network it is highly
recommended to use SSL, otherwise an attacker may interecept your RPC credentials
resulting in theft of your Bitcoins.
When enabling `ssl` by setting the configuration option to `true`, the `sslStrict`
option will also be enabled by default. It is highly recommended to specify the
CA as well to ensure you are not connecting to an attacker's bitcoind attempting
to impersonate your own bitcoind.
```js
var client = new bitcoin.Client({
host: 'localhost',
port: 8332,
user: 'username',
pass: 'password',
ssl: true
sslStrict: true,
sslCa: fs.readFileSync(__dirname + '/myca.cert')
});
```

View File

@ -1,4 +1,5 @@
var rpc = require('../jsonrpc');
var rpc = require('../jsonrpc'),
deprecate = require('deprecate');
//===----------------------------------------------------------------------===//
// jsonrpc wrappers
@ -69,16 +70,32 @@ var bitcoinAPI = {
//===----------------------------------------------------------------------===//
// Client
// Either pass in 4 arguments, or a single object with host, port, user, pass
// Pass in an object with host, port, user, pass
//===----------------------------------------------------------------------===//
function Client() {
var args = [].slice.call(arguments);
this.host = args[0].host || args[0];
this.port = args[0].port || args[1];
this.user = args[0].user || args[2];
this.pass = args[0].pass || args[3];
if (args.length > 1) {
deprecate('calling bitcoin.Client with more than one argument is deprecated');
this.host = args[0];
this.port = args[1];
this.user = args[2];
this.pass = args[3];
this.ssl = false;
this.sslStrict = false;
this.sslCa = null;
} else {
this.host = args[0].host;
this.port = args[0].port;
this.user = args[0].user;
this.pass = args[0].pass;
this.ssl = args[0].ssl ? true : false;
this.sslStrict = (typeof args[0].sslStrict === 'undefined' || args[0].sslStrict);
this.sslCa = args[0].sslCa;
}
this.rpc = new rpc.Client(this.port, this.host, this.user, this.pass);
this.rpc = new rpc.Client(this.port, this.host, this.user, this.pass,
this.ssl, this.sslStrict, this.sslCa);
}

View File

@ -1,10 +1,16 @@
var http = require('http');
var http = require('http'),
https = require('https');
var Client = function(port, host, user, password) {
var Client = function(port, host, user, password, ssl, sslStrict, sslCa) {
this.port = port;
this.host = host;
this.user = user;
this.password = password;
this.ssl = ssl ? true : false;
this.sslStrict = sslStrict ? true : false;
this.http = this.ssl ? https : http;
this.sslCa = sslCa;
};
Client.prototype.call = function(method, params, callback, errback, path) {
@ -42,8 +48,17 @@ Client.prototype.call = function(method, params, callback, errback, path) {
headers: {
'Host': this.host,
'Content-Length': requestJSON.length
}
},
agent: false
};
if (this.ssl && this.sslStrict) {
requestOptions.rejectUnauthorized = true;
}
if (this.ssl && this.sslCa) {
requestOptions.ca = this.sslCa;
}
// use HTTP auth if user and password set
if (this.user && this.password) {
@ -51,7 +66,7 @@ Client.prototype.call = function(method, params, callback, errback, path) {
}
// Now we'll make a request to the server
var request = http.request(requestOptions);
var request = this.http.request(requestOptions);
request.on('error', errback);

View File

@ -11,9 +11,12 @@
"contributors": [
"Sean Lavine <sean@eternalrise.com>"
],
"dependencies": {},
"dependencies": {
"deprecate": "~0.1.0"
},
"devDependencies": {
"mocha": "~1.8.1"
"mocha": "~1.8.1",
"clone": "~0.1.6"
},
"repository": {
"type": "git",

View File

@ -1,6 +1,10 @@
var assert = require('assert'),
bitcoin = require('../lib/bitcoin'),
config = require('./config');
config = require('./config'),
deprecate = require('deprecate');
// hide deprecation warnings
deprecate.silence = true;
var test = {
account: 'test'

56
test/ssl.js Normal file
View File

@ -0,0 +1,56 @@
var assert = require('assert'),
fs = require('fs'),
clone = require('clone'),
bitcoin = require('../lib/bitcoin'),
config = require('./config');
var getInfo = function(opts, cb) {
var client = new bitcoin.Client(opts);
client.getInfo(cb);
};
describe('Client SSL', function() {
it('use sslStrict by default', function(done) {
var opts = clone(config);
opts.ssl = true;
getInfo(opts, function(err, info) {
assert.ok(err instanceof Error);
assert.equal(err.message, 'DEPTH_ZERO_SELF_SIGNED_CERT');
done();
});
});
it('strictSSL should fail with self-signed certificate', function(done) {
var opts = clone(config);
opts.ssl = true;
opts.sslStrict = true;
getInfo(opts, function(err, info) {
assert.ok(err instanceof Error);
assert.equal(err.message, 'DEPTH_ZERO_SELF_SIGNED_CERT');
done();
});
});
it('self-signed certificate with sslStrict false', function(done) {
var opts = clone(config);
opts.ssl = true;
opts.sslStrict = false;
getInfo(opts, function(err, info) {
assert.ifError(err);
assert.ok(info);
done();
});
});
it('self-signed certificate with sslStrict and CA specified', function(done) {
var opts = clone(config);
opts.ssl = true;
opts.sslCa = fs.readFileSync(__dirname + '/testnet-box/1/testnet3/server.cert');
getInfo(opts, function(err, info) {
assert.ifError(err);
assert.ok(info);
done();
});
});
});