From 130098b7b539c41da103d88ac1f6e079915f2814 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 16 Mar 2018 21:20:54 -0700 Subject: [PATCH] agent/consul/state: CARoot structs and initial state store --- agent/consul/state/connect_ca.go | 106 ++++++++++++++++++++++++++++++ agent/consul/state/state_store.go | 4 ++ agent/structs/connect_ca.go | 37 +++++++++++ 3 files changed, 147 insertions(+) create mode 100644 agent/consul/state/connect_ca.go create mode 100644 agent/structs/connect_ca.go diff --git a/agent/consul/state/connect_ca.go b/agent/consul/state/connect_ca.go new file mode 100644 index 0000000000..9e31959185 --- /dev/null +++ b/agent/consul/state/connect_ca.go @@ -0,0 +1,106 @@ +package state + +import ( + "fmt" + + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/go-memdb" +) + +const ( + caRootTableName = "connect-ca-roots" +) + +// caRootTableSchema returns a new table schema used for storing +// CA roots for Connect. +func caRootTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: caRootTableName, + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, + }, + } +} + +func init() { + registerSchema(caRootTableSchema) +} + +// CARoots returns the list of all CA roots. +func (s *Store) CARoots(ws memdb.WatchSet) (uint64, structs.CARoots, error) { + tx := s.db.Txn(false) + defer tx.Abort() + + // Get the index + idx := maxIndexTxn(tx, caRootTableName) + + // Get all + iter, err := tx.Get(caRootTableName, "id") + if err != nil { + return 0, nil, fmt.Errorf("failed CA root lookup: %s", err) + } + ws.Add(iter.WatchCh()) + + var results structs.CARoots + for v := iter.Next(); v != nil; v = iter.Next() { + results = append(results, v.(*structs.CARoot)) + } + return idx, results, nil +} + +// CARootSet creates or updates a CA root. +// +// NOTE(mitchellh): I have a feeling we'll want a CARootMultiSetCAS to +// perform a check-and-set on the entire set of CARoots versus an individual +// set, since we'll want to modify them atomically during events such as +// rotation. +func (s *Store) CARootSet(idx uint64, v *structs.CARoot) error { + tx := s.db.Txn(true) + defer tx.Abort() + + if err := s.caRootSetTxn(tx, idx, v); err != nil { + return err + } + + tx.Commit() + return nil +} + +// caRootSetTxn is the inner method used to insert or update a CA root with +// the proper indexes into the state store. +func (s *Store) caRootSetTxn(tx *memdb.Txn, idx uint64, v *structs.CARoot) error { + // ID is required + if v.ID == "" { + return ErrMissingCARootID + } + + // Check for an existing value + existing, err := tx.First(caRootTableName, "id", v.ID) + if err != nil { + return fmt.Errorf("failed CA root lookup: %s", err) + } + if existing != nil { + old := existing.(*structs.CARoot) + v.CreateIndex = old.CreateIndex + } else { + v.CreateIndex = idx + } + v.ModifyIndex = idx + + // Insert + if err := tx.Insert(caRootTableName, v); err != nil { + return err + } + if err := tx.Insert("index", &IndexEntry{caRootTableName, idx}); err != nil { + return fmt.Errorf("failed updating index: %s", err) + } + + return nil +} diff --git a/agent/consul/state/state_store.go b/agent/consul/state/state_store.go index 62b6a8bff9..c59e09e930 100644 --- a/agent/consul/state/state_store.go +++ b/agent/consul/state/state_store.go @@ -29,6 +29,10 @@ var ( // a Query with an empty ID. ErrMissingQueryID = errors.New("Missing Query ID") + // ErrMissingCARootID is returned when an CARoot set is called + // with an CARoot with an empty ID. + ErrMissingCARootID = errors.New("Missing CA Root ID") + // ErrMissingIntentionID is returned when an Intention set is called // with an Intention with an empty ID. ErrMissingIntentionID = errors.New("Missing Intention ID") diff --git a/agent/structs/connect_ca.go b/agent/structs/connect_ca.go new file mode 100644 index 0000000000..87211e09ff --- /dev/null +++ b/agent/structs/connect_ca.go @@ -0,0 +1,37 @@ +package structs + +// IndexedCARoots is the list of currently trusted CA Roots. +type IndexedCARoots struct { + // ActiveRootID is the ID of a root in Roots that is the active CA root. + // Other roots are still valid if they're in the Roots list but are in + // the process of being rotated out. + ActiveRootID string + + // Roots is a list of root CA certs to trust. + Roots []*CARoot + + QueryMeta +} + +// CARoot represents a root CA certificate that is trusted. +type CARoot struct { + // ID is a globally unique ID (UUID) representing this CA root. + ID string + + // Name is a human-friendly name for this CA root. This value is + // opaque to Consul and is not used for anything internally. + Name string + + // RootCert is the PEM-encoded public certificate. + RootCert string + + // SigningCert is the PEM-encoded signing certificate and SigningKey + // is the PEM-encoded private key for the signing certificate. + SigningCert string + SigningKey string + + RaftIndex +} + +// CARoots is a list of CARoot structures. +type CARoots []*CARoot