2022-02-09 10:15:00 +00:00
package steward
2022-02-09 13:59:40 +00:00
2022-04-05 08:35:59 +00:00
import (
2022-04-07 12:18:28 +00:00
"bytes"
2022-06-01 05:29:25 +00:00
"crypto/sha256"
2022-04-05 08:35:59 +00:00
"fmt"
"log"
"path/filepath"
2022-06-01 05:29:25 +00:00
"sort"
2022-04-05 08:35:59 +00:00
"sync"
2022-06-01 05:29:25 +00:00
"github.com/fxamacker/cbor/v2"
2022-04-05 08:35:59 +00:00
bolt "go.etcd.io/bbolt"
)
2022-04-04 08:29:14 +00:00
2022-04-20 04:26:01 +00:00
// centralAuth holds the logic related to handling public keys and auth maps.
2022-02-09 13:59:40 +00:00
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
2022-05-11 07:55:27 +00:00
}
// 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 )
2022-06-01 13:58:17 +00:00
c . generateACLsForAllNodes ( )
2022-05-11 07:55:27 +00:00
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
2022-04-05 08:35:59 +00:00
}
2022-05-11 07:55:27 +00:00
// 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 {
2022-04-07 12:18:28 +00:00
// 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 ,
2022-04-05 08:35:59 +00:00
}
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 )
2022-06-09 06:42:49 +00:00
return & p
2022-04-05 08:35:59 +00:00
}
2022-05-12 07:25:10 +00:00
p . db = db
2022-04-05 08:35:59 +00:00
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 ( )
2022-04-05 08:35:59 +00:00
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 08:35:59 +00:00
}
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-04-05 08:35:59 +00:00
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
2022-02-09 13:59:40 +00:00
}
2022-04-05 08:35:59 +00:00
// addPublicKey to the db if the node do not exist, or if it is a new value.
2022-06-01 13:11:23 +00:00
func ( c * centralAuth ) addPublicKey ( proc process , msg Message ) {
2022-04-05 08:35:59 +00:00
// Check if a key for the current node already exists in the map.
2022-06-01 13:11:23 +00:00
c . pki . nodesAcked . mu . Lock ( )
existingKey , ok := c . pki . nodesAcked . keysAndHash . Keys [ msg . FromNode ]
c . pki . nodesAcked . mu . Unlock ( )
2022-04-05 08:35:59 +00:00
2022-04-07 12:18:28 +00:00
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 )
2022-04-05 08:35:59 +00:00
return
}
2022-06-01 13:11:23 +00:00
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 ) {
2022-06-01 13:11:23 +00:00
c . pki . nodeNotAckedPublicKeys . mu . Unlock ( )
2022-04-20 16:33:52 +00:00
return
2022-04-05 08:35:59 +00:00
}
2022-06-01 13:11:23 +00:00
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 )
2022-06-01 13:11:23 +00:00
c . pki . errorKernel . infoSend ( proc , msg , er )
2023-01-06 07:48:21 +00:00
c . pki . errorKernel . logConsoleOnlyIfDebug ( er , c . pki . configuration )
2022-04-05 08:35:59 +00:00
}
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.
2022-06-01 13:11:23 +00:00
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 ( ) {
2022-06-01 13:11:23 +00:00
c . pki . nodesAcked . mu . Lock ( )
defer c . pki . nodesAcked . mu . Unlock ( )
2022-06-01 05:29:25 +00:00
for _ , n := range nodes {
2022-06-01 13:11:23 +00:00
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 )
2022-06-01 13:11:23 +00:00
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-04-05 08:35:59 +00:00
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 {
2022-04-05 08:35:59 +00:00
//Create a bucket
2022-05-12 07:25:10 +00:00
bu , err := tx . CreateBucketIfNotExists ( [ ] byte ( p . bucketNamePublicKeys ) )
2022-04-05 08:35:59 +00:00
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
}
2022-06-01 13:11:23 +00:00
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.
2022-06-01 13:11:23 +00:00
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 )
2022-06-01 13:11:23 +00:00
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 )
2022-06-01 13:11:23 +00:00
c . pki . nodesAcked . keysAndHash . Hash = hash
2022-06-01 05:29:25 +00:00
// Store the key to the db for persistence.
2022-06-01 13:11:23 +00:00
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 )
2022-06-01 13:11:23 +00:00
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
// }
2022-04-05 08:35:59 +00:00
// 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 ) {
2022-04-07 12:18:28 +00:00
m := make ( map [ Node ] [ ] byte )
2022-04-05 08:35:59 +00:00
2022-05-12 07:25:10 +00:00
err := p . db . View ( func ( tx * bolt . Tx ) error {
bu := tx . Bucket ( [ ] byte ( p . bucketNamePublicKeys ) )
2022-04-05 08:35:59 +00:00
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 {
2022-04-07 12:18:28 +00:00
m [ Node ( k ) ] = v
2022-04-05 08:35:59 +00:00
return nil
} )
return nil
} )
if err != nil {
return nil , err
2022-02-09 13:59:40 +00:00
}
2022-04-05 08:35:59 +00:00
return m , nil
2022-02-09 13:59:40 +00:00
}
2022-04-04 08:29:14 +00:00
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
}