github.imxd.top/hashicorp/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 }