Create limited RPC user.
The limited user is specified with the --rpclimituser and --rpclimitpass options (or the equivalent in the config file). The config struct and loadConfig() are updated to take the new options into account. The limited user can have neither the same username nor the same password as the admin user. The package-level rpcLimit map in rpcserver.go specifies the RPC commands accessible by limited users. This map includes both HTTP/S and websocket commands. The checkAuth function gets a new return parameter to signify whether the user is authorized to change server state. The result is passed to the jsonRPCRead function and to the WebsocketHandler function in rpcwebsocket.go. The wsClient struct is updated with an "isAdmin" field signifying that the client is authorized to change server state, written by WebsocketHandler and handleMessage. The handleMessage function also checks the field to allow or disallow an RPC call. The following documentation files are updated: - doc.go - sample-btcd.conf - docs/README.md - docs/json_rpc_api.md - docs/configure_rpc_server_listen_interfaces.md
This commit is contained in:
parent
abe74f1d4e
commit
4a1445a032
8 changed files with 226 additions and 83 deletions
27
config.go
27
config.go
|
@ -77,12 +77,14 @@ type config struct {
|
|||
BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"`
|
||||
RPCUser string `short:"u" long:"rpcuser" description:"Username for RPC connections"`
|
||||
RPCPass string `short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
|
||||
RPCLimitUser string `long:"rpclimituser" description:"Username for limited RPC connections"`
|
||||
RPCLimitPass string `long:"rpclimitpass" default-mask:"-" description:"Password for limited RPC connections"`
|
||||
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 8334, testnet: 18334)"`
|
||||
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
|
||||
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
||||
RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"`
|
||||
RPCMaxWebsockets int `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"`
|
||||
DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified"`
|
||||
DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass or rpclimituser/rpclimitpass is specified"`
|
||||
DisableTLS bool `long:"notls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"`
|
||||
DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"`
|
||||
ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"`
|
||||
|
@ -546,8 +548,29 @@ func loadConfig() (*config, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Check to make sure limited and admin users don't have the same username
|
||||
if cfg.RPCUser == cfg.RPCLimitUser && cfg.RPCUser != "" {
|
||||
str := "%s: --rpcuser and --rpclimituser must not specify the " +
|
||||
"same username"
|
||||
err := fmt.Errorf(str, funcName)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Check to make sure limited and admin users don't have the same password
|
||||
if cfg.RPCPass == cfg.RPCLimitPass && cfg.RPCPass != "" {
|
||||
str := "%s: --rpcpass and --rpclimitpass must not specify the " +
|
||||
"same password"
|
||||
err := fmt.Errorf(str, funcName)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// The RPC server is disabled if no username or password is provided.
|
||||
if cfg.RPCUser == "" || cfg.RPCPass == "" {
|
||||
if (cfg.RPCUser == "" || cfg.RPCPass == "") &&
|
||||
(cfg.RPCLimitUser == "" || cfg.RPCLimitPass == "") {
|
||||
cfg.DisableRPC = true
|
||||
}
|
||||
|
||||
|
|
2
doc.go
2
doc.go
|
@ -37,6 +37,8 @@ Application Options:
|
|||
are {s, m, h}. Minimum 1 second (24h0m0s)
|
||||
-u, --rpcuser= Username for RPC connections
|
||||
-P, --rpcpass= Password for RPC connections
|
||||
--rpclimituser= Username for limited RPC connections
|
||||
--rpclimitpass= Password for limited RPC connections
|
||||
--rpclisten= Add an interface/port to listen for RPC connections
|
||||
(default port: 8334, testnet: 18334)
|
||||
--rpccert= File containing the certificate file
|
||||
|
|
|
@ -89,13 +89,15 @@ options, which can be viewed by running: `$ btcd --help`.
|
|||
btcctl is a command line utility that can be used to both control and query btcd
|
||||
via [RPC](http://www.wikipedia.org/wiki/Remote_procedure_call). btcd does
|
||||
**not** enable its RPC server by default; You must configure at minimum both an
|
||||
RPC username and password:
|
||||
RPC username and password or both an RPC limited username and password:
|
||||
|
||||
* btcd.conf configuration file
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
rpclimituser=mylimituser
|
||||
rpclimitpass=Limitedp4ssw0rd
|
||||
```
|
||||
* btcctl.conf configuration file
|
||||
```
|
||||
|
@ -103,12 +105,19 @@ rpcpass=SomeDecentp4ssw0rd
|
|||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
```
|
||||
OR
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=mylimituser
|
||||
rpcpass=Limitedp4ssw0rd
|
||||
```
|
||||
For a list of available options, run: `$ btcctl --help`
|
||||
|
||||
<a name="Mining" />
|
||||
**2.4 Mining**<br />
|
||||
btcd supports both the `getwork` and `getblocktemplate` RPCs although the
|
||||
`getwork` RPC is deprecated and will likely be removed in a future release.<br />
|
||||
`getwork` RPC is deprecated and will likely be removed in a future release.
|
||||
The limited user cannot access these RPCs.<br />
|
||||
|
||||
**1. Add the payment addresses with the `miningaddr` option.**<br />
|
||||
|
||||
|
|
|
@ -7,10 +7,11 @@ options). The configuration file takes one entry per line.
|
|||
A few things to note regarding the RPC server:
|
||||
* The RPC server will **not** be enabled unless the `rpcuser` and `rpcpass`
|
||||
options are specified.
|
||||
* When the `rpcuser` and `rpcpass` options are specified, the RPC server will
|
||||
only listen on localhost IPv4 and IPv6 interfaces by default. You will need
|
||||
to override the RPC listen interfaces to include external interfaces if you
|
||||
want to connect from a remote machine.
|
||||
* When the `rpcuser` and `rpcpass` and/or `rpclimituser` and `rpclimitpass`
|
||||
options are specified, the RPC server will only listen on localhost IPv4 and
|
||||
IPv6 interfaces by default. You will need to override the RPC listen
|
||||
interfaces to include external interfaces if you want to connect from a remote
|
||||
machine.
|
||||
* The RPC server has TLS enabled by default, even for localhost. You may use
|
||||
the `--notls` option to disable it, but only when all listeners are on
|
||||
localhost interfaces.
|
||||
|
|
|
@ -89,16 +89,19 @@ JSON-RPC API are:
|
|||
The following authentication details are needed before establishing a connection
|
||||
to a btcd RPC server:
|
||||
|
||||
* **rpcuser** is the username that the btcd RPC server is configured with
|
||||
* **rpcpass** is the password that the btcd RPC server is configured with
|
||||
* **rpcuser** is the full-access username configured for the btcd RPC server
|
||||
* **rpcpass** is the full-access password configured for the btcd RPC server
|
||||
* **rpclimituser** is the limited username configured for the btcd RPC server
|
||||
* **rpclimitpass** is the limited password configured for the btcd RPC server
|
||||
* **rpccert** is the PEM-encoded X.509 certificate (public key) that the btcd
|
||||
server is configured with. It is automatically generated by btcd and placed
|
||||
in the btcd home directory (which is typically `%LOCALAPPDATA%\Btcd` on
|
||||
Windows and `~/.btcd` on POSIX-like OSes)
|
||||
|
||||
**NOTE:** As mentioned above, btcd is secure by default which means the RPC
|
||||
server is not running unless configured with a **rpcuser** and **rpcpass** and
|
||||
uses TLS authentication for all connections.
|
||||
server is not running unless configured with a **rpcuser** and **rpcpass**
|
||||
and/or a **rpclimituser** and **rpclimitpass**, and uses TLS authentication for
|
||||
all connections.
|
||||
|
||||
Depending on which connection transaction you are using, you can choose one of
|
||||
two, mutually exclusive, methods.
|
||||
|
@ -143,37 +146,37 @@ API compatible with the original bitcoind/bitcoin-qt client.
|
|||
The following is an overview of the RPC methods and their current status. Click
|
||||
the method name for further details such as parameter and return information.
|
||||
|
||||
|#|Method|Description|
|
||||
|---|------|-----------|
|
||||
|1|[addnode](#addnode)|Attempts to add or remove a persistent peer.|
|
||||
|2|[createrawtransaction](#createrawtransaction)|Returns a new transaction spending the provided inputs and sending to the provided addresses.|
|
||||
|3|[decoderawtransaction](#decoderawtransaction)|Returns a JSON object representing the provided serialized, hex-encoded transaction.|
|
||||
|4|[decodescript](#decodescript)|Returns a JSON object with information about the provided hex-encoded script.|
|
||||
|5|[getaddednodeinfo](#getaddednodeinfo)|Returns information about manually added (persistent) peers.|
|
||||
|6|[getbestblockhash](#getbestblockhash)|Returns the hash of the of the best (most recent) block in the longest block chain.|
|
||||
|7|[getblock](#getblock)|Returns information about a block given its hash.|
|
||||
|8|[getblockcount](#getblockcount)|Returns the number of blocks in the longest block chain.|
|
||||
|9|[getblockhash](#getblockhash)|Returns hash of the block in best block chain at the given height.|
|
||||
|10|[getconnectioncount](#getconnectioncount)|Returns the number of active connections to other peers.|
|
||||
|11|[getdifficulty](#getdifficulty)|Returns the proof-of-work difficulty as a multiple of the minimum difficulty.|
|
||||
|12|[getgenerate](#getgenerate)|Return if the server is set to generate coins (mine) or not.|
|
||||
|13|[gethashespersec](#gethashespersec)|Returns a recent hashes per second performance measurement while generating coins (mining).|
|
||||
|14|[getinfo](#getinfo)|Returns a JSON object containing various state info.|
|
||||
|15|[getmininginfo](#getmininginfo)|Returns a JSON object containing mining-related information.|
|
||||
|16|[getnettotals](#getnettotals)|Returns a JSON object containing network traffic statistics.|
|
||||
|17|[getnetworkhashps](#getnetworkhashps)|Returns the estimated network hashes per second for the block heights provided by the parameters.|
|
||||
|18|[getpeerinfo](#getpeerinfo)|Returns information about each connected network peer as an array of json objects.|
|
||||
|19|[getrawmempool](#getrawmempool)|Returns an array of hashes for all of the transactions currently in the memory pool.|
|
||||
|20|[getrawtransaction](#getrawtransaction)|Returns information about a transaction given its hash.|
|
||||
|21|[getwork](#getwork)|Returns formatted hash data to work on or checks and submits solved data.<br /><font color="orange">NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.</font>|
|
||||
|22|[help](#help)|Returns a list of all commands or help for a specified command.|
|
||||
|23|[ping](#ping)|Queues a ping to be sent to each connected peer.|
|
||||
|24|[sendrawtransaction](#sendrawtransaction)|Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.<br /><font color="orange">btcd does not yet implement the `allowhighfees` parameter, so it has no effect</font>|
|
||||
|25|[setgenerate](#setgenerate) |Set the server to generate coins (mine) or not.<br/>NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.|
|
||||
|26|[stop](#stop)|Shutdown btcd.|
|
||||
|27|[submitblock](#submitblock)|Attempts to submit a new serialized, hex-encoded block to the network.|
|
||||
|28|[validateaddress](#validateaddress)|Verifies the given address is valid. NOTE: Since btcd does not have a wallet integrated, btcd will only return whether the address is valid or not.|
|
||||
|29|[verifychain](#verifychain)|Verifies the block chain database.|
|
||||
|#|Method|Safe for limited user?|Description|
|
||||
|---|------|----------|-----------|
|
||||
|1|[addnode](#addnode)|N|Attempts to add or remove a persistent peer.|
|
||||
|2|[createrawtransaction](#createrawtransaction)|Y|Returns a new transaction spending the provided inputs and sending to the provided addresses.|
|
||||
|3|[decoderawtransaction](#decoderawtransaction)|Y|Returns a JSON object representing the provided serialized, hex-encoded transaction.|
|
||||
|4|[decodescript](#decodescript)|Y|Returns a JSON object with information about the provided hex-encoded script.|
|
||||
|5|[getaddednodeinfo](#getaddednodeinfo)|N|Returns information about manually added (persistent) peers.|
|
||||
|6|[getbestblockhash](#getbestblockhash)|Y|Returns the hash of the of the best (most recent) block in the longest block chain.|
|
||||
|7|[getblock](#getblock)|Y|Returns information about a block given its hash.|
|
||||
|8|[getblockcount](#getblockcount)|Y|Returns the number of blocks in the longest block chain.|
|
||||
|9|[getblockhash](#getblockhash)|Y|Returns hash of the block in best block chain at the given height.|
|
||||
|10|[getconnectioncount](#getconnectioncount)|N|Returns the number of active connections to other peers.|
|
||||
|11|[getdifficulty](#getdifficulty)|Y|Returns the proof-of-work difficulty as a multiple of the minimum difficulty.|
|
||||
|12|[getgenerate](#getgenerate)|N|Return if the server is set to generate coins (mine) or not.|
|
||||
|13|[gethashespersec](#gethashespersec)|N|Returns a recent hashes per second performance measurement while generating coins (mining).|
|
||||
|14|[getinfo](#getinfo)|Y|Returns a JSON object containing various state info.|
|
||||
|15|[getmininginfo](#getmininginfo)|N|Returns a JSON object containing mining-related information.|
|
||||
|16|[getnettotals](#getnettotals)|Y|Returns a JSON object containing network traffic statistics.|
|
||||
|17|[getnetworkhashps](#getnetworkhashps)|Y|Returns the estimated network hashes per second for the block heights provided by the parameters.|
|
||||
|18|[getpeerinfo](#getpeerinfo)|N|Returns information about each connected network peer as an array of json objects.|
|
||||
|19|[getrawmempool](#getrawmempool)|Y|Returns an array of hashes for all of the transactions currently in the memory pool.|
|
||||
|20|[getrawtransaction](#getrawtransaction)|Y|Returns information about a transaction given its hash.|
|
||||
|21|[getwork](#getwork)|N|Returns formatted hash data to work on or checks and submits solved data.<br /><font color="orange">NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.</font>|
|
||||
|22|[help](#help)|Y|Returns a list of all commands or help for a specified command.|
|
||||
|23|[ping](#ping)|N|Queues a ping to be sent to each connected peer.|
|
||||
|24|[sendrawtransaction](#sendrawtransaction)|Y|Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.<br /><font color="orange">btcd does not yet implement the `allowhighfees` parameter, so it has no effect</font>|
|
||||
|25|[setgenerate](#setgenerate) |N|Set the server to generate coins (mine) or not.<br/>NOTE: Since btcd does not have the wallet integrated to provide payment addresses, btcd must be configured via the `--miningaddr` option to provide which payment addresses to pay created blocks to for this RPC to function.|
|
||||
|26|[stop](#stop)|N|Shutdown btcd.|
|
||||
|27|[submitblock](#submitblock)|Y|Attempts to submit a new serialized, hex-encoded block to the network.|
|
||||
|28|[validateaddress](#validateaddress)|Y|Verifies the given address is valid. NOTE: Since btcd does not have a wallet integrated, btcd will only return whether the address is valid or not.|
|
||||
|29|[verifychain](#verifychain)|N|Verifies the block chain database.|
|
||||
|
||||
<a name="MethodDetails" />
|
||||
**5.2 Method Details**<br />
|
||||
|
@ -544,12 +547,12 @@ the method name for further details such as parameter and return information.
|
|||
|
||||
The following is an overview of the RPC methods which are implemented by btcd, but not the original bitcoind client. Click the method name for further details such as parameter and return information.
|
||||
|
||||
|#|Method|Description|
|
||||
|---|------|-----------|
|
||||
|1|[debuglevel](#debuglevel)|Dynamically changes the debug logging level.|
|
||||
|2|[getbestblock](#getbestblock)|Get block height and hash of best block in the main chain.|None|
|
||||
|3|[getcurrentnet](#getcurrentnet)|Get bitcoin network btcd is running on.|None|
|
||||
|4|[searchrawtransactions](#searchrawtransactions)|Query for transactions related to a particular address.|None|
|
||||
|#|Method|Safe for limited user?|Description|
|
||||
|---|------|----------|-----------|
|
||||
|1|[debuglevel](#debuglevel)|N|Dynamically changes the debug logging level.|
|
||||
|2|[getbestblock](#getbestblock)|Y|Get block height and hash of best block in the main chain.|None|
|
||||
|3|[getcurrentnet](#getcurrentnet)|Y|Get bitcoin network btcd is running on.|None|
|
||||
|4|[searchrawtransactions](#searchrawtransactions)|Y|Query for transactions related to a particular address.|None|
|
||||
|
||||
<a name="ExtMethodDetails" />
|
||||
**6.2 Method Details**<br />
|
||||
|
@ -612,7 +615,8 @@ The following is an overview of the RPC methods which are implemented by btcd, b
|
|||
<a name="WSExtMethodOverview" />
|
||||
**7.1 Method Overview**<br />
|
||||
|
||||
The following is an overview of the RPC method requests available exclusively to Websocket clients. Click the method name for further details such as parameter and return information.
|
||||
The following is an overview of the RPC method requests available exclusively to Websocket clients. All of these RPC methods are available to the limited
|
||||
user. Click the method name for further details such as parameter and return information.
|
||||
|
||||
|#|Method|Description|Notifications|
|
||||
|---|------|-----------|-------------|
|
||||
|
|
122
rpcserver.go
122
rpcserver.go
|
@ -223,6 +223,42 @@ var rpcUnimplemented = map[string]struct{}{
|
|||
"getnetworkinfo": struct{}{},
|
||||
}
|
||||
|
||||
// Commands that are available to a limited user
|
||||
var rpcLimited = map[string]struct{}{
|
||||
// Websockets commands
|
||||
"notifyblocks": struct{}{},
|
||||
"notifynewtransactions": struct{}{},
|
||||
"notifyreceived": struct{}{},
|
||||
"notifyspent": struct{}{},
|
||||
"rescan": struct{}{},
|
||||
|
||||
// Websockets AND HTTP/S commands
|
||||
"help": struct{}{},
|
||||
|
||||
// HTTP/S-only commands
|
||||
"createrawtransaction": struct{}{},
|
||||
"decoderawtransaction": struct{}{},
|
||||
"decodescript": struct{}{},
|
||||
"getbestblock": struct{}{},
|
||||
"getbestblockhash": struct{}{},
|
||||
"getblock": struct{}{},
|
||||
"getblockcount": struct{}{},
|
||||
"getblockhash": struct{}{},
|
||||
"getcurrentnet": struct{}{},
|
||||
"getdifficulty": struct{}{},
|
||||
"getinfo": struct{}{},
|
||||
"getnettotals": struct{}{},
|
||||
"getnetworkhashps": struct{}{},
|
||||
"getrawmempool": struct{}{},
|
||||
"getrawtransaction": struct{}{},
|
||||
"gettxout": struct{}{},
|
||||
"searchrawtransactions": struct{}{},
|
||||
"sendrawtransaction": struct{}{},
|
||||
"submitblock": struct{}{},
|
||||
"validateaddress": struct{}{},
|
||||
"verifymessage": struct{}{},
|
||||
}
|
||||
|
||||
// builderScript is a convenience function which is used for hard-coded scripts
|
||||
// built with the script builder. Any errors are converted to a panic since it
|
||||
// is only, and must only, be used with hard-coded, and therefore, known good,
|
||||
|
@ -2981,6 +3017,7 @@ type rpcServer struct {
|
|||
shutdown int32
|
||||
server *server
|
||||
authsha [fastsha256.Size]byte
|
||||
limitauthsha [fastsha256.Size]byte
|
||||
ntfnMgr *wsNotificationManager
|
||||
numClients int32
|
||||
statusLines map[int]string
|
||||
|
@ -3114,25 +3151,42 @@ func (s *rpcServer) decrementClients() {
|
|||
// returned.
|
||||
//
|
||||
// This check is time-constant.
|
||||
func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, error) {
|
||||
//
|
||||
// The first bool return value signifies auth success (true if successful) and
|
||||
// the second bool return value specifies whether the user can change the state
|
||||
// of the server (true) or whether the user is limited (false). The second is
|
||||
// always false if the first is.
|
||||
func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, bool,
|
||||
error) {
|
||||
authhdr := r.Header["Authorization"]
|
||||
if len(authhdr) <= 0 {
|
||||
if require {
|
||||
rpcsLog.Warnf("RPC authentication failure from %s",
|
||||
r.RemoteAddr)
|
||||
return false, errors.New("auth failure")
|
||||
return false, false, errors.New("auth failure")
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
authsha := fastsha256.Sum256([]byte(authhdr[0]))
|
||||
cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:])
|
||||
if cmp != 1 {
|
||||
rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr)
|
||||
return false, errors.New("auth failure")
|
||||
|
||||
// Check for limited auth first as in environments with limited users, those
|
||||
// are probably expected to have a higher volume of calls
|
||||
limitcmp := subtle.ConstantTimeCompare(authsha[:], s.limitauthsha[:])
|
||||
if limitcmp == 1 {
|
||||
return true, false, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
// Check for admin-level auth
|
||||
cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:])
|
||||
if cmp == 1 {
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
// Request's auth doesn't match either user
|
||||
rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr)
|
||||
return false, false, errors.New("auth failure")
|
||||
}
|
||||
|
||||
// parsedRPCCmd represents a JSON-RPC request object that has been parsed into
|
||||
|
@ -3218,7 +3272,8 @@ func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, erro
|
|||
}
|
||||
|
||||
// jsonRPCRead handles reading and responding to RPC messages.
|
||||
func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request,
|
||||
isAdmin bool) {
|
||||
if atomic.LoadInt32(&s.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
@ -3293,14 +3348,25 @@ func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}()
|
||||
|
||||
// Attempt to parse the JSON-RPC request into a known concrete
|
||||
// command.
|
||||
parsedCmd := parseCmd(&request)
|
||||
if parsedCmd.err != nil {
|
||||
jsonErr = parsedCmd.err
|
||||
} else {
|
||||
result, jsonErr = s.standardCmdResult(parsedCmd,
|
||||
closeChan)
|
||||
// Check if the user is limited and set error if method unauthorized
|
||||
if !isAdmin {
|
||||
if _, ok := rpcLimited[request.Method]; !ok {
|
||||
jsonErr = &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInvalidParams.Code,
|
||||
Message: "limited user not authorized for this method",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if jsonErr == nil {
|
||||
// Attempt to parse the JSON-RPC request into a known concrete
|
||||
// command.
|
||||
parsedCmd := parseCmd(&request)
|
||||
if parsedCmd.err != nil {
|
||||
jsonErr = parsedCmd.err
|
||||
} else {
|
||||
result, jsonErr = s.standardCmdResult(parsedCmd, closeChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3356,18 +3422,19 @@ func (s *rpcServer) Start() {
|
|||
// Keep track of the number of connected clients.
|
||||
s.incrementClients()
|
||||
defer s.decrementClients()
|
||||
if _, err := s.checkAuth(r, true); err != nil {
|
||||
_, isAdmin, err := s.checkAuth(r, true)
|
||||
if err != nil {
|
||||
jsonAuthFail(w)
|
||||
return
|
||||
}
|
||||
|
||||
// Read and respond to the request.
|
||||
s.jsonRPCRead(w, r)
|
||||
s.jsonRPCRead(w, r, isAdmin)
|
||||
})
|
||||
|
||||
// Websocket endpoint.
|
||||
rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
authenticated, err := s.checkAuth(r, false)
|
||||
authenticated, isAdmin, err := s.checkAuth(r, false)
|
||||
if err != nil {
|
||||
jsonAuthFail(w)
|
||||
return
|
||||
|
@ -3384,7 +3451,7 @@ func (s *rpcServer) Start() {
|
|||
http.Error(w, "400 Bad Request.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.WebsocketHandler(ws, r.RemoteAddr, authenticated)
|
||||
s.WebsocketHandler(ws, r.RemoteAddr, authenticated, isAdmin)
|
||||
})
|
||||
|
||||
for _, listener := range s.listeners {
|
||||
|
@ -3426,10 +3493,7 @@ func genCertPair(certFile, keyFile string) error {
|
|||
|
||||
// newRPCServer returns a new instance of the rpcServer struct.
|
||||
func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) {
|
||||
login := cfg.RPCUser + ":" + cfg.RPCPass
|
||||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
rpc := rpcServer{
|
||||
authsha: fastsha256.Sum256([]byte(auth)),
|
||||
server: s,
|
||||
statusLines: make(map[int]string),
|
||||
workState: newWorkState(),
|
||||
|
@ -3437,6 +3501,16 @@ func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) {
|
|||
helpCacher: newHelpCacher(),
|
||||
quit: make(chan int),
|
||||
}
|
||||
if cfg.RPCUser != "" && cfg.RPCPass != "" {
|
||||
login := cfg.RPCUser + ":" + cfg.RPCPass
|
||||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
rpc.authsha = fastsha256.Sum256([]byte(auth))
|
||||
}
|
||||
if cfg.RPCLimitUser != "" && cfg.RPCLimitPass != "" {
|
||||
login := cfg.RPCLimitUser + ":" + cfg.RPCLimitPass
|
||||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
rpc.limitauthsha = fastsha256.Sum256([]byte(auth))
|
||||
}
|
||||
rpc.ntfnMgr = newWsNotificationManager(&rpc)
|
||||
|
||||
// Setup TLS if not disabled.
|
||||
|
|
|
@ -71,7 +71,7 @@ var wsAsyncHandlers = map[string]struct{}{
|
|||
// server handler which runs each new connection in a new goroutine thereby
|
||||
// satisfying the requirement.
|
||||
func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string,
|
||||
authenticated bool) {
|
||||
authenticated bool, isAdmin bool) {
|
||||
|
||||
// Clear the read deadline that was set before the websocket hijacked
|
||||
// the connection.
|
||||
|
@ -90,7 +90,7 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string,
|
|||
// Create a new websocket client to handle the new websocket connection
|
||||
// and wait for it to shutdown. Once it has shutdown (and hence
|
||||
// disconnected), remove it and any notifications it registered for.
|
||||
client := newWebsocketClient(s, conn, remoteAddr, authenticated)
|
||||
client := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin)
|
||||
s.ntfnMgr.AddClient(client)
|
||||
client.Start()
|
||||
client.WaitForShutdown()
|
||||
|
@ -875,6 +875,10 @@ type wsClient struct {
|
|||
// and therefore is allowed to communicated over the websocket.
|
||||
authenticated bool
|
||||
|
||||
// isAdmin specifies whether a client may change the state of the server;
|
||||
// false means its access is only to the limited set of RPC calls.
|
||||
isAdmin bool
|
||||
|
||||
// verboseTxUpdates specifies whether a client has requested verbose
|
||||
// information about all new transactions.
|
||||
verboseTxUpdates bool
|
||||
|
@ -933,12 +937,14 @@ func (c *wsClient) handleMessage(msg []byte) {
|
|||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
authSha := fastsha256.Sum256([]byte(auth))
|
||||
cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:])
|
||||
if cmp != 1 {
|
||||
limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:])
|
||||
if cmp != 1 && limitcmp != 1 {
|
||||
rpcsLog.Warnf("Auth failure.")
|
||||
c.Disconnect()
|
||||
return
|
||||
}
|
||||
c.authenticated = true
|
||||
c.isAdmin = cmp == 1
|
||||
|
||||
// Marshal and send response.
|
||||
reply, err := createMarshalledReply(parsedCmd.id, nil, nil)
|
||||
|
@ -975,6 +981,25 @@ func (c *wsClient) handleMessage(msg []byte) {
|
|||
return
|
||||
}
|
||||
|
||||
// Check if the user is limited and disconnect client if unauthorized
|
||||
if !c.isAdmin {
|
||||
if _, ok := rpcLimited[request.Method]; !ok {
|
||||
jsonErr := &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInvalidParams.Code,
|
||||
Message: "limited user not authorized for this method",
|
||||
}
|
||||
// Marshal and send response.
|
||||
reply, err := createMarshalledReply(request.ID, nil, jsonErr)
|
||||
if err != nil {
|
||||
rpcsLog.Errorf("Failed to marshal parse failure "+
|
||||
"reply: %v", err)
|
||||
return
|
||||
}
|
||||
c.SendMessage(reply, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to parse the JSON-RPC request into a known concrete command.
|
||||
cmd := parseCmd(&request)
|
||||
if cmd.err != nil {
|
||||
|
@ -1368,12 +1393,13 @@ func (c *wsClient) WaitForShutdown() {
|
|||
// incoming and outgoing messages in separate goroutines complete with queueing
|
||||
// and asynchrous handling for long-running operations.
|
||||
func newWebsocketClient(server *rpcServer, conn *websocket.Conn,
|
||||
remoteAddr string, authenticated bool) *wsClient {
|
||||
remoteAddr string, authenticated bool, isAdmin bool) *wsClient {
|
||||
|
||||
return &wsClient{
|
||||
conn: conn,
|
||||
addr: remoteAddr,
|
||||
authenticated: authenticated,
|
||||
isAdmin: isAdmin,
|
||||
server: server,
|
||||
addrRequests: make(map[string]struct{}),
|
||||
spentRequests: make(map[wire.OutPoint]struct{}),
|
||||
|
|
|
@ -144,14 +144,18 @@
|
|||
; RPC server options - The following options control the built-in RPC server
|
||||
; which is used to control and query information from a running btcd process.
|
||||
;
|
||||
; NOTE: The RPC server is disabled by default if no rpcuser or rpcpass is
|
||||
; specified.
|
||||
; NOTE: The RPC server is disabled by default if rpcuser AND rpcpass, or
|
||||
; rpclimituser AND rpclimitpass, are not specified.
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; Secure the RPC API by specifying the username and password. You must specify
|
||||
; both or the RPC server will be disabled.
|
||||
; rpcuser=whatever_username_you_want
|
||||
; Secure the RPC API by specifying the username and password. You can also
|
||||
; specify a limited username and password. You must specify at least one
|
||||
; full set of credentials - limited or admin - or the RPC server will
|
||||
; be disabled.
|
||||
; rpcuser=whatever_admin_username_you_want
|
||||
; rpcpass=
|
||||
; rpclimituser=whatever_limited_username_you_want
|
||||
; rpclimitpass=
|
||||
|
||||
; Specify the interfaces for the RPC server listen on. One listen address per
|
||||
; line. NOTE: The default port is modified by some options such as 'testnet',
|
||||
|
|
Loading…
Add table
Reference in a new issue