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-04-05 08:35:59 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
bolt "go.etcd.io/bbolt"
|
|
|
|
)
|
2022-04-04 08:29:14 +00:00
|
|
|
|
2022-02-09 13:59:40 +00:00
|
|
|
type signatureBase32 string
|
|
|
|
type argsString string
|
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-04-07 12:18:28 +00:00
|
|
|
// schema map[Node]map[argsString]signatureBase32
|
|
|
|
nodePublicKeys *nodePublicKeys
|
|
|
|
configuration *Configuration
|
|
|
|
db *bolt.DB
|
|
|
|
bucketNamePublicKeys string
|
|
|
|
errorKernel *errorKernel
|
2022-04-05 08:35:59 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 04:26:01 +00:00
|
|
|
// newCentralAuth will return a prepared *centralAuth with input values set.
|
2022-04-05 08:35:59 +00:00
|
|
|
func newCentralAuth(configuration *Configuration, errorKernel *errorKernel) *centralAuth {
|
|
|
|
c := centralAuth{
|
2022-04-07 12:18:28 +00:00
|
|
|
// schema: make(map[Node]map[argsString]signatureBase32),
|
|
|
|
nodePublicKeys: newNodePublicKeys(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)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.db = db
|
|
|
|
|
2022-04-05 10:02:45 +00:00
|
|
|
// Get public keys from db storage.
|
2022-04-05 08:35:59 +00:00
|
|
|
keys, err := c.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 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-04-07 07:34:06 +00:00
|
|
|
c.nodePublicKeys.KeyMap = 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
|
|
|
|
|
|
|
return &c
|
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.
|
|
|
|
func (c *centralAuth) addPublicKey(proc process, msg Message) {
|
|
|
|
c.nodePublicKeys.mu.Lock()
|
|
|
|
|
2022-04-20 06:41:57 +00:00
|
|
|
// TODO: When receiviving a new or different keys for a node we should
|
|
|
|
// have a service with it's own storage for these keys, and an operator
|
|
|
|
// should have to acknowledge the new keys.
|
|
|
|
// For this we need:
|
|
|
|
// - A service that keeps the state of all the new keys detected in the
|
|
|
|
// bytes.equal check below.
|
|
|
|
// - A Log message should be thrown so we know that there is a new key.
|
|
|
|
// - A Request method that can be used by operator to acknowledge a new
|
|
|
|
// key for a host.
|
|
|
|
|
2022-04-05 08:35:59 +00:00
|
|
|
// Check if a key for the current node already exists in the map.
|
2022-04-07 07:34:06 +00:00
|
|
|
existingKey, ok := c.nodePublicKeys.KeyMap[msg.FromNode]
|
2022-04-05 08:35:59 +00:00
|
|
|
|
2022-04-07 12:18:28 +00:00
|
|
|
if ok && bytes.Equal(existingKey, msg.Data) {
|
2022-04-05 08:35:59 +00:00
|
|
|
fmt.Printf(" * key value for node %v is the same, doing nothing\n", msg.FromNode)
|
|
|
|
c.nodePublicKeys.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// New key
|
2022-04-07 12:18:28 +00:00
|
|
|
c.nodePublicKeys.KeyMap[msg.FromNode] = msg.Data
|
2022-04-05 08:35:59 +00:00
|
|
|
c.nodePublicKeys.mu.Unlock()
|
|
|
|
|
|
|
|
// Add key to persistent storage.
|
|
|
|
c.dbUpdatePublicKey(string(msg.FromNode), msg.Data)
|
|
|
|
|
|
|
|
if ok {
|
2022-04-05 10:12:49 +00:00
|
|
|
er := fmt.Errorf("info: updated with new public key for node: %v", msg.FromNode)
|
2022-04-05 08:35:59 +00:00
|
|
|
fmt.Printf(" * %v\n", er)
|
|
|
|
c.errorKernel.infoSend(proc, msg, er)
|
|
|
|
}
|
|
|
|
if !ok {
|
2022-04-05 10:12:49 +00:00
|
|
|
er := fmt.Errorf("info: added public key for new node: %v", msg.FromNode)
|
2022-04-05 08:35:59 +00:00
|
|
|
fmt.Printf(" * %v\n", er)
|
|
|
|
c.errorKernel.infoSend(proc, msg, er)
|
|
|
|
}
|
|
|
|
|
|
|
|
//c.dbDump(c.bucketPublicKeys)
|
|
|
|
}
|
|
|
|
|
2022-04-20 04:26:01 +00:00
|
|
|
// dbGetPublicKey will look up and return a specific value if it exists for a key in a bucket in a DB.
|
2022-04-05 08:35:59 +00:00
|
|
|
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.
|
2022-04-07 12:18:28 +00:00
|
|
|
bu := tx.Bucket([]byte(c.bucketNamePublicKeys))
|
2022-04-05 08:35:59 +00:00
|
|
|
if bu == nil {
|
2022-04-07 12:18:28 +00:00
|
|
|
log.Printf("info: no db bucket exist: %v\n", c.bucketNamePublicKeys)
|
2022-04-05 08:35:59 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbUpdatePublicKey will update the public key for a node in the db.
|
|
|
|
func (c *centralAuth) dbUpdatePublicKey(node string, value []byte) error {
|
|
|
|
err := c.db.Update(func(tx *bolt.Tx) error {
|
|
|
|
//Create a bucket
|
2022-04-07 12:18:28 +00:00
|
|
|
bu, err := tx.CreateBucketIfNotExists([]byte(c.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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2022-04-07 12:18:28 +00:00
|
|
|
bu := tx.Bucket([]byte(c.bucketNamePublicKeys))
|
2022-04-05 08:35:59 +00:00
|
|
|
|
|
|
|
err := bu.Delete([]byte(key))
|
|
|
|
if err != nil {
|
2022-04-07 12:18:28 +00:00
|
|
|
log.Printf("error: delete key in bucket %v failed: %v\n", c.bucketNamePublicKeys, err)
|
2022-04-05 08:35:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// dumpBucket will dump out all they keys and values in the
|
|
|
|
// specified bucket, and return a sorted []samDBValue
|
2022-04-07 12:18:28 +00:00
|
|
|
func (c *centralAuth) dbDumpPublicKey() (map[Node][]byte, error) {
|
|
|
|
m := make(map[Node][]byte)
|
2022-04-05 08:35:59 +00:00
|
|
|
|
|
|
|
err := c.db.View(func(tx *bolt.Tx) error {
|
2022-04-07 12:18:28 +00:00
|
|
|
bu := tx.Bucket([]byte(c.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-05 08:35:59 +00:00
|
|
|
// nodePublicKeys holds all the gathered public keys of nodes in the system.
|
|
|
|
// The keys will be written to a k/v store for persistence.
|
2022-04-04 08:29:14 +00:00
|
|
|
type nodePublicKeys struct {
|
2022-04-09 04:46:47 +00:00
|
|
|
mu sync.RWMutex
|
2022-04-07 12:18:28 +00:00
|
|
|
KeyMap map[Node][]byte
|
2022-04-04 08:29:14 +00:00
|
|
|
}
|
|
|
|
|
2022-04-05 08:35:59 +00:00
|
|
|
// newNodePublicKeys will return a prepared type of nodePublicKeys.
|
|
|
|
func newNodePublicKeys(configuration *Configuration) *nodePublicKeys {
|
2022-04-04 08:29:14 +00:00
|
|
|
n := nodePublicKeys{
|
2022-04-07 12:18:28 +00:00
|
|
|
KeyMap: make(map[Node][]byte),
|
2022-04-04 08:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &n
|
|
|
|
}
|