mirror of https://github.com/poanetwork/quorum.git
Merge pull request #15 from jpmorganchase/permissions-rebased
implemented node permissioning
This commit is contained in:
commit
a961389cc6
|
@ -156,6 +156,7 @@ participating.
|
|||
utils.VoteMinBlockTimeFlag,
|
||||
utils.VoteMaxBlockTimeFlag,
|
||||
utils.SingleBlockMakerFlag,
|
||||
utils.EnableNodePermissionFlag,
|
||||
}
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
|
||||
|
|
|
@ -369,6 +369,10 @@ var (
|
|||
Name: "singleblockmaker",
|
||||
Usage: "Indicate this node is the only node that can create blocks",
|
||||
}
|
||||
EnableNodePermissionFlag = cli.BoolFlag{
|
||||
Name: "permissioned",
|
||||
Usage: "If enabled, the node will allow only a defined list of nodes to connect",
|
||||
}
|
||||
)
|
||||
|
||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||
|
@ -588,28 +592,29 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
|||
}
|
||||
|
||||
config := &node.Config{
|
||||
DataDir: MakeDataDir(ctx),
|
||||
KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name),
|
||||
UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
|
||||
PrivateKey: MakeNodeKey(ctx),
|
||||
Name: name,
|
||||
Version: vsn,
|
||||
UserIdent: makeNodeUserIdent(ctx),
|
||||
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
||||
ListenAddr: MakeListenAddress(ctx),
|
||||
NAT: MakeNAT(ctx),
|
||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
||||
IPCPath: MakeIPCPath(ctx),
|
||||
HTTPHost: MakeHTTPRpcHost(ctx),
|
||||
HTTPPort: ctx.GlobalInt(RPCPortFlag.Name),
|
||||
HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name),
|
||||
HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)),
|
||||
WSHost: MakeWSRpcHost(ctx),
|
||||
WSPort: ctx.GlobalInt(WSPortFlag.Name),
|
||||
WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name),
|
||||
WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)),
|
||||
DataDir: MakeDataDir(ctx),
|
||||
KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name),
|
||||
UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
|
||||
PrivateKey: MakeNodeKey(ctx),
|
||||
Name: name,
|
||||
Version: vsn,
|
||||
UserIdent: makeNodeUserIdent(ctx),
|
||||
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
||||
ListenAddr: MakeListenAddress(ctx),
|
||||
NAT: MakeNAT(ctx),
|
||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
||||
IPCPath: MakeIPCPath(ctx),
|
||||
HTTPHost: MakeHTTPRpcHost(ctx),
|
||||
HTTPPort: ctx.GlobalInt(RPCPortFlag.Name),
|
||||
HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name),
|
||||
HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)),
|
||||
WSHost: MakeWSRpcHost(ctx),
|
||||
WSPort: ctx.GlobalInt(WSPortFlag.Name),
|
||||
WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name),
|
||||
WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)),
|
||||
EnableNodePermission: ctx.GlobalBool(EnableNodePermissionFlag.Name),
|
||||
}
|
||||
if ctx.GlobalBool(DevModeFlag.Name) {
|
||||
if !ctx.GlobalIsSet(DataDirFlag.Name) {
|
||||
|
|
|
@ -12,6 +12,7 @@ QUORUM OPTIONS:
|
|||
--singleblockmaker Indicate this node is the only node that can create blocks
|
||||
--minblocktime value Set minimum block time (default: 3)
|
||||
--maxblocktime value Set max block time (default: 10)
|
||||
--permissioned If enabled, the node will allow only a defined list of nodes to connect
|
||||
```
|
||||
|
||||
The full list of arguments can be viewed by running `geth --help`.
|
||||
|
@ -144,3 +145,29 @@ All scripts can be found in the `7nodes` folder in the `quorum-examples` reposit
|
|||
1. Step 1, run `init.sh` and initialize data directories (change variables accordingly)
|
||||
2. Step 2, start nodes with `start.sh` (change variables accordingly)
|
||||
3. Step 3, stop network with `stop.sh`
|
||||
|
||||
## Permissioned Network
|
||||
|
||||
Node Permissioning is a feature that controls which nodes can connect to a given node and also to which nodes this node can dial out to. Currently, it is managed at individual node level by the command line flag `--permissioned` while starting the node.
|
||||
|
||||
If the `--permissioned` node is present, the node looks for a file named `<data-dir>/permissioned-nodes.json`. This file contains the list of enodes that this node can connect to and also accepts connections only from those nodes. In other words, if permissioning is enabled, only the nodes that are listed in this file become part of the network. It is an error to enable `--permissioned` but not have the `permissioned-nodes.json` file. If the flag is given, but no nodes are present in this file, then this node can neither connect to any node or accept any incoming connections.
|
||||
|
||||
The `permissioned-nodes.json` follows following pattern (similar to `static-nodes.json`):
|
||||
|
||||
```json
|
||||
[
|
||||
"enode://enodehash1@ip1:port1",
|
||||
"enode://enodehash2@ip2:port2",
|
||||
"enode://enodehash3@ip3:port3",
|
||||
]
|
||||
```
|
||||
|
||||
Sample file:
|
||||
|
||||
```json
|
||||
[
|
||||
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300",
|
||||
]
|
||||
```
|
||||
|
||||
In the current release, every node has its own copy of `permissioned-nodes.json`. In a future release, the permissioned nodes list will be moved to a smart contract, thereby keeping the list on chain and one global list of nodes that connect to the network.
|
||||
|
|
|
@ -67,6 +67,7 @@ type Config struct {
|
|||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
Genesis string // Genesis JSON to seed the chain database with
|
||||
SingleBlockMaker bool // Assume this node is the only node on the network allowed to create blocks
|
||||
EnableNodePermission bool //Used for enabling / disabling node permissioning
|
||||
|
||||
SkipBcVersionCheck bool // e.g. blockchain export
|
||||
DatabaseCache int
|
||||
|
|
|
@ -158,6 +158,9 @@ type Config struct {
|
|||
// If the module list is empty, all RPC API endpoints designated public will be
|
||||
// exposed.
|
||||
WSModules []string
|
||||
|
||||
//enables node level Permissioning
|
||||
EnableNodePermission bool
|
||||
}
|
||||
|
||||
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
||||
|
|
|
@ -79,6 +79,8 @@ type Node struct {
|
|||
|
||||
stop chan struct{} // Channel to wait for termination notifications
|
||||
lock sync.RWMutex
|
||||
|
||||
|
||||
}
|
||||
|
||||
// New creates a new P2P node, ready for protocol registration.
|
||||
|
@ -167,6 +169,9 @@ func (n *Node) Start() error {
|
|||
NoDial: n.config.NoDial,
|
||||
MaxPeers: n.config.MaxPeers,
|
||||
MaxPendingPeers: n.config.MaxPendingPeers,
|
||||
EnableNodePermission: n.config.EnableNodePermission,
|
||||
DataDir: n.config.DataDir,
|
||||
|
||||
}
|
||||
running := &p2p.Server{Config: n.serverConfig}
|
||||
glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name)
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package p2p
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
)
|
||||
|
||||
const (
|
||||
NODE_NAME_LENGTH = 32
|
||||
PERMISSIONED_CONFIG = "permissioned-nodes.json"
|
||||
)
|
||||
|
||||
// check if a given node is permissioned to connect to the change
|
||||
func isNodePermissioned(nodename string, currentNode string, datadir string, direction string) bool {
|
||||
|
||||
var permissonedList []string
|
||||
nodes := parsePermissionedNodes(datadir)
|
||||
for _, v := range nodes {
|
||||
permissonedList = append(permissonedList, v.ID.String())
|
||||
}
|
||||
|
||||
glog.V(logger.Debug).Infof("Permisssioned_list %v", permissonedList)
|
||||
for _, v := range permissonedList {
|
||||
if v == nodename {
|
||||
glog.V(logger.Debug).Infof("isNodePermissioned <%v> connection:: nodename <%v> ALLOWED-BY <%v>", direction, nodename[:NODE_NAME_LENGTH], currentNode[:NODE_NAME_LENGTH])
|
||||
return true
|
||||
}
|
||||
glog.V(logger.Debug).Infof("isNodePermissioned <%v> connection:: nodename <%v> DENIED-BY <%v>", direction, nodename[:NODE_NAME_LENGTH], currentNode[:NODE_NAME_LENGTH])
|
||||
}
|
||||
glog.V(logger.Debug).Infof("isNodePermissioned <%v> connection:: nodename <%v> DENIED-BY <%v>", direction, nodename[:NODE_NAME_LENGTH], currentNode[:NODE_NAME_LENGTH])
|
||||
return false
|
||||
}
|
||||
|
||||
//this is a shameless copy from the config.go. It is a duplication of the code
|
||||
//for the timebeing to allow reload of the permissioned nodes while the server is running
|
||||
|
||||
func parsePermissionedNodes(DataDir string) []*discover.Node {
|
||||
|
||||
glog.V(logger.Debug).Infof("parsePermissionedNodes DataDir %v, file %v", DataDir, PERMISSIONED_CONFIG)
|
||||
|
||||
path := filepath.Join(DataDir, PERMISSIONED_CONFIG)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
glog.V(logger.Error).Infof("Read Error for permissioned-nodes.json file %v. This is because 'permissioned' flag is specified but no permissioned-nodes.json file is present.", err)
|
||||
return nil
|
||||
}
|
||||
// Load the nodes from the config file
|
||||
blob, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("parsePermissionedNodes: Failed to access nodes: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
nodelist := []string{}
|
||||
if err := json.Unmarshal(blob, &nodelist); err != nil {
|
||||
glog.V(logger.Error).Infof("parsePermissionedNodes: Failed to load nodes: %v", err)
|
||||
return nil
|
||||
}
|
||||
// Interpret the list as a discovery node array
|
||||
var nodes []*discover.Node
|
||||
for _, url := range nodelist {
|
||||
if url == "" {
|
||||
glog.V(logger.Error).Infof("parsePermissionedNodes: Node URL blank")
|
||||
continue
|
||||
}
|
||||
node, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("parsePermissionedNodes: Node URL %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
|
@ -116,6 +116,12 @@ type Config struct {
|
|||
|
||||
// If NoDial is true, the server will not dial any peers.
|
||||
NoDial bool
|
||||
|
||||
//Enables Permissioning
|
||||
EnableNodePermission bool
|
||||
|
||||
//DataDir
|
||||
DataDir string
|
||||
}
|
||||
|
||||
// Server manages all peer connections.
|
||||
|
@ -638,6 +644,30 @@ func (srv *Server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Nod
|
|||
c.close(err)
|
||||
return
|
||||
}
|
||||
//START - QUORUM Permissioning
|
||||
currentNode := srv.NodeInfo().ID
|
||||
cnodeName := srv.NodeInfo().Name
|
||||
glog.V(logger.Debug).Infof("EnableNodePermission <%v>, DataDir <%v>, Current Node ID <%v>, Node Name <%v>, Dialed Dest<%v>, Connection ID <%v>, Connection String <%v> ", srv.EnableNodePermission, srv.DataDir, currentNode, cnodeName, dialDest, c.id, c.id.String())
|
||||
|
||||
if srv.EnableNodePermission {
|
||||
glog.V(logger.Debug).Infof("Node Permissioning is Enabled. ")
|
||||
node := c.id.String()
|
||||
direction := "INCOMING"
|
||||
if dialDest != nil {
|
||||
node = dialDest.ID.String()
|
||||
direction = "OUTGOING"
|
||||
glog.V(logger.Debug).Infof("Connection Direction <%v>", direction)
|
||||
}
|
||||
|
||||
if !isNodePermissioned(node, currentNode, srv.DataDir, direction) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
glog.V(logger.Debug).Infof("Node Permissioning is Disabled. ")
|
||||
}
|
||||
|
||||
//END - QUORUM Permissioning
|
||||
|
||||
// For dialed connections, check that the remote public key matches.
|
||||
if dialDest != nil && c.id != dialDest.ID {
|
||||
c.close(DiscUnexpectedIdentity)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
|
||||
[
|
||||
|
||||
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300",
|
||||
"enode://e4d8738cd275024de9799669f60e2d5b6b4adc9439430748906e1cec5a4c0aed736000b24bf6c75bfe4c2ac768bdbe365046a98b624ba4641b451847a7150c1b@192.168.0.105:30302",
|
||||
"enode://920b930bcd285cda9edf4c0c92a65026cabfb18d820d121178dacef8902b93203b87b8f5acd01715ff3864cbff041d69affd739e2521d81ac1e40c6b17d25ee@192.168.0.105:30303",
|
||||
"enode://a311f122aa3b2d9deffc721f39fa9ee4816dcec68e7845b5a1c6875ecaf4fcadec48c0b6a6e50bda78e44bd68cf966a3c92509c4abe09fe32b282cae9d1d1d1b@192.168.0.105:30304",
|
||||
"enode://58a71648e004675ae30f6e0953cc89cbbd05a5f6744d2fd15cc04b8923d4079185acefb669d608081b00aa5bfd03722f4843b4c71d9021ff8cb3a25b95673773@192.168.0.105:30305",
|
||||
"enode://aaae04bb274dabf04f03a18548bd763c999ac88d029a756ad69e8082178d6dc10d3ed5be50947ee57478f4c5ae8c73dc88774b2992c942477c589e50a826b0d6@192.168.0.105:30306",
|
||||
"enode://9287b30ed6753eb27a017daa4f23504979070d3353de2a5dc572f46ab338393ee8c5afe00b79d28e8bffab0774c4ecdcfb62e5ac5a51c4544e350fae309fae21@192.168.0.105:30307",
|
||||
"enode://ce70feafc1ca25d815b492b2625625c2a79c7fe551a75ea42a9b28e5c956b117eab0fb86e3f344b51ef8fcdef0d7c3c4305d89089d1b8fbbafd52607679f4475@192.168.0.105:30308",
|
||||
"enode://66f5ad6594aadb6cc7111b033304c3e03a5bf274d2535a3c15613125d35f3a489426569ae587f2d55deffcfda93766fe264fc139f6601ef1932d7113278f27e3@192.168.0.105:30309"
|
||||
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
[
|
||||
|
||||
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300",
|
||||
"enode://e4d8738cd275024de9799669f60e2d5b6b4adc9439430748906e1cec5a4c0aed736000b24bf6c75bfe4c2ac768bdbe365046a98b624ba4641b451847a7150c1b@192.168.0.105:30302",
|
||||
"enode://920b930bcd285cda9edf4c0c92a65026cabfb18d820d121178dacef8902b93203b87b8f5acd01715ff3864cbff041d69affd739e2521d81ac1e40c6b17d25ee@192.168.0.105:30303",
|
||||
"enode://a311f122aa3b2d9deffc721f39fa9ee4816dcec68e7845b5a1c6875ecaf4fcadec48c0b6a6e50bda78e44bd68cf966a3c92509c4abe09fe32b282cae9d1d1d1b@192.168.0.105:30304",
|
||||
"enode://58a71648e004675ae30f6e0953cc89cbbd05a5f6744d2fd15cc04b8923d4079185acefb669d608081b00aa5bfd03722f4843b4c71d9021ff8cb3a25b95673773@192.168.0.105:30305",
|
||||
"enode://aaae04bb274dabf04f03a18548bd763c999ac88d029a756ad69e8082178d6dc10d3ed5be50947ee57478f4c5ae8c73dc88774b2992c942477c589e50a826b0d6@192.168.0.105:30306",
|
||||
"enode://9287b30ed6753eb27a017daa4f23504979070d3353de2a5dc572f46ab338393ee8c5afe00b79d28e8bffab0774c4ecdcfb62e5ac5a51c4544e350fae309fae21@192.168.0.105:30307",
|
||||
"enode://ce70feafc1ca25d815b492b2625625c2a79c7fe551a75ea42a9b28e5c956b117eab0fb86e3f344b51ef8fcdef0d7c3c4305d89089d1b8fbbafd52607679f4475@192.168.0.105:30308",
|
||||
"enode://66f5ad6594aadb6cc7111b033304c3e03a5bf274d2535a3c15613125d35f3a489426569ae587f2d55deffcfda93766fe264fc139f6601ef1932d7113278f27e3@192.168.0.105:30309"
|
||||
|
||||
]
|
Loading…
Reference in New Issue