1package vault 2 3import ( 4 "context" 5 "crypto/subtle" 6 "encoding/json" 7 "fmt" 8 "sync/atomic" 9 10 proto "github.com/golang/protobuf/proto" 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/vault/physical" 13 "github.com/hashicorp/vault/vault/seal" 14) 15 16// barrierTypeUpgradeCheck checks for backwards compat on barrier type, not 17// applicable in the OSS side 18var barrierTypeUpgradeCheck = func(_ string, _ *SealConfig) {} 19 20// autoSeal is a Seal implementation that contains logic for encrypting and 21// decrypting stored keys via an underlying AutoSealAccess implementation, as 22// well as logic related to recovery keys and barrier config. 23type autoSeal struct { 24 seal.Access 25 26 barrierConfig atomic.Value 27 recoveryConfig atomic.Value 28 core *Core 29} 30 31// Ensure we are implementing the Seal interface 32var _ Seal = (*autoSeal)(nil) 33 34func NewAutoSeal(lowLevel seal.Access) Seal { 35 ret := &autoSeal{ 36 Access: lowLevel, 37 } 38 ret.barrierConfig.Store((*SealConfig)(nil)) 39 ret.recoveryConfig.Store((*SealConfig)(nil)) 40 return ret 41} 42 43func (d *autoSeal) checkCore() error { 44 if d.core == nil { 45 return fmt.Errorf("seal does not have a core set") 46 } 47 return nil 48} 49 50func (d *autoSeal) SetCore(core *Core) { 51 d.core = core 52} 53 54func (d *autoSeal) Init(ctx context.Context) error { 55 return d.Access.Init(ctx) 56} 57 58func (d *autoSeal) Finalize(ctx context.Context) error { 59 return d.Access.Finalize(ctx) 60} 61 62func (d *autoSeal) BarrierType() string { 63 return d.SealType() 64} 65 66func (d *autoSeal) StoredKeysSupported() bool { 67 return true 68} 69 70func (d *autoSeal) RecoveryKeySupported() bool { 71 return true 72} 73 74// SetStoredKeys uses the autoSeal.Access.Encrypts method to wrap the keys. The stored entry 75// does not need to be seal wrapped in this case. 76func (d *autoSeal) SetStoredKeys(ctx context.Context, keys [][]byte) error { 77 if keys == nil { 78 return fmt.Errorf("keys were nil") 79 } 80 if len(keys) == 0 { 81 return fmt.Errorf("no keys provided") 82 } 83 84 buf, err := json.Marshal(keys) 85 if err != nil { 86 return errwrap.Wrapf("failed to encode keys for storage: {{err}}", err) 87 } 88 89 // Encrypt and marshal the keys 90 blobInfo, err := d.Encrypt(ctx, buf) 91 if err != nil { 92 return errwrap.Wrapf("failed to encrypt keys for storage: {{err}}", err) 93 } 94 95 value, err := proto.Marshal(blobInfo) 96 if err != nil { 97 return errwrap.Wrapf("failed to marshal value for storage: {{err}}", err) 98 } 99 100 // Store the seal configuration. 101 pe := &physical.Entry{ 102 Key: StoredBarrierKeysPath, 103 Value: value, 104 } 105 106 if err := d.core.physical.Put(ctx, pe); err != nil { 107 return errwrap.Wrapf("failed to write keys to storage: {{err}}", err) 108 } 109 110 return nil 111} 112 113// GetStoredKeys retrieves the key shares by unwrapping the encrypted key using the 114// autoseal. 115func (d *autoSeal) GetStoredKeys(ctx context.Context) ([][]byte, error) { 116 pe, err := d.core.physical.Get(ctx, StoredBarrierKeysPath) 117 if err != nil { 118 return nil, errwrap.Wrapf("failed to fetch stored keys: {{err}}", err) 119 } 120 121 // This is not strictly an error; we may not have any stored keys, for 122 // instance, if we're not initialized 123 if pe == nil { 124 return nil, nil 125 } 126 127 blobInfo := &physical.EncryptedBlobInfo{} 128 if err := proto.Unmarshal(pe.Value, blobInfo); err != nil { 129 return nil, errwrap.Wrapf("failed to proto decode stored keys: {{err}}", err) 130 } 131 132 pt, err := d.Decrypt(ctx, blobInfo) 133 if err != nil { 134 return nil, errwrap.Wrapf("failed to decrypt encrypted stored keys: {{err}}", err) 135 } 136 137 // Decode the barrier entry 138 var keys [][]byte 139 if err := json.Unmarshal(pt, &keys); err != nil { 140 return nil, fmt.Errorf("failed to decode stored keys: %v", err) 141 } 142 143 return keys, nil 144} 145 146func (d *autoSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) { 147 if d.barrierConfig.Load().(*SealConfig) != nil { 148 return d.barrierConfig.Load().(*SealConfig).Clone(), nil 149 } 150 151 if err := d.checkCore(); err != nil { 152 return nil, err 153 } 154 155 sealType := "barrier" 156 157 entry, err := d.core.physical.Get(ctx, barrierSealConfigPath) 158 if err != nil { 159 d.core.logger.Error("autoseal: failed to read seal configuration", "seal_type", sealType, "error", err) 160 return nil, errwrap.Wrapf(fmt.Sprintf("failed to read %q seal configuration: {{err}}", sealType), err) 161 } 162 163 // If the seal configuration is missing, we are not initialized 164 if entry == nil { 165 if d.core.logger.IsInfo() { 166 d.core.logger.Info("autoseal: seal configuration missing, not initialized", "seal_type", sealType) 167 } 168 return nil, nil 169 } 170 171 conf := &SealConfig{} 172 err = json.Unmarshal(entry.Value, conf) 173 if err != nil { 174 d.core.logger.Error("autoseal: failed to decode seal configuration", "seal_type", sealType, "error", err) 175 return nil, errwrap.Wrapf(fmt.Sprintf("failed to decode %q seal configuration: {{err}}", sealType), err) 176 } 177 178 // Check for a valid seal configuration 179 if err := conf.Validate(); err != nil { 180 d.core.logger.Error("autoseal: invalid seal configuration", "seal_type", sealType, "error", err) 181 return nil, errwrap.Wrapf(fmt.Sprintf("%q seal validation failed: {{err}}", sealType), err) 182 } 183 184 barrierTypeUpgradeCheck(d.BarrierType(), conf) 185 186 if conf.Type != d.BarrierType() { 187 d.core.logger.Error("autoseal: barrier seal type does not match loaded type", "seal_type", conf.Type, "loaded_type", d.BarrierType()) 188 return nil, fmt.Errorf("barrier seal type of %q does not match loaded type of %q", conf.Type, d.BarrierType()) 189 } 190 191 d.barrierConfig.Store(conf) 192 return conf.Clone(), nil 193} 194 195func (d *autoSeal) SetBarrierConfig(ctx context.Context, conf *SealConfig) error { 196 if err := d.checkCore(); err != nil { 197 return err 198 } 199 200 if conf == nil { 201 d.barrierConfig.Store((*SealConfig)(nil)) 202 return nil 203 } 204 205 conf.Type = d.BarrierType() 206 207 // Encode the seal configuration 208 buf, err := json.Marshal(conf) 209 if err != nil { 210 return errwrap.Wrapf("failed to encode barrier seal configuration: {{err}}", err) 211 } 212 213 // Store the seal configuration 214 pe := &physical.Entry{ 215 Key: barrierSealConfigPath, 216 Value: buf, 217 } 218 219 if err := d.core.physical.Put(ctx, pe); err != nil { 220 d.core.logger.Error("autoseal: failed to write barrier seal configuration", "error", err) 221 return errwrap.Wrapf("failed to write barrier seal configuration: {{err}}", err) 222 } 223 224 d.barrierConfig.Store(conf.Clone()) 225 226 return nil 227} 228 229func (d *autoSeal) SetCachedBarrierConfig(config *SealConfig) { 230 d.barrierConfig.Store(config) 231} 232 233func (d *autoSeal) RecoveryType() string { 234 return RecoveryTypeShamir 235} 236 237// RecoveryConfig returns the recovery config on recoverySealConfigPlaintextPath. 238func (d *autoSeal) RecoveryConfig(ctx context.Context) (*SealConfig, error) { 239 if d.recoveryConfig.Load().(*SealConfig) != nil { 240 return d.recoveryConfig.Load().(*SealConfig).Clone(), nil 241 } 242 243 if err := d.checkCore(); err != nil { 244 return nil, err 245 } 246 247 sealType := "recovery" 248 249 var entry *physical.Entry 250 var err error 251 entry, err = d.core.physical.Get(ctx, recoverySealConfigPlaintextPath) 252 if err != nil { 253 d.core.logger.Error("autoseal: failed to read seal configuration", "seal_type", sealType, "error", err) 254 return nil, errwrap.Wrapf(fmt.Sprintf("failed to read %q seal configuration: {{err}}", sealType), err) 255 } 256 257 if entry == nil { 258 if d.core.Sealed() { 259 d.core.logger.Info("autoseal: seal configuration missing, but cannot check old path as core is sealed", "seal_type", sealType) 260 return nil, nil 261 } 262 263 // Check the old recovery seal config path so an upgraded standby will 264 // return the correct seal config 265 be, err := d.core.barrier.Get(ctx, recoverySealConfigPath) 266 if err != nil { 267 return nil, errwrap.Wrapf("failed to read old recovery seal configuration: {{err}}", err) 268 } 269 270 // If the seal configuration is missing, then we are not initialized. 271 if be == nil { 272 if d.core.logger.IsInfo() { 273 d.core.logger.Info("autoseal: seal configuration missing, not initialized", "seal_type", sealType) 274 } 275 return nil, nil 276 } 277 278 // Reconstruct the physical entry 279 entry = &physical.Entry{ 280 Key: be.Key, 281 Value: be.Value, 282 } 283 } 284 285 conf := &SealConfig{} 286 if err := json.Unmarshal(entry.Value, conf); err != nil { 287 d.core.logger.Error("autoseal: failed to decode seal configuration", "seal_type", sealType, "error", err) 288 return nil, errwrap.Wrapf(fmt.Sprintf("failed to decode %q seal configuration: {{err}}", sealType), err) 289 } 290 291 // Check for a valid seal configuration 292 if err := conf.Validate(); err != nil { 293 d.core.logger.Error("autoseal: invalid seal configuration", "seal_type", sealType, "error", err) 294 return nil, errwrap.Wrapf(fmt.Sprintf("%q seal validation failed: {{err}}", sealType), err) 295 } 296 297 if conf.Type != d.RecoveryType() { 298 d.core.logger.Error("autoseal: recovery seal type does not match loaded type", "seal_type", conf.Type, "loaded_type", d.RecoveryType()) 299 return nil, fmt.Errorf("recovery seal type of %q does not match loaded type of %q", conf.Type, d.RecoveryType()) 300 } 301 302 d.recoveryConfig.Store(conf) 303 return conf.Clone(), nil 304} 305 306func (d *autoSeal) RecoveryKey(ctx context.Context) ([]byte, error) { 307 pe, err := d.core.physical.Get(ctx, recoveryKeyPath) 308 if err != nil { 309 d.core.logger.Error("autoseal: failed to read recovery key", "error", err) 310 return nil, errwrap.Wrapf("failed to read recovery key: {{err}}", err) 311 } 312 if pe == nil { 313 d.core.logger.Warn("autoseal: no recovery key found") 314 return nil, fmt.Errorf("no recovery key found") 315 } 316 317 blobInfo := &physical.EncryptedBlobInfo{} 318 if err := proto.Unmarshal(pe.Value, blobInfo); err != nil { 319 return nil, errwrap.Wrapf("failed to proto decode recovery keys: {{err}}", err) 320 } 321 322 pt, err := d.Decrypt(ctx, blobInfo) 323 if err != nil { 324 return nil, errwrap.Wrapf("failed to decrypt encrypted recovery keys: {{err}}", err) 325 } 326 327 return pt, nil 328} 329 330// SetRecoveryConfig writes the recovery configuration to the physical storage 331// and sets it as the seal's recoveryConfig. 332func (d *autoSeal) SetRecoveryConfig(ctx context.Context, conf *SealConfig) error { 333 if err := d.checkCore(); err != nil { 334 return err 335 } 336 337 // Perform migration if applicable 338 if err := d.migrateRecoveryConfig(ctx); err != nil { 339 return err 340 } 341 342 if conf == nil { 343 d.recoveryConfig.Store((*SealConfig)(nil)) 344 return nil 345 } 346 347 conf.Type = d.RecoveryType() 348 349 // Encode the seal configuration 350 buf, err := json.Marshal(conf) 351 if err != nil { 352 return errwrap.Wrapf("failed to encode recovery seal configuration: {{err}}", err) 353 } 354 355 // Store the seal configuration directly in the physical storage 356 pe := &physical.Entry{ 357 Key: recoverySealConfigPlaintextPath, 358 Value: buf, 359 } 360 361 if err := d.core.physical.Put(ctx, pe); err != nil { 362 d.core.logger.Error("autoseal: failed to write recovery seal configuration", "error", err) 363 return errwrap.Wrapf("failed to write recovery seal configuration: {{err}}", err) 364 } 365 366 d.recoveryConfig.Store(conf.Clone()) 367 368 return nil 369} 370 371func (d *autoSeal) SetCachedRecoveryConfig(config *SealConfig) { 372 d.recoveryConfig.Store(config) 373} 374 375func (d *autoSeal) VerifyRecoveryKey(ctx context.Context, key []byte) error { 376 if key == nil { 377 return fmt.Errorf("recovery key to verify is nil") 378 } 379 380 pt, err := d.RecoveryKey(ctx) 381 if err != nil { 382 return err 383 } 384 385 if subtle.ConstantTimeCompare(key, pt) != 1 { 386 return fmt.Errorf("recovery key does not match submitted values") 387 } 388 389 return nil 390} 391 392func (d *autoSeal) SetRecoveryKey(ctx context.Context, key []byte) error { 393 if err := d.checkCore(); err != nil { 394 return err 395 } 396 397 if key == nil { 398 return fmt.Errorf("recovery key to store is nil") 399 } 400 401 // Encrypt and marshal the keys 402 blobInfo, err := d.Encrypt(ctx, key) 403 if err != nil { 404 return errwrap.Wrapf("failed to encrypt keys for storage: {{err}}", err) 405 } 406 407 value, err := proto.Marshal(blobInfo) 408 if err != nil { 409 return errwrap.Wrapf("failed to marshal value for storage: {{err}}", err) 410 } 411 412 be := &physical.Entry{ 413 Key: recoveryKeyPath, 414 Value: value, 415 } 416 417 if err := d.core.physical.Put(ctx, be); err != nil { 418 d.core.logger.Error("autoseal: failed to write recovery key", "error", err) 419 return errwrap.Wrapf("failed to write recovery key: {{err}}", err) 420 } 421 422 return nil 423} 424 425// migrateRecoveryConfig is a helper func to migrate the recovery config to 426// live outside the barrier. This is called from SetRecoveryConfig which is 427// always called with the stateLock. 428func (d *autoSeal) migrateRecoveryConfig(ctx context.Context) error { 429 // Get config from the old recoverySealConfigPath path 430 be, err := d.core.barrier.Get(ctx, recoverySealConfigPath) 431 if err != nil { 432 return errwrap.Wrapf("failed to read old recovery seal configuration during migration: {{err}}", err) 433 } 434 435 // If this entry is nil, then skip migration 436 if be == nil { 437 return nil 438 } 439 440 // Only log if we are performing the migration 441 d.core.logger.Debug("migrating recovery seal configuration") 442 defer d.core.logger.Debug("done migrating recovery seal configuration") 443 444 // Perform migration 445 pe := &physical.Entry{ 446 Key: recoverySealConfigPlaintextPath, 447 Value: be.Value, 448 } 449 450 if err := d.core.physical.Put(ctx, pe); err != nil { 451 return errwrap.Wrapf("failed to write recovery seal configuration during migration: {{err}}", err) 452 } 453 454 // Perform deletion of the old entry 455 if err := d.core.barrier.Delete(ctx, recoverySealConfigPath); err != nil { 456 return errwrap.Wrapf("failed to delete old recovery seal configuration during migration: {{err}}", err) 457 } 458 459 return nil 460} 461