github.com/clly/consul@v1.4.5/agent/consul/state/connect_ca.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/consul/agent/structs"
     7  	"github.com/hashicorp/go-memdb"
     8  )
     9  
    10  const (
    11  	caBuiltinProviderTableName = "connect-ca-builtin"
    12  	caConfigTableName          = "connect-ca-config"
    13  	caRootTableName            = "connect-ca-roots"
    14  	caLeafIndexName            = "connect-ca-leaf-certs"
    15  )
    16  
    17  // caBuiltinProviderTableSchema returns a new table schema used for storing
    18  // the built-in CA provider's state for connect. This is only used by
    19  // the internal Consul CA provider.
    20  func caBuiltinProviderTableSchema() *memdb.TableSchema {
    21  	return &memdb.TableSchema{
    22  		Name: caBuiltinProviderTableName,
    23  		Indexes: map[string]*memdb.IndexSchema{
    24  			"id": &memdb.IndexSchema{
    25  				Name:         "id",
    26  				AllowMissing: false,
    27  				Unique:       true,
    28  				Indexer: &memdb.StringFieldIndex{
    29  					Field: "ID",
    30  				},
    31  			},
    32  		},
    33  	}
    34  }
    35  
    36  // caConfigTableSchema returns a new table schema used for storing
    37  // the CA config for Connect.
    38  func caConfigTableSchema() *memdb.TableSchema {
    39  	return &memdb.TableSchema{
    40  		Name: caConfigTableName,
    41  		Indexes: map[string]*memdb.IndexSchema{
    42  			// This table only stores one row, so this just ignores the ID field
    43  			// and always overwrites the same config object.
    44  			"id": &memdb.IndexSchema{
    45  				Name:         "id",
    46  				AllowMissing: true,
    47  				Unique:       true,
    48  				Indexer: &memdb.ConditionalIndex{
    49  					Conditional: func(obj interface{}) (bool, error) { return true, nil },
    50  				},
    51  			},
    52  		},
    53  	}
    54  }
    55  
    56  // caRootTableSchema returns a new table schema used for storing
    57  // CA roots for Connect.
    58  func caRootTableSchema() *memdb.TableSchema {
    59  	return &memdb.TableSchema{
    60  		Name: caRootTableName,
    61  		Indexes: map[string]*memdb.IndexSchema{
    62  			"id": &memdb.IndexSchema{
    63  				Name:         "id",
    64  				AllowMissing: false,
    65  				Unique:       true,
    66  				Indexer: &memdb.StringFieldIndex{
    67  					Field: "ID",
    68  				},
    69  			},
    70  		},
    71  	}
    72  }
    73  
    74  func init() {
    75  	registerSchema(caBuiltinProviderTableSchema)
    76  	registerSchema(caConfigTableSchema)
    77  	registerSchema(caRootTableSchema)
    78  }
    79  
    80  // CAConfig is used to pull the CA config from the snapshot.
    81  func (s *Snapshot) CAConfig() (*structs.CAConfiguration, error) {
    82  	c, err := s.tx.First(caConfigTableName, "id")
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	config, ok := c.(*structs.CAConfiguration)
    88  	if !ok {
    89  		return nil, nil
    90  	}
    91  
    92  	return config, nil
    93  }
    94  
    95  // CAConfig is used when restoring from a snapshot.
    96  func (s *Restore) CAConfig(config *structs.CAConfiguration) error {
    97  	// Don't restore a blank CA config
    98  	// https://github.com/hashicorp/consul/issues/4954
    99  	if config.Provider == "" {
   100  		return nil
   101  	}
   102  
   103  	if err := s.tx.Insert(caConfigTableName, config); err != nil {
   104  		return fmt.Errorf("failed restoring CA config: %s", err)
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  // CAConfig is used to get the current CA configuration.
   111  func (s *Store) CAConfig() (uint64, *structs.CAConfiguration, error) {
   112  	tx := s.db.Txn(false)
   113  	defer tx.Abort()
   114  
   115  	// Get the CA config
   116  	c, err := tx.First(caConfigTableName, "id")
   117  	if err != nil {
   118  		return 0, nil, fmt.Errorf("failed CA config lookup: %s", err)
   119  	}
   120  
   121  	config, ok := c.(*structs.CAConfiguration)
   122  	if !ok {
   123  		return 0, nil, nil
   124  	}
   125  
   126  	return config.ModifyIndex, config, nil
   127  }
   128  
   129  // CASetConfig is used to set the current CA configuration.
   130  func (s *Store) CASetConfig(idx uint64, config *structs.CAConfiguration) error {
   131  	tx := s.db.Txn(true)
   132  	defer tx.Abort()
   133  
   134  	if err := s.caSetConfigTxn(idx, tx, config); err != nil {
   135  		return err
   136  	}
   137  
   138  	tx.Commit()
   139  	return nil
   140  }
   141  
   142  // CACheckAndSetConfig is used to try updating the CA configuration with a
   143  // given Raft index. If the CAS index specified is not equal to the last observed index
   144  // for the config, then the call is a noop,
   145  func (s *Store) CACheckAndSetConfig(idx, cidx uint64, config *structs.CAConfiguration) (bool, error) {
   146  	tx := s.db.Txn(true)
   147  	defer tx.Abort()
   148  
   149  	// Check for an existing config
   150  	existing, err := tx.First(caConfigTableName, "id")
   151  	if err != nil {
   152  		return false, fmt.Errorf("failed CA config lookup: %s", err)
   153  	}
   154  
   155  	// If the existing index does not match the provided CAS
   156  	// index arg, then we shouldn't update anything and can safely
   157  	// return early here.
   158  	e, ok := existing.(*structs.CAConfiguration)
   159  	if !ok || e.ModifyIndex != cidx {
   160  		return false, nil
   161  	}
   162  
   163  	if err := s.caSetConfigTxn(idx, tx, config); err != nil {
   164  		return false, err
   165  	}
   166  
   167  	tx.Commit()
   168  	return true, nil
   169  }
   170  
   171  func (s *Store) caSetConfigTxn(idx uint64, tx *memdb.Txn, config *structs.CAConfiguration) error {
   172  	// Check for an existing config
   173  	prev, err := tx.First(caConfigTableName, "id")
   174  	if err != nil {
   175  		return fmt.Errorf("failed CA config lookup: %s", err)
   176  	}
   177  	// Set the indexes, prevent the cluster ID from changing.
   178  	if prev != nil {
   179  		existing := prev.(*structs.CAConfiguration)
   180  		config.CreateIndex = existing.CreateIndex
   181  		// Allow the ClusterID to change if it's provided by an internal operation, such
   182  		// as a primary datacenter being switched to secondary mode.
   183  		if config.ClusterID == "" {
   184  			config.ClusterID = existing.ClusterID
   185  		}
   186  	} else {
   187  		config.CreateIndex = idx
   188  	}
   189  	config.ModifyIndex = idx
   190  
   191  	if err := tx.Insert(caConfigTableName, config); err != nil {
   192  		return fmt.Errorf("failed updating CA config: %s", err)
   193  	}
   194  	return nil
   195  }
   196  
   197  // CARoots is used to pull all the CA roots for the snapshot.
   198  func (s *Snapshot) CARoots() (structs.CARoots, error) {
   199  	ixns, err := s.tx.Get(caRootTableName, "id")
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	var ret structs.CARoots
   205  	for wrapped := ixns.Next(); wrapped != nil; wrapped = ixns.Next() {
   206  		ret = append(ret, wrapped.(*structs.CARoot))
   207  	}
   208  
   209  	return ret, nil
   210  }
   211  
   212  // CARoots is used when restoring from a snapshot.
   213  func (s *Restore) CARoot(r *structs.CARoot) error {
   214  	// Insert
   215  	if err := s.tx.Insert(caRootTableName, r); err != nil {
   216  		return fmt.Errorf("failed restoring CA root: %s", err)
   217  	}
   218  	if err := indexUpdateMaxTxn(s.tx, r.ModifyIndex, caRootTableName); err != nil {
   219  		return fmt.Errorf("failed updating index: %s", err)
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  // CARoots returns the list of all CA roots.
   226  func (s *Store) CARoots(ws memdb.WatchSet) (uint64, structs.CARoots, error) {
   227  	tx := s.db.Txn(false)
   228  	defer tx.Abort()
   229  
   230  	// Get the index
   231  	idx := maxIndexTxn(tx, caRootTableName)
   232  
   233  	// Get all
   234  	iter, err := tx.Get(caRootTableName, "id")
   235  	if err != nil {
   236  		return 0, nil, fmt.Errorf("failed CA root lookup: %s", err)
   237  	}
   238  	ws.Add(iter.WatchCh())
   239  
   240  	var results structs.CARoots
   241  	for v := iter.Next(); v != nil; v = iter.Next() {
   242  		results = append(results, v.(*structs.CARoot))
   243  	}
   244  	return idx, results, nil
   245  }
   246  
   247  // CARootActive returns the currently active CARoot.
   248  func (s *Store) CARootActive(ws memdb.WatchSet) (uint64, *structs.CARoot, error) {
   249  	// Get all the roots since there should never be that many and just
   250  	// do the filtering in this method.
   251  	var result *structs.CARoot
   252  	idx, roots, err := s.CARoots(ws)
   253  	if err == nil {
   254  		for _, r := range roots {
   255  			if r.Active {
   256  				result = r
   257  				break
   258  			}
   259  		}
   260  	}
   261  
   262  	return idx, result, err
   263  }
   264  
   265  // CARootSetCAS sets the current CA root state using a check-and-set operation.
   266  // On success, this will replace the previous set of CARoots completely with
   267  // the given set of roots.
   268  //
   269  // The first boolean result returns whether the transaction succeeded or not.
   270  func (s *Store) CARootSetCAS(idx, cidx uint64, rs []*structs.CARoot) (bool, error) {
   271  	tx := s.db.Txn(true)
   272  	defer tx.Abort()
   273  
   274  	// There must be exactly one active CA root.
   275  	activeCount := 0
   276  	for _, r := range rs {
   277  		if r.Active {
   278  			activeCount++
   279  		}
   280  	}
   281  	if activeCount != 1 {
   282  		return false, fmt.Errorf("there must be exactly one active CA")
   283  	}
   284  
   285  	// Get the current max index
   286  	if midx := maxIndexTxn(tx, caRootTableName); midx != cidx {
   287  		return false, nil
   288  	}
   289  
   290  	// Go through and find any existing matching CAs so we can preserve and
   291  	// update their Create/ModifyIndex values.
   292  	for _, r := range rs {
   293  		if r.ID == "" {
   294  			return false, ErrMissingCARootID
   295  		}
   296  
   297  		existing, err := tx.First(caRootTableName, "id", r.ID)
   298  		if err != nil {
   299  			return false, fmt.Errorf("failed CA root lookup: %s", err)
   300  		}
   301  
   302  		if existing != nil {
   303  			r.CreateIndex = existing.(*structs.CARoot).CreateIndex
   304  		} else {
   305  			r.CreateIndex = idx
   306  		}
   307  		r.ModifyIndex = idx
   308  	}
   309  
   310  	// Delete all
   311  	_, err := tx.DeleteAll(caRootTableName, "id")
   312  	if err != nil {
   313  		return false, err
   314  	}
   315  
   316  	// Insert all
   317  	for _, r := range rs {
   318  		if err := tx.Insert(caRootTableName, r); err != nil {
   319  			return false, err
   320  		}
   321  	}
   322  
   323  	// Update the index
   324  	if err := tx.Insert("index", &IndexEntry{caRootTableName, idx}); err != nil {
   325  		return false, fmt.Errorf("failed updating index: %s", err)
   326  	}
   327  
   328  	tx.Commit()
   329  	return true, nil
   330  }
   331  
   332  // CAProviderState is used to pull the built-in provider states from the snapshot.
   333  func (s *Snapshot) CAProviderState() ([]*structs.CAConsulProviderState, error) {
   334  	ixns, err := s.tx.Get(caBuiltinProviderTableName, "id")
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	var ret []*structs.CAConsulProviderState
   340  	for wrapped := ixns.Next(); wrapped != nil; wrapped = ixns.Next() {
   341  		ret = append(ret, wrapped.(*structs.CAConsulProviderState))
   342  	}
   343  
   344  	return ret, nil
   345  }
   346  
   347  // CAProviderState is used when restoring from a snapshot.
   348  func (s *Restore) CAProviderState(state *structs.CAConsulProviderState) error {
   349  	if err := s.tx.Insert(caBuiltinProviderTableName, state); err != nil {
   350  		return fmt.Errorf("failed restoring built-in CA state: %s", err)
   351  	}
   352  	if err := indexUpdateMaxTxn(s.tx, state.ModifyIndex, caBuiltinProviderTableName); err != nil {
   353  		return fmt.Errorf("failed updating index: %s", err)
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  // CAProviderState is used to get the Consul CA provider state for the given ID.
   360  func (s *Store) CAProviderState(id string) (uint64, *structs.CAConsulProviderState, error) {
   361  	tx := s.db.Txn(false)
   362  	defer tx.Abort()
   363  
   364  	// Get the index
   365  	idx := maxIndexTxn(tx, caBuiltinProviderTableName)
   366  
   367  	// Get the provider config
   368  	c, err := tx.First(caBuiltinProviderTableName, "id", id)
   369  	if err != nil {
   370  		return 0, nil, fmt.Errorf("failed built-in CA state lookup: %s", err)
   371  	}
   372  
   373  	state, ok := c.(*structs.CAConsulProviderState)
   374  	if !ok {
   375  		return 0, nil, nil
   376  	}
   377  
   378  	return idx, state, nil
   379  }
   380  
   381  // CASetProviderState is used to set the current built-in CA provider state.
   382  func (s *Store) CASetProviderState(idx uint64, state *structs.CAConsulProviderState) (bool, error) {
   383  	tx := s.db.Txn(true)
   384  	defer tx.Abort()
   385  
   386  	// Check for an existing config
   387  	existing, err := tx.First(caBuiltinProviderTableName, "id", state.ID)
   388  	if err != nil {
   389  		return false, fmt.Errorf("failed built-in CA state lookup: %s", err)
   390  	}
   391  
   392  	// Set the indexes.
   393  	if existing != nil {
   394  		state.CreateIndex = existing.(*structs.CAConsulProviderState).CreateIndex
   395  	} else {
   396  		state.CreateIndex = idx
   397  	}
   398  	state.ModifyIndex = idx
   399  
   400  	if err := tx.Insert(caBuiltinProviderTableName, state); err != nil {
   401  		return false, fmt.Errorf("failed updating built-in CA state: %s", err)
   402  	}
   403  
   404  	// Update the index
   405  	if err := tx.Insert("index", &IndexEntry{caBuiltinProviderTableName, idx}); err != nil {
   406  		return false, fmt.Errorf("failed updating index: %s", err)
   407  	}
   408  
   409  	tx.Commit()
   410  
   411  	return true, nil
   412  }
   413  
   414  // CADeleteProviderState is used to remove the built-in Consul CA provider
   415  // state for the given ID.
   416  func (s *Store) CADeleteProviderState(id string) error {
   417  	tx := s.db.Txn(true)
   418  	defer tx.Abort()
   419  
   420  	// Get the index
   421  	idx := maxIndexTxn(tx, caBuiltinProviderTableName)
   422  
   423  	// Check for an existing config
   424  	existing, err := tx.First(caBuiltinProviderTableName, "id", id)
   425  	if err != nil {
   426  		return fmt.Errorf("failed built-in CA state lookup: %s", err)
   427  	}
   428  	if existing == nil {
   429  		return nil
   430  	}
   431  
   432  	providerState := existing.(*structs.CAConsulProviderState)
   433  
   434  	// Do the delete and update the index
   435  	if err := tx.Delete(caBuiltinProviderTableName, providerState); err != nil {
   436  		return err
   437  	}
   438  	if err := tx.Insert("index", &IndexEntry{caBuiltinProviderTableName, idx}); err != nil {
   439  		return fmt.Errorf("failed updating index: %s", err)
   440  	}
   441  
   442  	tx.Commit()
   443  
   444  	return nil
   445  }
   446  
   447  func (s *Store) CALeafSetIndex(index uint64) error {
   448  	tx := s.db.Txn(true)
   449  	defer tx.Abort()
   450  
   451  	return indexUpdateMaxTxn(tx, index, caLeafIndexName)
   452  }