diff --git a/central_auth.go b/central_auth.go index 95fecfb..3fe9d62 100644 --- a/central_auth.go +++ b/central_auth.go @@ -29,7 +29,7 @@ func newCentralAuth(configuration *Configuration, errorKernel *errorKernel) *cen } type keys struct { - nodePublicKeys *nodePublicKeys + NodePublicKeys *nodePublicKeys nodeNotAckedPublicKeys *nodeNotAckedPublicKeys configuration *Configuration db *bolt.DB @@ -41,7 +41,7 @@ type keys struct { func newKeys(configuration *Configuration, errorKernel *errorKernel) *keys { c := keys{ // schema: make(map[Node]map[argsString]signatureBase32), - nodePublicKeys: newNodePublicKeys(configuration), + NodePublicKeys: newNodePublicKeys(configuration), nodeNotAckedPublicKeys: newNodeNotAckedPublicKeys(configuration), configuration: configuration, bucketNamePublicKeys: "publicKeys", @@ -67,7 +67,7 @@ func newKeys(configuration *Configuration, errorKernel *errorKernel) *keys { // Only assign from storage to in memory map if the storage contained any values. if keys != nil { - c.nodePublicKeys.KeyMap = keys + c.NodePublicKeys.KeyMap = keys for k, v := range keys { log.Printf("info: public keys db contains: %v, %v\n", k, []byte(v)) } @@ -90,9 +90,9 @@ func (c *keys) addPublicKey(proc process, msg Message) { // key for a host. // Check if a key for the current node already exists in the map. - c.nodePublicKeys.mu.Lock() - existingKey, ok := c.nodePublicKeys.KeyMap[msg.FromNode] - c.nodePublicKeys.mu.Unlock() + c.NodePublicKeys.mu.Lock() + existingKey, ok := c.NodePublicKeys.KeyMap[msg.FromNode] + c.NodePublicKeys.mu.Unlock() if ok && bytes.Equal(existingKey, msg.Data) { fmt.Printf(" * \nkey value for REGISTERED node %v is the same, doing nothing\n\n", msg.FromNode) @@ -116,30 +116,6 @@ func (c *keys) addPublicKey(proc process, msg Message) { er := fmt.Errorf("info: detected new public key for node: %v. This key will need to be authorized by operator to be allowed into the system", msg.FromNode) fmt.Printf(" * %v\n", er) c.errorKernel.infoSend(proc, msg, er) - - // TODO: The below commented code should put used within the REQ handler instead to - // store the real keys into the allowed public keys map. - // Here we should only add new keys to the NotAcked map. - - // // New key - // c.nodePublicKeys.KeyMap[msg.FromNode] = msg.Data - // c.nodePublicKeys.mu.Unlock() - // - // // Add key to persistent storage. - // c.dbUpdatePublicKey(string(msg.FromNode), msg.Data) - // - // if ok { - // er := fmt.Errorf("info: updated with new public key for node: %v", msg.FromNode) - // fmt.Printf(" * %v\n", er) - // c.errorKernel.infoSend(proc, msg, er) - // } - // if !ok { - // er := fmt.Errorf("info: added public key for new node: %v", msg.FromNode) - // fmt.Printf(" * %v\n", er) - // c.errorKernel.infoSend(proc, msg, er) - // } - - //c.dbDump(c.bucketPublicKeys) } // // dbGetPublicKey will look up and return a specific value if it exists for a key in a bucket in a DB. @@ -238,6 +214,11 @@ func (c *keys) dbDumpPublicKey() (map[Node][]byte, error) { type nodePublicKeys struct { mu sync.RWMutex KeyMap map[Node][]byte + // TODO TOMOROW: implement sorting of KeyMap, + // Hash it and store the result into hash, + // marshal and send the whole nodePublicKeys to the end node. + // We should update the hash when a node is added with the allow key method. + Hash [32]byte } // newNodePublicKeys will return a prepared type of nodePublicKeys. diff --git a/processes.go b/processes.go index 42e6161..8b01f5b 100644 --- a/processes.go +++ b/processes.go @@ -449,9 +449,9 @@ func (s startup) subREQHello(p process) { // update the prometheus metrics - s.server.centralAuth.keys.nodePublicKeys.mu.Lock() - mapLen := len(s.server.centralAuth.keys.nodePublicKeys.KeyMap) - s.server.centralAuth.keys.nodePublicKeys.mu.Unlock() + s.server.centralAuth.keys.NodePublicKeys.mu.Lock() + mapLen := len(s.server.centralAuth.keys.NodePublicKeys.KeyMap) + s.server.centralAuth.keys.NodePublicKeys.mu.Unlock() s.metrics.promHelloNodesTotal.Set(float64(mapLen)) s.metrics.promHelloNodesContactLast.With(prometheus.Labels{"nodeName": string(m.FromNode)}).SetToCurrentTime() diff --git a/requests.go b/requests.go index 26d321f..f85bfd2 100644 --- a/requests.go +++ b/requests.go @@ -36,19 +36,23 @@ import ( "bufio" "bytes" "context" + "crypto/sha256" "encoding/json" "fmt" "io" + "log" "net/http" "os" "os/exec" "path" "path/filepath" + "sort" "strconv" "strings" "sync" "time" + "github.com/fxamacker/cbor/v2" "github.com/hpcloud/tail" "github.com/prometheus/client_golang/prometheus" ) @@ -2060,14 +2064,14 @@ func (m methodREQPublicKeysGet) handler(proc process, message Message, node stri case <-ctx.Done(): // case out := <-outCh: case <-outCh: - proc.centralAuth.keys.nodePublicKeys.mu.Lock() + proc.centralAuth.keys.NodePublicKeys.mu.Lock() // TODO: We should probably create a hash of the current map content, // store it alongside the KeyMap, and send both the KeyMap and hash // back. We can then later send that hash when asking for keys, compare // it with the current one for the KeyMap, and know if we need to send // and update back to the node who published the request to here. - b, err := json.Marshal(proc.centralAuth.keys.nodePublicKeys.KeyMap) - proc.centralAuth.keys.nodePublicKeys.mu.Unlock() + b, err := json.Marshal(proc.centralAuth.keys.NodePublicKeys.KeyMap) + proc.centralAuth.keys.NodePublicKeys.mu.Unlock() if err != nil { er := fmt.Errorf("error: REQPublicKeysGet, failed to marshal keys map: %v", err) proc.errorKernel.errSend(proc, message, er) @@ -2192,13 +2196,19 @@ func (m methodREQPublicKeysAllow) handler(proc process, message Message, node st proc.centralAuth.keys.nodeNotAckedPublicKeys.mu.Lock() defer proc.centralAuth.keys.nodeNotAckedPublicKeys.mu.Unlock() + // Range over all the MethodArgs, where each element represents a node to allow, + // and move the node from the notAcked map to the allowed map. for _, n := range message.MethodArgs { key, ok := proc.centralAuth.keys.nodeNotAckedPublicKeys.KeyMap[Node(n)] if ok { - // Store/update the node and public key on the allowed pubKey map. - proc.centralAuth.keys.nodePublicKeys.mu.Lock() - proc.centralAuth.keys.nodePublicKeys.KeyMap[Node(n)] = key - proc.centralAuth.keys.nodePublicKeys.mu.Unlock() + + func() { + proc.centralAuth.keys.NodePublicKeys.mu.Lock() + defer proc.centralAuth.keys.NodePublicKeys.mu.Unlock() + + // Store/update the node and public key on the allowed pubKey map. + proc.centralAuth.keys.NodePublicKeys.KeyMap[Node(n)] = key + }() // Add key to persistent storage. proc.centralAuth.keys.dbUpdatePublicKey(string(n), key) @@ -2211,6 +2221,51 @@ func (m methodREQPublicKeysAllow) handler(proc process, message Message, node st } } + // All new elements are now added, and we can create a new hash + // representing the current keys in the allowed map. + func() { + proc.centralAuth.keys.NodePublicKeys.mu.Lock() + defer proc.centralAuth.keys.NodePublicKeys.mu.Unlock() + + type NodesAndKeys struct { + Node Node + Key []byte + } + + // Create a slice of all the map keys, and its value. + sortedNodesAndKeys := []NodesAndKeys{} + + // Range the map, and add each k/v to the sorted slice, to be sorted later. + for k, v := range proc.centralAuth.keys.NodePublicKeys.KeyMap { + nk := NodesAndKeys{ + Node: k, + Key: v, + } + + sortedNodesAndKeys = append(sortedNodesAndKeys, nk) + } + + // sort the slice based on the node name. + // Sort all the commands. + sort.SliceStable(sortedNodesAndKeys, func(i, j int) bool { + return sortedNodesAndKeys[i].Node < sortedNodesAndKeys[j].Node + }) + + // Then create a hash based on the sorted slice. + + b, err := cbor.Marshal(sortedNodesAndKeys) + if err != nil { + er := fmt.Errorf("error: methodREQPublicKeysAllow, failed to marshal slice, and will not update hash for public keys: %v", err) + proc.errorKernel.errSend(proc, message, er) + log.Printf(" * DEBUG: %v\n", err) + + return + } + + proc.centralAuth.keys.NodePublicKeys.Hash = sha256.Sum256(b) + + }() + } }()