Skip to content

Commit

Permalink
Merge pull request ethereum#15 from jpmorganchase/permissions-rebased
Browse files Browse the repository at this point in the history
implemented node permissioning
  • Loading branch information
patrickmn authored Nov 18, 2016
2 parents 8011cd2 + 5806b45 commit a961389
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 22 deletions.
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ participating.
utils.VoteMinBlockTimeFlag,
utils.VoteMaxBlockTimeFlag,
utils.SingleBlockMakerFlag,
utils.EnableNodePermissionFlag,
}
app.Flags = append(app.Flags, debug.Flags...)

Expand Down
49 changes: 27 additions & 22 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
27 changes: 27 additions & 0 deletions docs/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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.
1 change: 1 addition & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
80 changes: 80 additions & 0 deletions p2p/permissions.go
Original file line number Diff line number Diff line change
@@ -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
}

30 changes: 30 additions & 0 deletions p2p/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
15 changes: 15 additions & 0 deletions permissioned-nodes-sample.json
Original file line number Diff line number Diff line change
@@ -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"

]
13 changes: 13 additions & 0 deletions permissioned-nodes.json
Original file line number Diff line number Diff line change
@@ -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"

]

0 comments on commit a961389

Please sign in to comment.