1package vault 2 3import ( 4 "context" 5 "reflect" 6 "strings" 7 "testing" 8 "time" 9 10 metrics "github.com/armon/go-metrics" 11 "github.com/hashicorp/vault/helper/metricsutil" 12 "github.com/hashicorp/vault/helper/namespace" 13 "github.com/hashicorp/vault/sdk/helper/jsonutil" 14 "github.com/hashicorp/vault/sdk/logical" 15) 16 17func TestAuth_ReadOnlyViewDuringMount(t *testing.T) { 18 c, _, _ := TestCoreUnsealed(t) 19 c.credentialBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { 20 err := config.StorageView.Put(ctx, &logical.StorageEntry{ 21 Key: "bar", 22 Value: []byte("baz"), 23 }) 24 if err == nil || !strings.Contains(err.Error(), logical.ErrSetupReadOnly.Error()) { 25 t.Fatalf("expected a read-only error") 26 } 27 return &NoopBackend{ 28 BackendType: logical.TypeCredential, 29 }, nil 30 } 31 32 me := &MountEntry{ 33 Table: credentialTableType, 34 Path: "foo", 35 Type: "noop", 36 } 37 err := c.enableCredential(namespace.RootContext(nil), me) 38 if err != nil { 39 t.Fatalf("err: %v", err) 40 } 41} 42 43func TestAuthMountMetrics(t *testing.T) { 44 c, _, _, _ := TestCoreUnsealedWithMetrics(t) 45 c.credentialBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { 46 return &NoopBackend{ 47 BackendType: logical.TypeCredential, 48 }, nil 49 } 50 mountKeyName := "core.mount_table.num_entries.type|auth||local|false||" 51 mountMetrics := &c.metricsHelper.LoopMetrics.Metrics 52 loadMetric, ok := mountMetrics.Load(mountKeyName) 53 var numEntriesMetric metricsutil.GaugeMetric = loadMetric.(metricsutil.GaugeMetric) 54 55 // 1 default nonlocal auth backend 56 if !ok || numEntriesMetric.Value != 1 { 57 t.Fatalf("Auth values should be: %+v", numEntriesMetric) 58 } 59 60 me := &MountEntry{ 61 Table: credentialTableType, 62 Path: "foo", 63 Type: "noop", 64 } 65 err := c.enableCredential(namespace.RootContext(nil), me) 66 if err != nil { 67 t.Fatalf("err: %v", err) 68 } 69 mountMetrics = &c.metricsHelper.LoopMetrics.Metrics 70 loadMetric, ok = mountMetrics.Load(mountKeyName) 71 numEntriesMetric = loadMetric.(metricsutil.GaugeMetric) 72 if !ok || numEntriesMetric.Value != 2 { 73 t.Fatalf("mount metrics for num entries do not match true values") 74 } 75 if len(numEntriesMetric.Key) != 3 || 76 numEntriesMetric.Key[0] != "core" || 77 numEntriesMetric.Key[1] != "mount_table" || 78 numEntriesMetric.Key[2] != "num_entries" { 79 t.Fatalf("mount metrics for num entries have wrong key") 80 } 81 if len(numEntriesMetric.Labels) != 2 || 82 numEntriesMetric.Labels[0].Name != "type" || 83 numEntriesMetric.Labels[0].Value != "auth" || 84 numEntriesMetric.Labels[1].Name != "local" || 85 numEntriesMetric.Labels[1].Value != "false" { 86 t.Fatalf("mount metrics for num entries have wrong labels") 87 } 88 mountSizeKeyName := "core.mount_table.size.type|auth||local|false||" 89 loadMetric, ok = mountMetrics.Load(mountSizeKeyName) 90 sizeMetric := loadMetric.(metricsutil.GaugeMetric) 91 92 if !ok { 93 t.Fatalf("mount metrics for size do not match exist") 94 } 95 if len(sizeMetric.Key) != 3 || 96 sizeMetric.Key[0] != "core" || 97 sizeMetric.Key[1] != "mount_table" || 98 sizeMetric.Key[2] != "size" { 99 t.Fatalf("mount metrics for size have wrong key") 100 } 101 if len(sizeMetric.Labels) != 2 || 102 sizeMetric.Labels[0].Name != "type" || 103 sizeMetric.Labels[0].Value != "auth" || 104 sizeMetric.Labels[1].Name != "local" || 105 sizeMetric.Labels[1].Value != "false" { 106 t.Fatalf("mount metrics for size have wrong labels") 107 } 108} 109 110func TestCore_DefaultAuthTable(t *testing.T) { 111 c, keys, _ := TestCoreUnsealed(t) 112 verifyDefaultAuthTable(t, c.auth) 113 114 // Start a second core with same physical 115 inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour) 116 conf := &CoreConfig{ 117 Physical: c.physical, 118 DisableMlock: true, 119 BuiltinRegistry: NewMockBuiltinRegistry(), 120 MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink), 121 MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false), 122 } 123 c2, err := NewCore(conf) 124 if err != nil { 125 t.Fatalf("err: %v", err) 126 } 127 defer c2.Shutdown() 128 for i, key := range keys { 129 unseal, err := TestCoreUnseal(c2, key) 130 if err != nil { 131 t.Fatalf("err: %v", err) 132 } 133 if i+1 == len(keys) && !unseal { 134 t.Fatalf("should be unsealed") 135 } 136 } 137 138 // Verify matching mount tables 139 if !reflect.DeepEqual(c.auth, c2.auth) { 140 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 141 } 142} 143 144func TestCore_EnableCredential(t *testing.T) { 145 c, keys, _ := TestCoreUnsealed(t) 146 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 147 return &NoopBackend{ 148 BackendType: logical.TypeCredential, 149 }, nil 150 } 151 152 me := &MountEntry{ 153 Table: credentialTableType, 154 Path: "foo", 155 Type: "noop", 156 } 157 err := c.enableCredential(namespace.RootContext(nil), me) 158 if err != nil { 159 t.Fatalf("err: %v", err) 160 } 161 162 match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar") 163 if match != "auth/foo/" { 164 t.Fatalf("missing mount, match: %q", match) 165 } 166 167 inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour) 168 conf := &CoreConfig{ 169 Physical: c.physical, 170 DisableMlock: true, 171 BuiltinRegistry: NewMockBuiltinRegistry(), 172 MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink), 173 MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false), 174 } 175 c2, err := NewCore(conf) 176 if err != nil { 177 t.Fatalf("err: %v", err) 178 } 179 defer c2.Shutdown() 180 c2.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 181 return &NoopBackend{ 182 BackendType: logical.TypeCredential, 183 }, nil 184 } 185 for i, key := range keys { 186 unseal, err := TestCoreUnseal(c2, key) 187 if err != nil { 188 t.Fatalf("err: %v", err) 189 } 190 if i+1 == len(keys) && !unseal { 191 t.Fatalf("should be unsealed") 192 } 193 } 194 195 // Verify matching auth tables 196 if !reflect.DeepEqual(c.auth, c2.auth) { 197 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 198 } 199} 200 201// Test that the local table actually gets populated as expected with local 202// entries, and that upon reading the entries from both are recombined 203// correctly 204func TestCore_EnableCredential_Local(t *testing.T) { 205 c, _, _ := TestCoreUnsealed(t) 206 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 207 return &NoopBackend{ 208 BackendType: logical.TypeCredential, 209 }, nil 210 } 211 212 c.auth = &MountTable{ 213 Type: credentialTableType, 214 Entries: []*MountEntry{ 215 { 216 Table: credentialTableType, 217 Path: "noop/", 218 Type: "noop", 219 UUID: "abcd", 220 Accessor: "noop-abcd", 221 BackendAwareUUID: "abcde", 222 NamespaceID: namespace.RootNamespaceID, 223 namespace: namespace.RootNamespace, 224 }, 225 { 226 Table: credentialTableType, 227 Path: "noop2/", 228 Type: "noop", 229 UUID: "bcde", 230 Accessor: "noop-bcde", 231 BackendAwareUUID: "bcdea", 232 NamespaceID: namespace.RootNamespaceID, 233 namespace: namespace.RootNamespace, 234 }, 235 }, 236 } 237 238 // Both should set up successfully 239 err := c.setupCredentials(context.Background()) 240 if err != nil { 241 t.Fatal(err) 242 } 243 244 rawLocal, err := c.barrier.Get(context.Background(), coreLocalAuthConfigPath) 245 if err != nil { 246 t.Fatal(err) 247 } 248 if rawLocal == nil { 249 t.Fatal("expected non-nil local credential") 250 } 251 localCredentialTable := &MountTable{} 252 if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil { 253 t.Fatal(err) 254 } 255 if len(localCredentialTable.Entries) > 0 { 256 t.Fatalf("expected no entries in local credential table, got %#v", localCredentialTable) 257 } 258 259 c.auth.Entries[1].Local = true 260 if err := c.persistAuth(context.Background(), c.auth, nil); err != nil { 261 t.Fatal(err) 262 } 263 264 rawLocal, err = c.barrier.Get(context.Background(), coreLocalAuthConfigPath) 265 if err != nil { 266 t.Fatal(err) 267 } 268 if rawLocal == nil { 269 t.Fatal("expected non-nil local credential") 270 } 271 localCredentialTable = &MountTable{} 272 if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil { 273 t.Fatal(err) 274 } 275 if len(localCredentialTable.Entries) != 1 { 276 t.Fatalf("expected one entry in local credential table, got %#v", localCredentialTable) 277 } 278 279 oldCredential := c.auth 280 if err := c.loadCredentials(context.Background()); err != nil { 281 t.Fatal(err) 282 } 283 284 if !reflect.DeepEqual(oldCredential, c.auth) { 285 t.Fatalf("expected\n%#v\ngot\n%#v\n", oldCredential, c.auth) 286 } 287 288 if len(c.auth.Entries) != 2 { 289 t.Fatalf("expected two credential entries, got %#v", localCredentialTable) 290 } 291} 292 293func TestCore_EnableCredential_twice_409(t *testing.T) { 294 c, _, _ := TestCoreUnsealed(t) 295 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 296 return &NoopBackend{ 297 BackendType: logical.TypeCredential, 298 }, nil 299 } 300 301 me := &MountEntry{ 302 Table: credentialTableType, 303 Path: "foo", 304 Type: "noop", 305 } 306 err := c.enableCredential(namespace.RootContext(nil), me) 307 if err != nil { 308 t.Fatalf("err: %v", err) 309 } 310 311 // 2nd should be a 409 error 312 err2 := c.enableCredential(namespace.RootContext(nil), me) 313 switch err2.(type) { 314 case logical.HTTPCodedError: 315 if err2.(logical.HTTPCodedError).Code() != 409 { 316 t.Fatalf("invalid code given") 317 } 318 default: 319 t.Fatalf("expected a different error type") 320 } 321} 322 323func TestCore_EnableCredential_Token(t *testing.T) { 324 c, _, _ := TestCoreUnsealed(t) 325 me := &MountEntry{ 326 Table: credentialTableType, 327 Path: "foo", 328 Type: "token", 329 } 330 err := c.enableCredential(namespace.RootContext(nil), me) 331 if err.Error() != "token credential backend cannot be instantiated" { 332 t.Fatalf("err: %v", err) 333 } 334} 335 336func TestCore_DisableCredential(t *testing.T) { 337 c, keys, _ := TestCoreUnsealed(t) 338 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 339 return &NoopBackend{ 340 BackendType: logical.TypeCredential, 341 }, nil 342 } 343 344 err := c.disableCredential(namespace.RootContext(nil), "foo") 345 if err != nil && !strings.HasPrefix(err.Error(), "no matching mount") { 346 t.Fatal(err) 347 } 348 349 me := &MountEntry{ 350 Table: credentialTableType, 351 Path: "foo", 352 Type: "noop", 353 } 354 err = c.enableCredential(namespace.RootContext(nil), me) 355 if err != nil { 356 t.Fatalf("err: %v", err) 357 } 358 359 err = c.disableCredential(namespace.RootContext(nil), "foo") 360 if err != nil { 361 t.Fatalf("err: %v", err) 362 } 363 364 match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar") 365 if match != "" { 366 t.Fatalf("backend present") 367 } 368 369 inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour) 370 conf := &CoreConfig{ 371 Physical: c.physical, 372 DisableMlock: true, 373 BuiltinRegistry: NewMockBuiltinRegistry(), 374 MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink), 375 MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false), 376 } 377 c2, err := NewCore(conf) 378 if err != nil { 379 t.Fatalf("err: %v", err) 380 } 381 defer c2.Shutdown() 382 for i, key := range keys { 383 unseal, err := TestCoreUnseal(c2, key) 384 if err != nil { 385 t.Fatalf("err: %v", err) 386 } 387 if i+1 == len(keys) && !unseal { 388 t.Fatalf("should be unsealed") 389 } 390 } 391 392 // Verify matching mount tables 393 if !reflect.DeepEqual(c.auth, c2.auth) { 394 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 395 } 396} 397 398func TestCore_DisableCredential_Protected(t *testing.T) { 399 c, _, _ := TestCoreUnsealed(t) 400 err := c.disableCredential(namespace.RootContext(nil), "token") 401 if err.Error() != "token credential backend cannot be disabled" { 402 t.Fatalf("err: %v", err) 403 } 404} 405 406func TestCore_DisableCredential_Cleanup(t *testing.T) { 407 noop := &NoopBackend{ 408 Login: []string{"login"}, 409 BackendType: logical.TypeCredential, 410 } 411 c, _, _ := TestCoreUnsealed(t) 412 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 413 return noop, nil 414 } 415 416 me := &MountEntry{ 417 Table: credentialTableType, 418 Path: "foo", 419 Type: "noop", 420 } 421 err := c.enableCredential(namespace.RootContext(nil), me) 422 if err != nil { 423 t.Fatalf("err: %v", err) 424 } 425 426 // Store the view 427 view := c.router.MatchingStorageByAPIPath(namespace.RootContext(nil), "auth/foo/") 428 429 // Inject data 430 se := &logical.StorageEntry{ 431 Key: "plstodelete", 432 Value: []byte("test"), 433 } 434 if err := view.Put(context.Background(), se); err != nil { 435 t.Fatalf("err: %v", err) 436 } 437 438 // Generate a new token auth 439 noop.Response = &logical.Response{ 440 Auth: &logical.Auth{ 441 Policies: []string{"foo"}, 442 }, 443 } 444 r := &logical.Request{ 445 Operation: logical.ReadOperation, 446 Path: "auth/foo/login", 447 } 448 resp, err := c.HandleRequest(namespace.RootContext(nil), r) 449 if err != nil { 450 t.Fatalf("err: %v", err) 451 } 452 if resp.Auth.ClientToken == "" { 453 t.Fatalf("bad: %#v", resp) 454 } 455 456 // Disable should cleanup 457 err = c.disableCredential(namespace.RootContext(nil), "foo") 458 if err != nil { 459 t.Fatalf("err: %v", err) 460 } 461 462 // Token should be revoked 463 te, err := c.tokenStore.Lookup(namespace.RootContext(nil), resp.Auth.ClientToken) 464 if err != nil { 465 t.Fatalf("err: %v", err) 466 } 467 if te != nil { 468 t.Fatalf("bad: %#v", te) 469 } 470 471 // View should be empty 472 out, err := logical.CollectKeys(context.Background(), view) 473 if err != nil { 474 t.Fatalf("err: %v", err) 475 } 476 if len(out) != 0 { 477 t.Fatalf("bad: %#v", out) 478 } 479} 480 481func TestDefaultAuthTable(t *testing.T) { 482 c, _, _ := TestCoreUnsealed(t) 483 table := c.defaultAuthTable() 484 verifyDefaultAuthTable(t, table) 485} 486 487func verifyDefaultAuthTable(t *testing.T, table *MountTable) { 488 if len(table.Entries) != 1 { 489 t.Fatalf("bad: %v", table.Entries) 490 } 491 if table.Type != credentialTableType { 492 t.Fatalf("bad: %v", *table) 493 } 494 for idx, entry := range table.Entries { 495 switch idx { 496 case 0: 497 if entry.Path != "token/" { 498 t.Fatalf("bad: %v", entry) 499 } 500 if entry.Type != "token" { 501 t.Fatalf("bad: %v", entry) 502 } 503 } 504 if entry.Description == "" { 505 t.Fatalf("bad: %v", entry) 506 } 507 if entry.UUID == "" { 508 t.Fatalf("bad: %v", entry) 509 } 510 } 511} 512 513func TestCore_CredentialInitialize(t *testing.T) { 514 { 515 backend := &InitializableBackend{ 516 &NoopBackend{ 517 BackendType: logical.TypeCredential, 518 }, false, 519 } 520 521 c, _, _ := TestCoreUnsealed(t) 522 c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 523 return backend, nil 524 } 525 526 me := &MountEntry{ 527 Table: credentialTableType, 528 Path: "foo/", 529 Type: "initable", 530 } 531 err := c.enableCredential(namespace.RootContext(nil), me) 532 if err != nil { 533 t.Fatalf("err: %v", err) 534 } 535 536 if !backend.isInitialized { 537 t.Fatal("backend is not initialized") 538 } 539 } 540 { 541 backend := &InitializableBackend{ 542 &NoopBackend{ 543 BackendType: logical.TypeCredential, 544 }, false, 545 } 546 547 c, _, _ := TestCoreUnsealed(t) 548 c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 549 return backend, nil 550 } 551 552 c.auth = &MountTable{ 553 Type: credentialTableType, 554 Entries: []*MountEntry{ 555 { 556 Table: credentialTableType, 557 Path: "foo/", 558 Type: "initable", 559 UUID: "abcd", 560 Accessor: "initable-abcd", 561 BackendAwareUUID: "abcde", 562 NamespaceID: namespace.RootNamespaceID, 563 namespace: namespace.RootNamespace, 564 }, 565 }, 566 } 567 568 err := c.setupCredentials(context.Background()) 569 if err != nil { 570 t.Fatal(err) 571 } 572 573 // run the postUnseal funcs, so that the backend will be inited 574 for _, f := range c.postUnsealFuncs { 575 f() 576 } 577 578 if !backend.isInitialized { 579 t.Fatal("backend is not initialized") 580 } 581 } 582} 583