github.com/influxdata/telegraf@v1.30.3/config/secret_test.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "log" 8 "testing" 9 10 "github.com/awnumar/memguard" 11 "github.com/stretchr/testify/require" 12 "github.com/stretchr/testify/suite" 13 14 "github.com/influxdata/telegraf" 15 "github.com/influxdata/telegraf/plugins/inputs" 16 "github.com/influxdata/telegraf/plugins/secretstores" 17 ) 18 19 func TestSecretConstantManually(t *testing.T) { 20 mysecret := "a wonderful test" 21 s := NewSecret([]byte(mysecret)) 22 defer s.Destroy() 23 retrieved, err := s.Get() 24 require.NoError(t, err) 25 defer retrieved.Destroy() 26 require.EqualValues(t, mysecret, retrieved.TemporaryString()) 27 } 28 29 func TestLinking(t *testing.T) { 30 mysecret := "a @{referenced:secret}" 31 resolvers := map[string]telegraf.ResolveFunc{ 32 "@{referenced:secret}": func() ([]byte, bool, error) { 33 return []byte("resolved secret"), false, nil 34 }, 35 } 36 s := NewSecret([]byte(mysecret)) 37 defer s.Destroy() 38 require.NoError(t, s.Link(resolvers)) 39 retrieved, err := s.Get() 40 require.NoError(t, err) 41 defer retrieved.Destroy() 42 require.EqualValues(t, "a resolved secret", retrieved.TemporaryString()) 43 } 44 45 func TestLinkingResolverError(t *testing.T) { 46 mysecret := "a @{referenced:secret}" 47 resolvers := map[string]telegraf.ResolveFunc{ 48 "@{referenced:secret}": func() ([]byte, bool, error) { 49 return nil, false, errors.New("broken") 50 }, 51 } 52 s := NewSecret([]byte(mysecret)) 53 defer s.Destroy() 54 expected := `linking secrets failed: resolving "@{referenced:secret}" failed: broken` 55 require.EqualError(t, s.Link(resolvers), expected) 56 } 57 58 func TestGettingUnlinked(t *testing.T) { 59 mysecret := "a @{referenced:secret}" 60 s := NewSecret([]byte(mysecret)) 61 defer s.Destroy() 62 _, err := s.Get() 63 require.ErrorContains(t, err, "unlinked parts in secret") 64 } 65 66 func TestGettingMissingResolver(t *testing.T) { 67 mysecret := "a @{referenced:secret}" 68 s := NewSecret([]byte(mysecret)) 69 defer s.Destroy() 70 s.unlinked = []string{} 71 s.resolvers = map[string]telegraf.ResolveFunc{ 72 "@{a:dummy}": func() ([]byte, bool, error) { 73 return nil, false, nil 74 }, 75 } 76 _, err := s.Get() 77 expected := `replacing secrets failed: no resolver for "@{referenced:secret}"` 78 require.EqualError(t, err, expected) 79 } 80 81 func TestGettingResolverError(t *testing.T) { 82 mysecret := "a @{referenced:secret}" 83 s := NewSecret([]byte(mysecret)) 84 defer s.Destroy() 85 s.unlinked = []string{} 86 s.resolvers = map[string]telegraf.ResolveFunc{ 87 "@{referenced:secret}": func() ([]byte, bool, error) { 88 return nil, false, errors.New("broken") 89 }, 90 } 91 _, err := s.Get() 92 expected := `replacing secrets failed: resolving "@{referenced:secret}" failed: broken` 93 require.EqualError(t, err, expected) 94 } 95 96 func TestUninitializedEnclave(t *testing.T) { 97 s := Secret{} 98 defer s.Destroy() 99 require.NoError(t, s.Link(map[string]telegraf.ResolveFunc{})) 100 retrieved, err := s.Get() 101 require.NoError(t, err) 102 defer retrieved.Destroy() 103 require.Empty(t, retrieved.Bytes()) 104 } 105 106 func TestEnclaveOpenError(t *testing.T) { 107 mysecret := "a @{referenced:secret}" 108 s := NewSecret([]byte(mysecret)) 109 defer s.Destroy() 110 memguard.Purge() 111 err := s.Link(map[string]telegraf.ResolveFunc{}) 112 require.ErrorContains(t, err, "opening enclave failed") 113 114 s.unlinked = []string{} 115 _, err = s.Get() 116 require.ErrorContains(t, err, "opening enclave failed") 117 } 118 119 func TestMissingResolver(t *testing.T) { 120 mysecret := "a @{referenced:secret}" 121 s := NewSecret([]byte(mysecret)) 122 defer s.Destroy() 123 err := s.Link(map[string]telegraf.ResolveFunc{}) 124 require.ErrorContains(t, err, "linking secrets failed: unlinked part") 125 } 126 127 func TestSecretConstant(t *testing.T) { 128 tests := []struct { 129 name string 130 cfg []byte 131 expected string 132 }{ 133 { 134 name: "simple string", 135 cfg: []byte(` 136 [[inputs.mockup]] 137 secret = "a secret" 138 `), 139 expected: "a secret", 140 }, 141 { 142 name: "mail address", 143 cfg: []byte(` 144 [[inputs.mockup]] 145 secret = "someone@mock.org" 146 `), 147 expected: "someone@mock.org", 148 }, 149 } 150 151 for _, tt := range tests { 152 t.Run(tt.name, func(t *testing.T) { 153 c := NewConfig() 154 require.NoError(t, c.LoadConfigData(tt.cfg)) 155 require.Len(t, c.Inputs, 1) 156 157 // Create a mockup secretstore 158 store := &MockupSecretStore{ 159 Secrets: map[string][]byte{"mock": []byte("fail")}, 160 } 161 require.NoError(t, store.Init()) 162 c.SecretStores["mock"] = store 163 require.NoError(t, c.LinkSecrets()) 164 165 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 166 secret, err := plugin.Secret.Get() 167 require.NoError(t, err) 168 defer secret.Destroy() 169 170 require.EqualValues(t, tt.expected, secret.TemporaryString()) 171 }) 172 } 173 } 174 175 func TestSecretUnquote(t *testing.T) { 176 tests := []struct { 177 name string 178 cfg []byte 179 }{ 180 { 181 name: "single quotes", 182 cfg: []byte(` 183 [[inputs.mockup]] 184 secret = 'a secret' 185 expected = 'a secret' 186 `), 187 }, 188 { 189 name: "double quotes", 190 cfg: []byte(` 191 [[inputs.mockup]] 192 secret = "a secret" 193 expected = "a secret" 194 `), 195 }, 196 { 197 name: "triple single quotes", 198 cfg: []byte(` 199 [[inputs.mockup]] 200 secret = '''a secret''' 201 expected = '''a secret''' 202 `), 203 }, 204 { 205 name: "triple double quotes", 206 cfg: []byte(` 207 [[inputs.mockup]] 208 secret = """a secret""" 209 expected = """a secret""" 210 `), 211 }, 212 { 213 name: "escaped double quotes", 214 cfg: []byte(` 215 [[inputs.mockup]] 216 secret = "\"a secret\"" 217 expected = "\"a secret\"" 218 `), 219 }, 220 { 221 name: "mix double-single quotes (single)", 222 cfg: []byte(` 223 [[inputs.mockup]] 224 secret = "'a secret'" 225 expected = "'a secret'" 226 `), 227 }, 228 { 229 name: "mix single-double quotes (single)", 230 cfg: []byte(` 231 [[inputs.mockup]] 232 secret = '"a secret"' 233 expected = '"a secret"' 234 `), 235 }, 236 { 237 name: "mix double-single quotes (triple-single)", 238 cfg: []byte(` 239 [[inputs.mockup]] 240 secret = """'a secret'""" 241 expected = """'a secret'""" 242 `), 243 }, 244 { 245 name: "mix single-double quotes (triple-single)", 246 cfg: []byte(` 247 [[inputs.mockup]] 248 secret = '''"a secret"''' 249 expected = '''"a secret"''' 250 `), 251 }, 252 { 253 name: "mix double-single quotes (triple)", 254 cfg: []byte(` 255 [[inputs.mockup]] 256 secret = """'''a secret'''""" 257 expected = """'''a secret'''""" 258 `), 259 }, 260 { 261 name: "mix single-double quotes (triple)", 262 cfg: []byte(` 263 [[inputs.mockup]] 264 secret = '''"""a secret"""''' 265 expected = '''"""a secret"""''' 266 `), 267 }, 268 { 269 name: "single quotes with backslashes", 270 cfg: []byte(` 271 [[inputs.mockup]] 272 secret = 'Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;' 273 expected = 'Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;' 274 `), 275 }, 276 { 277 name: "double quotes with backslashes", 278 cfg: []byte(` 279 [[inputs.mockup]] 280 secret = "Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;" 281 expected = "Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;" 282 `), 283 }, 284 { 285 name: "triple single quotes with backslashes", 286 cfg: []byte(` 287 [[inputs.mockup]] 288 secret = '''Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;''' 289 expected = '''Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;''' 290 `), 291 }, 292 { 293 name: "triple double quotes with backslashes", 294 cfg: []byte(` 295 [[inputs.mockup]] 296 secret = """Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;""" 297 expected = """Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;""" 298 `), 299 }, 300 } 301 302 for _, tt := range tests { 303 t.Run(tt.name, func(t *testing.T) { 304 c := NewConfig() 305 require.NoError(t, c.LoadConfigData(tt.cfg)) 306 require.Len(t, c.Inputs, 1) 307 308 // Create a mockup secretstore 309 store := &MockupSecretStore{ 310 Secrets: map[string][]byte{}, 311 } 312 require.NoError(t, store.Init()) 313 c.SecretStores["mock"] = store 314 require.NoError(t, c.LinkSecrets()) 315 316 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 317 secret, err := plugin.Secret.Get() 318 require.NoError(t, err) 319 defer secret.Destroy() 320 321 require.EqualValues(t, plugin.Expected, secret.TemporaryString()) 322 }) 323 } 324 } 325 326 func TestSecretEnvironmentVariable(t *testing.T) { 327 cfg := []byte(` 328 [[inputs.mockup]] 329 secret = "$SOME_ENV_SECRET" 330 `) 331 t.Setenv("SOME_ENV_SECRET", "an env secret") 332 333 c := NewConfig() 334 err := c.LoadConfigData(cfg) 335 require.NoError(t, err) 336 require.Len(t, c.Inputs, 1) 337 338 // Create a mockup secretstore 339 store := &MockupSecretStore{ 340 Secrets: map[string][]byte{}, 341 } 342 require.NoError(t, store.Init()) 343 c.SecretStores["mock"] = store 344 require.NoError(t, c.LinkSecrets()) 345 346 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 347 secret, err := plugin.Secret.Get() 348 require.NoError(t, err) 349 defer secret.Destroy() 350 351 require.EqualValues(t, "an env secret", secret.TemporaryString()) 352 } 353 354 func TestSecretCount(t *testing.T) { 355 secretCount.Store(0) 356 cfg := []byte(` 357 [[inputs.mockup]] 358 359 [[inputs.mockup]] 360 secret = "a secret" 361 362 [[inputs.mockup]] 363 secret = "another secret" 364 `) 365 366 c := NewConfig() 367 require.NoError(t, c.LoadConfigData(cfg)) 368 require.Len(t, c.Inputs, 3) 369 require.Equal(t, int64(2), secretCount.Load()) 370 371 // Remove all secrets and check 372 for _, ri := range c.Inputs { 373 input := ri.Input.(*MockupSecretPlugin) 374 input.Secret.Destroy() 375 } 376 require.Equal(t, int64(0), secretCount.Load()) 377 } 378 379 func TestSecretStoreStatic(t *testing.T) { 380 cfg := []byte( 381 ` 382 [[inputs.mockup]] 383 secret = "@{mock:secret1}" 384 [[inputs.mockup]] 385 secret = "@{mock:secret2}" 386 [[inputs.mockup]] 387 secret = "@{mock:a_strange_secret}" 388 [[inputs.mockup]] 389 secret = "@{mock:a_weird_secret}" 390 `) 391 392 c := NewConfig() 393 err := c.LoadConfigData(cfg) 394 require.NoError(t, err) 395 require.Len(t, c.Inputs, 4) 396 397 // Create a mockup secretstore 398 store := &MockupSecretStore{ 399 Secrets: map[string][]byte{ 400 "secret1": []byte("Ood Bnar"), 401 "secret2": []byte("Thon"), 402 "a_strange_secret": []byte("Obi-Wan Kenobi"), 403 "a_weird_secret": []byte("Arca Jeth"), 404 }, 405 } 406 require.NoError(t, store.Init()) 407 c.SecretStores["mock"] = store 408 require.NoError(t, c.LinkSecrets()) 409 410 expected := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"} 411 for i, input := range c.Inputs { 412 plugin := input.Input.(*MockupSecretPlugin) 413 secret, err := plugin.Secret.Get() 414 require.NoError(t, err) 415 require.EqualValues(t, expected[i], secret.TemporaryString()) 416 secret.Destroy() 417 } 418 } 419 420 func TestSecretStoreInvalidKeys(t *testing.T) { 421 cfg := []byte( 422 ` 423 [[inputs.mockup]] 424 secret = "@{mock:}" 425 [[inputs.mockup]] 426 secret = "@{mock:wild?%go}" 427 [[inputs.mockup]] 428 secret = "@{mock:a-strange-secret}" 429 [[inputs.mockup]] 430 secret = "@{mock:a weird secret}" 431 `) 432 433 c := NewConfig() 434 err := c.LoadConfigData(cfg) 435 require.NoError(t, err) 436 require.Len(t, c.Inputs, 4) 437 438 // Create a mockup secretstore 439 store := &MockupSecretStore{ 440 Secrets: map[string][]byte{ 441 "": []byte("Ood Bnar"), 442 "wild?%go": []byte("Thon"), 443 "a-strange-secret": []byte("Obi-Wan Kenobi"), 444 "a weird secret": []byte("Arca Jeth"), 445 }, 446 } 447 require.NoError(t, store.Init()) 448 c.SecretStores["mock"] = store 449 require.NoError(t, c.LinkSecrets()) 450 451 expected := []string{ 452 "@{mock:}", 453 "@{mock:wild?%go}", 454 "@{mock:a-strange-secret}", 455 "@{mock:a weird secret}", 456 } 457 for i, input := range c.Inputs { 458 plugin := input.Input.(*MockupSecretPlugin) 459 secret, err := plugin.Secret.Get() 460 require.NoError(t, err) 461 require.EqualValues(t, expected[i], secret.TemporaryString()) 462 secret.Destroy() 463 } 464 } 465 466 func TestSecretStoreDeclarationMissingID(t *testing.T) { 467 defer func() { unlinkedSecrets = make([]*Secret, 0) }() 468 469 cfg := []byte(`[[secretstores.mockup]]`) 470 471 c := NewConfig() 472 err := c.LoadConfigData(cfg) 473 require.ErrorContains(t, err, `error parsing mockup, "mockup" secret-store without ID`) 474 } 475 476 func TestSecretStoreDeclarationInvalidID(t *testing.T) { 477 defer func() { unlinkedSecrets = make([]*Secret, 0) }() 478 479 invalidIDs := []string{"foo.bar", "dummy-123", "test!", "wohoo+"} 480 tmpl := ` 481 [[secretstores.mockup]] 482 id = %q 483 ` 484 for _, id := range invalidIDs { 485 t.Run(id, func(t *testing.T) { 486 cfg := []byte(fmt.Sprintf(tmpl, id)) 487 c := NewConfig() 488 err := c.LoadConfigData(cfg) 489 require.ErrorContains(t, err, `error parsing mockup, invalid secret-store ID`) 490 }) 491 } 492 } 493 494 func TestSecretStoreDeclarationValidID(t *testing.T) { 495 defer func() { unlinkedSecrets = make([]*Secret, 0) }() 496 497 validIDs := []string{"foobar", "dummy123", "test_id", "W0Hoo_lala123"} 498 tmpl := ` 499 [[secretstores.mockup]] 500 id = %q 501 ` 502 for _, id := range validIDs { 503 t.Run(id, func(t *testing.T) { 504 cfg := []byte(fmt.Sprintf(tmpl, id)) 505 c := NewConfig() 506 err := c.LoadConfigData(cfg) 507 require.NoError(t, err) 508 }) 509 } 510 } 511 512 type SecretImplTestSuite struct { 513 suite.Suite 514 protected bool 515 } 516 517 func (tsuite *SecretImplTestSuite) SetupSuite() { 518 if tsuite.protected { 519 EnableSecretProtection() 520 } else { 521 DisableSecretProtection() 522 } 523 } 524 525 func (*SecretImplTestSuite) TearDownSuite() { 526 EnableSecretProtection() 527 } 528 529 func (*SecretImplTestSuite) TearDownTest() { 530 unlinkedSecrets = make([]*Secret, 0) 531 } 532 533 func (tsuite *SecretImplTestSuite) TestSecretEqualTo() { 534 t := tsuite.T() 535 mysecret := "a wonderful test" 536 s := NewSecret([]byte(mysecret)) 537 defer s.Destroy() 538 539 equal, err := s.EqualTo([]byte(mysecret)) 540 require.NoError(t, err) 541 require.True(t, equal) 542 543 equal, err = s.EqualTo([]byte("some random text")) 544 require.NoError(t, err) 545 require.False(t, equal) 546 } 547 548 func (tsuite *SecretImplTestSuite) TestSecretStoreInvalidReference() { 549 t := tsuite.T() 550 551 cfg := []byte( 552 ` 553 [[inputs.mockup]] 554 secret = "@{mock:test}" 555 `) 556 557 c := NewConfig() 558 require.NoError(t, c.LoadConfigData(cfg)) 559 require.Len(t, c.Inputs, 1) 560 561 // Create a mockup secretstore 562 store := &MockupSecretStore{ 563 Secrets: map[string][]byte{"test": []byte("Arca Jeth")}, 564 } 565 require.NoError(t, store.Init()) 566 c.SecretStores["foo"] = store 567 err := c.LinkSecrets() 568 require.EqualError(t, err, `unknown secret-store for "@{mock:test}"`) 569 570 for _, input := range c.Inputs { 571 plugin := input.Input.(*MockupSecretPlugin) 572 secret, err := plugin.Secret.Get() 573 require.EqualError(t, err, `unlinked parts in secret: @{mock:test}`) 574 require.Empty(t, secret) 575 } 576 } 577 578 func (tsuite *SecretImplTestSuite) TestSecretStoreStaticChanging() { 579 t := tsuite.T() 580 581 cfg := []byte( 582 ` 583 [[inputs.mockup]] 584 secret = "@{mock:secret}" 585 `) 586 587 c := NewConfig() 588 err := c.LoadConfigData(cfg) 589 require.NoError(t, err) 590 require.Len(t, c.Inputs, 1) 591 592 // Create a mockup secretstore 593 store := &MockupSecretStore{ 594 Secrets: map[string][]byte{"secret": []byte("Ood Bnar")}, 595 Dynamic: false, 596 } 597 require.NoError(t, store.Init()) 598 c.SecretStores["mock"] = store 599 require.NoError(t, c.LinkSecrets()) 600 601 sequence := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"} 602 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 603 secret, err := plugin.Secret.Get() 604 require.NoError(t, err) 605 defer secret.Destroy() 606 607 require.EqualValues(t, "Ood Bnar", secret.TemporaryString()) 608 609 for _, v := range sequence { 610 store.Secrets["secret"] = []byte(v) 611 secret, err := plugin.Secret.Get() 612 require.NoError(t, err) 613 614 // The secret should not change as the store is marked non-dyamic! 615 require.EqualValues(t, "Ood Bnar", secret.TemporaryString()) 616 secret.Destroy() 617 } 618 } 619 620 func (tsuite *SecretImplTestSuite) TestSecretStoreDynamic() { 621 t := tsuite.T() 622 623 cfg := []byte( 624 ` 625 [[inputs.mockup]] 626 secret = "@{mock:secret}" 627 `) 628 629 c := NewConfig() 630 err := c.LoadConfigData(cfg) 631 require.NoError(t, err) 632 require.Len(t, c.Inputs, 1) 633 634 // Create a mockup secretstore 635 store := &MockupSecretStore{ 636 Secrets: map[string][]byte{"secret": []byte("Ood Bnar")}, 637 Dynamic: true, 638 } 639 require.NoError(t, store.Init()) 640 c.SecretStores["mock"] = store 641 require.NoError(t, c.LinkSecrets()) 642 643 sequence := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"} 644 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 645 for _, v := range sequence { 646 store.Secrets["secret"] = []byte(v) 647 secret, err := plugin.Secret.Get() 648 require.NoError(t, err) 649 650 // The secret should not change as the store is marked non-dynamic! 651 require.EqualValues(t, v, secret.TemporaryString()) 652 secret.Destroy() 653 } 654 } 655 656 func (tsuite *SecretImplTestSuite) TestSecretSet() { 657 t := tsuite.T() 658 659 cfg := []byte(` 660 [[inputs.mockup]] 661 secret = "a secret" 662 `) 663 c := NewConfig() 664 require.NoError(t, c.LoadConfigData(cfg)) 665 require.Len(t, c.Inputs, 1) 666 require.NoError(t, c.LinkSecrets()) 667 668 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 669 670 secret, err := plugin.Secret.Get() 671 require.NoError(t, err) 672 defer secret.Destroy() 673 require.EqualValues(t, "a secret", secret.TemporaryString()) 674 675 require.NoError(t, plugin.Secret.Set([]byte("another secret"))) 676 newsecret, err := plugin.Secret.Get() 677 require.NoError(t, err) 678 defer newsecret.Destroy() 679 require.EqualValues(t, "another secret", newsecret.TemporaryString()) 680 } 681 682 func (tsuite *SecretImplTestSuite) TestSecretSetResolve() { 683 t := tsuite.T() 684 cfg := []byte(` 685 [[inputs.mockup]] 686 secret = "@{mock:secret}" 687 `) 688 c := NewConfig() 689 require.NoError(t, c.LoadConfigData(cfg)) 690 require.Len(t, c.Inputs, 1) 691 692 // Create a mockup secretstore 693 store := &MockupSecretStore{ 694 Secrets: map[string][]byte{"secret": []byte("Ood Bnar")}, 695 Dynamic: true, 696 } 697 require.NoError(t, store.Init()) 698 c.SecretStores["mock"] = store 699 require.NoError(t, c.LinkSecrets()) 700 701 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 702 703 secret, err := plugin.Secret.Get() 704 require.NoError(t, err) 705 defer secret.Destroy() 706 require.EqualValues(t, "Ood Bnar", secret.TemporaryString()) 707 708 require.NoError(t, plugin.Secret.Set([]byte("@{mock:secret} is cool"))) 709 newsecret, err := plugin.Secret.Get() 710 require.NoError(t, err) 711 defer newsecret.Destroy() 712 require.EqualValues(t, "Ood Bnar is cool", newsecret.TemporaryString()) 713 } 714 715 func (tsuite *SecretImplTestSuite) TestSecretSetResolveInvalid() { 716 t := tsuite.T() 717 718 cfg := []byte(` 719 [[inputs.mockup]] 720 secret = "@{mock:secret}" 721 `) 722 c := NewConfig() 723 require.NoError(t, c.LoadConfigData(cfg)) 724 require.Len(t, c.Inputs, 1) 725 726 // Create a mockup secretstore 727 store := &MockupSecretStore{ 728 Secrets: map[string][]byte{"secret": []byte("Ood Bnar")}, 729 Dynamic: true, 730 } 731 require.NoError(t, store.Init()) 732 c.SecretStores["mock"] = store 733 require.NoError(t, c.LinkSecrets()) 734 735 plugin := c.Inputs[0].Input.(*MockupSecretPlugin) 736 737 secret, err := plugin.Secret.Get() 738 require.NoError(t, err) 739 defer secret.Destroy() 740 require.EqualValues(t, "Ood Bnar", secret.TemporaryString()) 741 742 err = plugin.Secret.Set([]byte("@{mock:another_secret}")) 743 require.ErrorContains(t, err, `linking new secrets failed: unlinked part "@{mock:another_secret}"`) 744 } 745 746 func (tsuite *SecretImplTestSuite) TestSecretInvalidWarn() { 747 t := tsuite.T() 748 749 // Intercept the log output 750 var buf bytes.Buffer 751 backup := log.Writer() 752 log.SetOutput(&buf) 753 defer log.SetOutput(backup) 754 755 cfg := []byte(` 756 [[inputs.mockup]] 757 secret = "server=a user=@{mock:secret-with-invalid-chars} pass=@{mock:secret_pass}" 758 `) 759 c := NewConfig() 760 require.NoError(t, c.LoadConfigData(cfg)) 761 require.Len(t, c.Inputs, 1) 762 763 require.Contains(t, buf.String(), `W! Secret "@{mock:secret-with-invalid-chars}" contains invalid character(s)`) 764 require.NotContains(t, buf.String(), "@{mock:secret_pass}") 765 } 766 767 func TestSecretImplUnprotected(t *testing.T) { 768 impl := &unprotectedSecretImpl{} 769 container := impl.Container([]byte("foobar")) 770 require.NotNil(t, container) 771 c, ok := container.(*unprotectedSecretContainer) 772 require.True(t, ok) 773 require.Equal(t, "foobar", string(c.buf.content)) 774 buf, err := container.Buffer() 775 require.NoError(t, err) 776 require.NotNil(t, buf) 777 require.Equal(t, []byte("foobar"), buf.Bytes()) 778 require.Equal(t, "foobar", buf.TemporaryString()) 779 require.Equal(t, "foobar", buf.String()) 780 } 781 782 func TestSecretImplTestSuiteUnprotected(t *testing.T) { 783 suite.Run(t, &SecretImplTestSuite{protected: false}) 784 } 785 786 func TestSecretImplTestSuiteProtected(t *testing.T) { 787 suite.Run(t, &SecretImplTestSuite{protected: true}) 788 } 789 790 /*** Mockup (input) plugin for testing to avoid cyclic dependencies ***/ 791 type MockupSecretPlugin struct { 792 Secret Secret `toml:"secret"` 793 Expected string `toml:"expected"` 794 } 795 796 func (*MockupSecretPlugin) SampleConfig() string { return "Mockup test secret plugin" } 797 func (*MockupSecretPlugin) Gather(_ telegraf.Accumulator) error { return nil } 798 799 type MockupSecretStore struct { 800 Secrets map[string][]byte 801 Dynamic bool 802 } 803 804 func (s *MockupSecretStore) Init() error { 805 return nil 806 } 807 func (*MockupSecretStore) SampleConfig() string { 808 return "Mockup test secret plugin" 809 } 810 811 func (s *MockupSecretStore) Get(key string) ([]byte, error) { 812 v, found := s.Secrets[key] 813 if !found { 814 return nil, errors.New("not found") 815 } 816 return v, nil 817 } 818 819 func (s *MockupSecretStore) Set(key, value string) error { 820 s.Secrets[key] = []byte(value) 821 return nil 822 } 823 824 func (s *MockupSecretStore) List() ([]string, error) { 825 keys := make([]string, 0, len(s.Secrets)) 826 for k := range s.Secrets { 827 keys = append(keys, k) 828 } 829 return keys, nil 830 } 831 func (s *MockupSecretStore) GetResolver(key string) (telegraf.ResolveFunc, error) { 832 return func() ([]byte, bool, error) { 833 v, err := s.Get(key) 834 return v, s.Dynamic, err 835 }, nil 836 } 837 838 // Register the mockup plugin on loading 839 func init() { 840 // Register the mockup input plugin for the required names 841 inputs.Add("mockup", func() telegraf.Input { return &MockupSecretPlugin{} }) 842 secretstores.Add("mockup", func(string) telegraf.SecretStore { 843 return &MockupSecretStore{} 844 }) 845 }