diff --git a/p2p/peer_error.go b/p2p/peer_error.go index ab61bfef06..64c5b1f041 100644 --- a/p2p/peer_error.go +++ b/p2p/peer_error.go @@ -69,6 +69,7 @@ const ( DiscUnexpectedIdentity DiscSelf DiscReadTimeout + DiscPermissionFailed DiscSubprotocolError = 0x10 ) diff --git a/p2p/permissions.go b/p2p/permissions.go new file mode 100644 index 0000000000..635b08a4be --- /dev/null +++ b/p2p/permissions.go @@ -0,0 +1,79 @@ +package p2p + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/log" + "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 permissionedList []string + nodes := parsePermissionedNodes(datadir) + for _, v := range nodes { + permissionedList = append(permissionedList, v.ID.String()) + } + + log.Debug("isNodePermissioned", "permissionedList", permissionedList) + for _, v := range permissionedList { + if v == nodename { + log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "ALLOWED-BY", currentNode[:NODE_NAME_LENGTH]) + return true + } + log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH]) + } + log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", 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 { + + log.Debug("parsePermissionedNodes", "DataDir", DataDir, "file", PERMISSIONED_CONFIG) + + path := filepath.Join(DataDir, PERMISSIONED_CONFIG) + if _, err := os.Stat(path); err != nil { + log.Error("Read Error for permissioned-nodes.json file. This is because 'permissioned' flag is specified but no permissioned-nodes.json file is present.", "err", err) + return nil + } + // Load the nodes from the config file + blob, err := ioutil.ReadFile(path) + if err != nil { + log.Error("parsePermissionedNodes: Failed to access nodes", "err", err) + return nil + } + + nodelist := []string{} + if err := json.Unmarshal(blob, &nodelist); err != nil { + log.Error("parsePermissionedNodes: Failed to load nodes", "err", err) + return nil + } + // Interpret the list as a discovery node array + var nodes []*discover.Node + for _, url := range nodelist { + if url == "" { + log.Error("parsePermissionedNodes: Node URL blank") + continue + } + node, err := discover.ParseNode(url) + if err != nil { + log.Error("parsePermissionedNodes: Node URL", "url", url, "err", err) + continue + } + //log.Error("json node: "+node.String()) + nodes = append(nodes, node) + } + return nodes +} diff --git a/p2p/server.go b/p2p/server.go index cdb5b1926e..b0189dd305 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -141,6 +141,10 @@ type Config struct { // Logger is a custom logger to use with the p2p.Server. Logger log.Logger `toml:",omitempty"` + + EnableNodePermission bool `toml:",omitempty"` + + DataDir string `toml:",omitempty"` } // Server manages all peer connections. @@ -820,6 +824,38 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err) return err } + + //START Permissioning + currentNode := srv.NodeInfo().ID + cnodeName := srv.NodeInfo().Name + log.Trace("Quorum permissioning", + "EnableNodePermission", srv.EnableNodePermission, + "DataDir", srv.DataDir, + "Current Node ID", currentNode, + "Node Name", cnodeName, + "Dialed Dest", dialDest, + "Connection ID", c.id, + "Connection String", c.id.String()) + + + log.Trace("Node Permissioning is Enabled.") + node := c.id.String() + direction := "INCOMING" + if dialDest != nil { + node = dialDest.ID.String() + direction = "OUTGOING" + log.Trace("Node Permissioning", "Connection Direction", direction) + } + + //log.Error("entry permission check.") + if !isNodePermissioned(node, currentNode, srv.DataDir, direction) { + //log.Error("permission check failed.") + return DiscPermissionFailed + } + + + //END Permissioning + clog := srv.log.New("id", c.id, "addr", c.fd.RemoteAddr(), "conn", c.flags) // For dialed connections, check that the remote public key matches. if dialDest != nil && c.id != dialDest.ID {