1
0
Fork 0
mirror of https://github.com/postmannen/ctrl.git synced 2024-12-14 12:37:31 +00:00
ctrl/central_auth_key_handling.go

400 lines
11 KiB
Go
Raw Normal View History

package steward
import (
"bytes"
2022-06-01 05:29:25 +00:00
"crypto/sha256"
"fmt"
"log"
"path/filepath"
2022-06-01 05:29:25 +00:00
"sort"
"sync"
2022-06-01 05:29:25 +00:00
"github.com/fxamacker/cbor/v2"
bolt "go.etcd.io/bbolt"
)
// centralAuth holds the logic related to handling public keys and auth maps.
type centralAuth struct {
2022-05-16 05:15:38 +00:00
// acl and authorization level related data and methods.
2022-05-18 07:21:14 +00:00
accessLists *accessLists
2022-05-16 05:15:38 +00:00
// public key distribution related data and methods.
pki *pki
}
// newCentralAuth will return a new and prepared *centralAuth
func newCentralAuth(configuration *Configuration, errorKernel *errorKernel) *centralAuth {
2022-06-01 11:04:34 +00:00
c := centralAuth{}
c.pki = newPKI(configuration, errorKernel)
2022-06-01 15:35:23 +00:00
c.accessLists = newAccessLists(c.pki, errorKernel, configuration)
c.generateACLsForAllNodes()
return &c
}
2022-05-16 05:15:38 +00:00
// nodesAcked is the structure that holds all the keys that we have
// acknowledged, and that are allowed to be distributed within the
// system. It also contains a hash of all those keys.
type nodesAcked struct {
mu sync.Mutex
keysAndHash *keysAndHash
}
// newNodesAcked will return a prepared *nodesAcked structure.
func newNodesAcked() *nodesAcked {
n := nodesAcked{
keysAndHash: newKeysAndHash(),
}
return &n
}
// pki holds the data and method relevant to key handling and distribution.
2022-05-12 07:25:10 +00:00
type pki struct {
2022-05-16 05:15:38 +00:00
nodesAcked *nodesAcked
2022-04-20 16:33:52 +00:00
nodeNotAckedPublicKeys *nodeNotAckedPublicKeys
configuration *Configuration
db *bolt.DB
bucketNamePublicKeys string
errorKernel *errorKernel
}
// newKeys will return a prepared *keys with input values set.
2022-05-12 07:25:10 +00:00
func newPKI(configuration *Configuration, errorKernel *errorKernel) *pki {
p := pki{
// schema: make(map[Node]map[argsString]signatureBase32),
2022-05-16 05:15:38 +00:00
nodesAcked: newNodesAcked(),
2022-04-20 16:33:52 +00:00
nodeNotAckedPublicKeys: newNodeNotAckedPublicKeys(configuration),
configuration: configuration,
bucketNamePublicKeys: "publicKeys",
errorKernel: errorKernel,
}
databaseFilepath := filepath.Join(configuration.DatabaseFolder, "auth.db")
// Open the database file for persistent storage of public keys.
db, err := bolt.Open(databaseFilepath, 0600, nil)
if err != nil {
log.Printf("error: failed to open db: %v\n", err)
return &p
}
2022-05-12 07:25:10 +00:00
p.db = db
2022-04-05 10:02:45 +00:00
// Get public keys from db storage.
2022-05-12 07:25:10 +00:00
keys, err := p.dbDumpPublicKey()
if err != nil {
2022-04-05 10:02:45 +00:00
log.Printf("debug: dbPublicKeyDump failed, probably empty db: %v\n", err)
}
2022-04-05 10:02:45 +00:00
// Only assign from storage to in memory map if the storage contained any values.
if keys != nil {
2022-05-16 05:15:38 +00:00
p.nodesAcked.keysAndHash.Keys = keys
2022-04-05 10:02:45 +00:00
for k, v := range keys {
log.Printf("info: public keys db contains: %v, %v\n", k, []byte(v))
}
}
2022-05-16 05:15:38 +00:00
// Get the current hash from db if one exists.
hash, err := p.dbViewHash()
if err != nil {
log.Printf("debug: dbViewHash failed: %v\n", err)
}
if hash != nil {
var h [32]byte
copy(h[:], hash)
p.nodesAcked.keysAndHash.Hash = h
}
2022-05-12 07:25:10 +00:00
return &p
}
// addPublicKey to the db if the node do not exist, or if it is a new value.
func (c *centralAuth) addPublicKey(proc process, msg Message) {
// Check if a key for the current node already exists in the map.
c.pki.nodesAcked.mu.Lock()
existingKey, ok := c.pki.nodesAcked.keysAndHash.Keys[msg.FromNode]
c.pki.nodesAcked.mu.Unlock()
if ok && bytes.Equal(existingKey, msg.Data) {
2022-10-05 07:16:22 +00:00
er := fmt.Errorf("info: public key value for REGISTERED node %v is the same, doing nothing", msg.FromNode)
proc.errorKernel.logConsoleOnlyIfDebug(er, proc.configuration)
return
}
c.pki.nodeNotAckedPublicKeys.mu.Lock()
existingNotAckedKey, ok := c.pki.nodeNotAckedPublicKeys.KeyMap[msg.FromNode]
2022-04-20 16:33:52 +00:00
// We only want to send one notification to the error kernel about new key detection,
// so we check if the values are the same as the one we already got before we continue
// with registering and logging for the the new key.
if ok && bytes.Equal(existingNotAckedKey, msg.Data) {
c.pki.nodeNotAckedPublicKeys.mu.Unlock()
2022-04-20 16:33:52 +00:00
return
}
c.pki.nodeNotAckedPublicKeys.KeyMap[msg.FromNode] = msg.Data
c.pki.nodeNotAckedPublicKeys.mu.Unlock()
2022-04-20 16:33:52 +00:00
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)
c.pki.errorKernel.infoSend(proc, msg, er)
2023-01-06 07:48:21 +00:00
c.pki.errorKernel.logConsoleOnlyIfDebug(er, c.pki.configuration)
}
2022-06-01 05:29:25 +00:00
// deletePublicKeys to the db if the node do not exist, or if it is a new value.
func (c *centralAuth) deletePublicKeys(proc process, msg Message, nodes []string) {
2022-06-01 05:29:25 +00:00
// Check if a key for the current node already exists in the map.
func() {
c.pki.nodesAcked.mu.Lock()
defer c.pki.nodesAcked.mu.Unlock()
2022-06-01 05:29:25 +00:00
for _, n := range nodes {
delete(c.pki.nodesAcked.keysAndHash.Keys, Node(n))
2022-06-01 05:29:25 +00:00
}
}()
2022-10-05 07:16:22 +00:00
err := c.pki.dbDeletePublicKeys(c.pki.bucketNamePublicKeys, nodes)
if err != nil {
proc.errorKernel.errSend(proc, msg, err)
}
2022-06-01 05:29:25 +00:00
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)
2022-10-05 07:16:22 +00:00
proc.errorKernel.logConsoleOnlyIfDebug(er, proc.configuration)
c.pki.errorKernel.infoSend(proc, msg, er)
2022-06-01 05:29:25 +00:00
}
2022-05-06 05:47:12 +00:00
// // dbGetPublicKey will look up and return a specific value if it exists for a key in a bucket in a DB.
// func (c *centralAuth) dbGetPublicKey(node string) ([]byte, error) {
// var value []byte
// // View is a help function to get values out of the database.
// err := c.db.View(func(tx *bolt.Tx) error {
// //Open a bucket to get key's and values from.
// bu := tx.Bucket([]byte(c.bucketNamePublicKeys))
// if bu == nil {
// log.Printf("info: no db bucket exist: %v\n", c.bucketNamePublicKeys)
// return nil
// }
//
// v := bu.Get([]byte(node))
// if len(v) == 0 {
// log.Printf("info: view: key not found\n")
// return nil
// }
//
// value = v
//
// return nil
// })
//
// return value, err
// }
2022-10-05 07:16:22 +00:00
// dbUpdatePublicKey will update the public key for a node in the db.
2022-05-12 07:25:10 +00:00
func (p *pki) dbUpdatePublicKey(node string, value []byte) error {
err := p.db.Update(func(tx *bolt.Tx) error {
//Create a bucket
2022-05-12 07:25:10 +00:00
bu, err := tx.CreateBucketIfNotExists([]byte(p.bucketNamePublicKeys))
if err != nil {
return fmt.Errorf("error: CreateBuckerIfNotExists failed: %v", err)
}
//Put a value into the bucket.
if err := bu.Put([]byte(node), []byte(value)); err != nil {
return err
}
//If all was ok, we should return a nil for a commit to happen. Any error
// returned will do a rollback.
return nil
})
return err
}
2022-06-01 05:29:25 +00:00
// dbDeletePublicKeys will delete the specified key from the specified
// bucket if it exists.
func (p *pki) dbDeletePublicKeys(bucket string, nodes []string) error {
err := p.db.Update(func(tx *bolt.Tx) error {
bu := tx.Bucket([]byte(bucket))
for _, n := range nodes {
err := bu.Delete([]byte(n))
if err != nil {
2022-10-05 07:16:22 +00:00
er := fmt.Errorf("error: delete key in bucket %v failed: %v", bucket, err)
p.errorKernel.logConsoleOnlyIfDebug(er, p.configuration)
return er
2022-06-01 05:29:25 +00:00
}
}
return nil
})
return err
}
2022-10-05 07:16:22 +00:00
// dbUpdateHash will update the public key for a node in the db.
2022-05-16 05:15:38 +00:00
func (p *pki) dbUpdateHash(hash []byte) error {
err := p.db.Update(func(tx *bolt.Tx) error {
//Create a bucket
bu, err := tx.CreateBucketIfNotExists([]byte("hash"))
if err != nil {
return fmt.Errorf("error: CreateBuckerIfNotExists failed: %v", err)
}
//Put a value into the bucket.
if err := bu.Put([]byte("hash"), []byte(hash)); err != nil {
return err
}
//If all was ok, we should return a nil for a commit to happen. Any error
// returned will do a rollback.
return nil
})
return err
}
func (c *centralAuth) updateHash(proc process, message Message) {
c.pki.nodesAcked.mu.Lock()
defer c.pki.nodesAcked.mu.Unlock()
2022-06-01 05:29:25 +00:00
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 c.pki.nodesAcked.keysAndHash.Keys {
2022-06-01 05:29:25 +00:00
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: methodREQKeysAllow, failed to marshal slice, and will not update hash for public keys: %v", err)
c.pki.errorKernel.errSend(proc, message, er)
2022-06-01 05:29:25 +00:00
log.Printf(" * DEBUG: %v\n", er)
return
}
// Store the key in the key value map.
hash := sha256.Sum256(b)
c.pki.nodesAcked.keysAndHash.Hash = hash
2022-06-01 05:29:25 +00:00
// Store the key to the db for persistence.
c.pki.dbUpdateHash(hash[:])
2022-06-01 05:29:25 +00:00
if err != nil {
er := fmt.Errorf("error: methodREQKeysAllow, failed to store the hash into the db: %v", err)
c.pki.errorKernel.errSend(proc, message, er)
2022-06-01 05:29:25 +00:00
log.Printf(" * DEBUG: %v\n", er)
return
}
}
2022-05-16 05:15:38 +00:00
// dbViewHash will look up and return a specific value if it exists for a key in a bucket in a DB.
func (p *pki) dbViewHash() ([]byte, error) {
var value []byte
// View is a help function to get values out of the database.
err := p.db.View(func(tx *bolt.Tx) error {
//Open a bucket to get key's and values from.
bu := tx.Bucket([]byte("hash"))
if bu == nil {
log.Printf("info: no db hash bucket exist\n")
return nil
}
v := bu.Get([]byte("hash"))
if len(v) == 0 {
log.Printf("info: view: hash key not found\n")
return nil
}
value = v
return nil
})
return value, err
}
2022-05-06 05:47:12 +00:00
// // deleteKeyFromBucket will delete the specified key from the specified
// // bucket if it exists.
// func (c *centralAuth) dbDeletePublicKey(key string) error {
// err := c.db.Update(func(tx *bolt.Tx) error {
// bu := tx.Bucket([]byte(c.bucketNamePublicKeys))
//
// err := bu.Delete([]byte(key))
// if err != nil {
// log.Printf("error: delete key in bucket %v failed: %v\n", c.bucketNamePublicKeys, err)
// }
//
// return nil
// })
//
// return err
// }
// dumpBucket will dump out all they keys and values in the
// specified bucket, and return a sorted []samDBValue
2022-05-12 07:25:10 +00:00
func (p *pki) dbDumpPublicKey() (map[Node][]byte, error) {
m := make(map[Node][]byte)
2022-05-12 07:25:10 +00:00
err := p.db.View(func(tx *bolt.Tx) error {
bu := tx.Bucket([]byte(p.bucketNamePublicKeys))
if bu == nil {
return fmt.Errorf("error: dumpBucket: tx.bucket returned nil")
}
// For each element found in the DB, print it.
bu.ForEach(func(k, v []byte) error {
m[Node(k)] = v
return nil
})
return nil
})
if err != nil {
return nil, err
}
return m, nil
}
2022-04-20 16:33:52 +00:00
// --- HERE
// nodeNotAckedPublicKeys holds all the gathered but not acknowledged public
// keys of nodes in the system.
type nodeNotAckedPublicKeys struct {
mu sync.RWMutex
KeyMap map[Node][]byte
}
// newNodeNotAckedPublicKeys will return a prepared type of nodePublicKeys.
func newNodeNotAckedPublicKeys(configuration *Configuration) *nodeNotAckedPublicKeys {
n := nodeNotAckedPublicKeys{
KeyMap: make(map[Node][]byte),
}
return &n
}