http: add admin key for dos-able calls.
This commit is contained in:
parent
80b6968318
commit
383c8f085f
13
bin/cli
13
bin/cli
@ -303,6 +303,14 @@ CLI.prototype.rescan = co(function* rescan() {
|
||||
this.log('Rescanning...');
|
||||
});
|
||||
|
||||
CLI.prototype.backup = co(function* backup() {
|
||||
var path = this.argv[0];
|
||||
|
||||
yield this.client.backup(path);
|
||||
|
||||
this.log('Backup complete.');
|
||||
});
|
||||
|
||||
CLI.prototype.importKey = co(function* importKey() {
|
||||
var key = this.argv[0];
|
||||
|
||||
@ -485,6 +493,8 @@ CLI.prototype.handleNode = co(function* handleNode() {
|
||||
return yield this.getBlock();
|
||||
case 'rescan':
|
||||
return yield this.rescan();
|
||||
case 'backup':
|
||||
return yield this.backup();
|
||||
case 'rpc':
|
||||
return yield this.rpc();
|
||||
default:
|
||||
@ -496,7 +506,8 @@ CLI.prototype.handleNode = co(function* handleNode() {
|
||||
this.log(' $ tx [hash/address]: View transactions.');
|
||||
this.log(' $ coin [hash+index/address]: View coins.');
|
||||
this.log(' $ block [hash/height]: View block.');
|
||||
this.log(' $ rescan [height/hash]: Rescan for transactions.');
|
||||
this.log(' $ rescan [height/hash]: Rescan for transactions (admin-only).');
|
||||
this.log(' $ backup [path]: Backup the wallet db (admin-only).');
|
||||
this.log(' $ rpc [command] [args]: Execute RPC command.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -59,5 +59,6 @@ known-peers: ./known-peers
|
||||
# http-port: 8332
|
||||
# http-host: 0.0.0.0
|
||||
api-key: bikeshed
|
||||
admin-key: bikeshed2
|
||||
wallet-auth: false
|
||||
# no-auth: false
|
||||
|
||||
@ -358,6 +358,17 @@ HTTPClient.prototype.rescan = function rescan(hash) {
|
||||
return this._post('/rescan', options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Backup the walletdb.
|
||||
* @param {String} path
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
HTTPClient.prototype.backup = function backup(path) {
|
||||
var options = { path: path };
|
||||
return this._post('/backup', options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for events on wallet id.
|
||||
* @param {WalletID} id
|
||||
|
||||
@ -69,9 +69,11 @@ function RPC(node) {
|
||||
|
||||
utils.inherits(RPC, EventEmitter);
|
||||
|
||||
RPC.prototype.execute = function execute(json) {
|
||||
RPC.prototype.execute = function execute(json, admin) {
|
||||
switch (json.method) {
|
||||
case 'stop':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.stop(json.params);
|
||||
case 'help':
|
||||
return this.help(json.params);
|
||||
@ -107,6 +109,8 @@ RPC.prototype.execute = function execute(json) {
|
||||
case 'gettxoutsetinfo':
|
||||
return this.gettxoutsetinfo(json.params);
|
||||
case 'verifychain':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.verifychain(json.params);
|
||||
|
||||
case 'invalidateblock':
|
||||
@ -130,12 +134,18 @@ RPC.prototype.execute = function execute(json) {
|
||||
return this.submitblock(json.params);
|
||||
|
||||
case 'setgenerate':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.setgenerate(json.params);
|
||||
case 'getgenerate':
|
||||
return this.getgenerate(json.params);
|
||||
case 'generate':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.generate(json.params);
|
||||
case 'generatetoaddress':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.generatetoaddress(json.params);
|
||||
|
||||
case 'estimatefee':
|
||||
@ -215,10 +225,14 @@ RPC.prototype.execute = function execute(json) {
|
||||
case 'addwitnessaddress':
|
||||
return this.addwitnessaddress(json.params);
|
||||
case 'backupwallet':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.backupwallet(json.params);
|
||||
case 'dumpprivkey':
|
||||
return this.dumpprivkey(json.params);
|
||||
case 'dumpwallet':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.dumpwallet(json.params);
|
||||
case 'encryptwallet':
|
||||
return this.encryptwallet(json.params);
|
||||
@ -247,6 +261,8 @@ RPC.prototype.execute = function execute(json) {
|
||||
case 'importprivkey':
|
||||
return this.importprivkey(json.params);
|
||||
case 'importwallet':
|
||||
if (!admin)
|
||||
return Promise.resolve('Not authorized.');
|
||||
return this.importwallet(json.params);
|
||||
case 'importaddress':
|
||||
return this.importaddress(json.params);
|
||||
|
||||
@ -62,6 +62,7 @@ function HTTPServer(options) {
|
||||
this.loaded = false;
|
||||
this.apiKey = options.apiKey;
|
||||
this.apiHash = null;
|
||||
this.adminHash = null;
|
||||
this.rpc = null;
|
||||
|
||||
if (!this.apiKey)
|
||||
@ -71,6 +72,13 @@ function HTTPServer(options) {
|
||||
assert(this.apiKey.length <= 200, 'API key must be under 200 bytes.');
|
||||
|
||||
this.apiHash = hash256(this.apiKey);
|
||||
this.adminHash = this.apiHash;
|
||||
|
||||
if (options.adminKey) {
|
||||
assert(typeof options.adminKey === 'string', 'API key must be a string.');
|
||||
assert(options.adminKey.length <= 200, 'API key must be under 200 bytes.');
|
||||
this.adminHash = hash256(options.adminKey);
|
||||
}
|
||||
|
||||
if (options.noAuth) {
|
||||
this.apiKey = null;
|
||||
@ -135,6 +143,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
if (!auth) {
|
||||
req.username = null;
|
||||
req.password = null;
|
||||
req.admin = false;
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -147,15 +156,25 @@ HTTPServer.prototype._init = function _init() {
|
||||
|
||||
req.username = parts.shift();
|
||||
req.password = parts.join(':');
|
||||
req.admin = false;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
this.use(function(req, res, send, next) {
|
||||
var hash;
|
||||
|
||||
if (!this.apiHash)
|
||||
return next();
|
||||
|
||||
if (crypto.ccmp(hash256(req.password), this.apiHash))
|
||||
hash = hash256(req.password);
|
||||
|
||||
if (crypto.ccmp(hash, this.adminHash)) {
|
||||
req.admin = true;
|
||||
return next();
|
||||
}
|
||||
|
||||
if (crypto.ccmp(hash, this.apiHash))
|
||||
return next();
|
||||
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="node"');
|
||||
@ -413,6 +432,11 @@ HTTPServer.prototype._init = function _init() {
|
||||
options.token = new Buffer(params.token, 'hex');
|
||||
}
|
||||
|
||||
if (params.path) {
|
||||
enforce(typeof params.path === 'string', 'Passphrase must be a string.');
|
||||
options.path = params.path;
|
||||
}
|
||||
|
||||
req.options = options;
|
||||
|
||||
next();
|
||||
@ -477,7 +501,7 @@ HTTPServer.prototype._init = function _init() {
|
||||
}
|
||||
|
||||
try {
|
||||
json = yield this.rpc.execute(req.body);
|
||||
json = yield this.rpc.execute(req.body, req.admin);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
|
||||
@ -669,11 +693,30 @@ HTTPServer.prototype._init = function _init() {
|
||||
this.post('/rescan', con(function* (req, res, send, next) {
|
||||
var options = req.options;
|
||||
var height = options.hash || options.height;
|
||||
|
||||
enforce(height != null, 'Hash or height is required.');
|
||||
|
||||
if (!req.admin)
|
||||
throw new Error('Cannot scan.');
|
||||
|
||||
send(200, { success: true });
|
||||
yield this.node.scan(height);
|
||||
}));
|
||||
|
||||
// Backup WalletDB
|
||||
this.post('/backup', con(function* (req, res, send, next) {
|
||||
var options = req.options;
|
||||
var path = options.path;
|
||||
|
||||
enforce(path, 'Path is required.');
|
||||
|
||||
if (!req.admin)
|
||||
throw new Error('Cannot backup.');
|
||||
|
||||
yield this.walletdb.backup(path);
|
||||
send(200, { success: true });
|
||||
}));
|
||||
|
||||
// Get wallet
|
||||
this.get('/wallet/:id', function(req, res, send, next) {
|
||||
send(200, req.wallet.toJSON());
|
||||
@ -1047,6 +1090,7 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
|
||||
socket.on('auth', function(args, callback) {
|
||||
var apiKey = args[0];
|
||||
var hash;
|
||||
|
||||
if (socket.auth)
|
||||
return callback({ error: 'Already authed.' });
|
||||
@ -1054,8 +1098,13 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
socket.stop();
|
||||
|
||||
if (self.apiHash) {
|
||||
if (!crypto.ccmp(hash256(apiKey), self.apiHash))
|
||||
return callback({ error: 'Bad key.' });
|
||||
hash = hash256(apiKey);
|
||||
if (crypto.ccmp(hash, self.adminHash)) {
|
||||
socket.admin = true;
|
||||
} else {
|
||||
if (!crypto.ccmp(hash, self.apiHash))
|
||||
return callback({ error: 'Bad key.' });
|
||||
}
|
||||
}
|
||||
|
||||
socket.auth = true;
|
||||
@ -1159,6 +1208,9 @@ HTTPServer.prototype._initIO = function _initIO() {
|
||||
socket.on('scan chain', function(args, callback) {
|
||||
var start = args[0];
|
||||
|
||||
if (!socket.admin)
|
||||
return callback({ error: 'Cannot scan.' });
|
||||
|
||||
if (!utils.isHex256(start) && !utils.isUInt32(start))
|
||||
return callback({ error: 'Invalid parameter.' });
|
||||
|
||||
@ -1305,6 +1357,7 @@ function ClientSocket(server, socket) {
|
||||
this.auth = false;
|
||||
this.filter = {};
|
||||
this.filterCount = 0;
|
||||
this.admin = false;
|
||||
|
||||
this.chain = this.server.chain;
|
||||
this.mempool = this.server.mempool;
|
||||
|
||||
@ -199,6 +199,7 @@ config.parseData = function parseData(data, prefix, dirname) {
|
||||
options.httpPort = num(data.httpport);
|
||||
options.httpHost = str(data.httphost);
|
||||
options.apiKey = str(data.apikey);
|
||||
options.adminKey = str(data.adminkey);
|
||||
options.walletAuth = bool(data.walletauth);
|
||||
options.noAuth = bool(data.noauth);
|
||||
|
||||
|
||||
@ -162,6 +162,7 @@ function Fullnode(options) {
|
||||
port: this.options.httpPort || this.network.rpcPort,
|
||||
host: this.options.httpHost || '0.0.0.0',
|
||||
apiKey: this.options.apiKey,
|
||||
adminKey: this.options.adminKey,
|
||||
walletAuth: this.options.walletAuth,
|
||||
noAuth: this.options.noAuth
|
||||
});
|
||||
|
||||
@ -99,6 +99,7 @@ function SPVNode(options) {
|
||||
port: this.options.httpPort || this.network.rpcPort,
|
||||
host: this.options.httpHost || '0.0.0.0',
|
||||
apiKey: this.options.apiKey,
|
||||
adminKey: this.options.adminKey,
|
||||
walletAuth: this.options.walletAuth,
|
||||
noAuth: this.options.noAuth
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user