github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/intention_endpoint_test.go (about) 1 package consul 2 3 import ( 4 "os" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/consul/acl" 9 "github.com/hashicorp/consul/agent/structs" 10 "github.com/hashicorp/consul/testrpc" 11 "github.com/hashicorp/net-rpc-msgpackrpc" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 // Test basic creation 17 func TestIntentionApply_new(t *testing.T) { 18 t.Parallel() 19 20 assert := assert.New(t) 21 dir1, s1 := testServer(t) 22 defer os.RemoveAll(dir1) 23 defer s1.Shutdown() 24 codec := rpcClient(t, s1) 25 defer codec.Close() 26 27 testrpc.WaitForLeader(t, s1.RPC, "dc1") 28 29 // Setup a basic record to create 30 ixn := structs.IntentionRequest{ 31 Datacenter: "dc1", 32 Op: structs.IntentionOpCreate, 33 Intention: &structs.Intention{ 34 SourceNS: structs.IntentionDefaultNamespace, 35 SourceName: "test", 36 DestinationNS: structs.IntentionDefaultNamespace, 37 DestinationName: "test", 38 Action: structs.IntentionActionAllow, 39 SourceType: structs.IntentionSourceConsul, 40 Meta: map[string]string{}, 41 }, 42 } 43 var reply string 44 45 // Record now to check created at time 46 now := time.Now() 47 48 // Create 49 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 50 assert.NotEmpty(reply) 51 52 // Read 53 ixn.Intention.ID = reply 54 { 55 req := &structs.IntentionQueryRequest{ 56 Datacenter: "dc1", 57 IntentionID: ixn.Intention.ID, 58 } 59 var resp structs.IndexedIntentions 60 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 61 assert.Len(resp.Intentions, 1) 62 actual := resp.Intentions[0] 63 assert.Equal(resp.Index, actual.ModifyIndex) 64 assert.WithinDuration(now, actual.CreatedAt, 5*time.Second) 65 assert.WithinDuration(now, actual.UpdatedAt, 5*time.Second) 66 67 actual.CreateIndex, actual.ModifyIndex = 0, 0 68 actual.CreatedAt = ixn.Intention.CreatedAt 69 actual.UpdatedAt = ixn.Intention.UpdatedAt 70 ixn.Intention.UpdatePrecedence() 71 assert.Equal(ixn.Intention, actual) 72 } 73 } 74 75 // Test the source type defaults 76 func TestIntentionApply_defaultSourceType(t *testing.T) { 77 t.Parallel() 78 79 assert := assert.New(t) 80 dir1, s1 := testServer(t) 81 defer os.RemoveAll(dir1) 82 defer s1.Shutdown() 83 codec := rpcClient(t, s1) 84 defer codec.Close() 85 86 testrpc.WaitForLeader(t, s1.RPC, "dc1") 87 88 // Setup a basic record to create 89 ixn := structs.IntentionRequest{ 90 Datacenter: "dc1", 91 Op: structs.IntentionOpCreate, 92 Intention: &structs.Intention{ 93 SourceNS: structs.IntentionDefaultNamespace, 94 SourceName: "test", 95 DestinationNS: structs.IntentionDefaultNamespace, 96 DestinationName: "test", 97 Action: structs.IntentionActionAllow, 98 }, 99 } 100 var reply string 101 102 // Create 103 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 104 assert.NotEmpty(reply) 105 106 // Read 107 ixn.Intention.ID = reply 108 { 109 req := &structs.IntentionQueryRequest{ 110 Datacenter: "dc1", 111 IntentionID: ixn.Intention.ID, 112 } 113 var resp structs.IndexedIntentions 114 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 115 assert.Len(resp.Intentions, 1) 116 actual := resp.Intentions[0] 117 assert.Equal(structs.IntentionSourceConsul, actual.SourceType) 118 } 119 } 120 121 // Shouldn't be able to create with an ID set 122 func TestIntentionApply_createWithID(t *testing.T) { 123 t.Parallel() 124 125 assert := assert.New(t) 126 dir1, s1 := testServer(t) 127 defer os.RemoveAll(dir1) 128 defer s1.Shutdown() 129 codec := rpcClient(t, s1) 130 defer codec.Close() 131 132 testrpc.WaitForLeader(t, s1.RPC, "dc1") 133 134 // Setup a basic record to create 135 ixn := structs.IntentionRequest{ 136 Datacenter: "dc1", 137 Op: structs.IntentionOpCreate, 138 Intention: &structs.Intention{ 139 ID: generateUUID(), 140 SourceName: "test", 141 }, 142 } 143 var reply string 144 145 // Create 146 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 147 assert.NotNil(err) 148 assert.Contains(err, "ID must be empty") 149 } 150 151 // Test basic updating 152 func TestIntentionApply_updateGood(t *testing.T) { 153 t.Parallel() 154 155 assert := assert.New(t) 156 dir1, s1 := testServer(t) 157 defer os.RemoveAll(dir1) 158 defer s1.Shutdown() 159 codec := rpcClient(t, s1) 160 defer codec.Close() 161 162 testrpc.WaitForLeader(t, s1.RPC, "dc1") 163 164 // Setup a basic record to create 165 ixn := structs.IntentionRequest{ 166 Datacenter: "dc1", 167 Op: structs.IntentionOpCreate, 168 Intention: &structs.Intention{ 169 SourceNS: structs.IntentionDefaultNamespace, 170 SourceName: "test", 171 DestinationNS: structs.IntentionDefaultNamespace, 172 DestinationName: "test", 173 Action: structs.IntentionActionAllow, 174 SourceType: structs.IntentionSourceConsul, 175 Meta: map[string]string{}, 176 }, 177 } 178 var reply string 179 180 // Create 181 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 182 assert.NotEmpty(reply) 183 184 // Read CreatedAt 185 var createdAt time.Time 186 ixn.Intention.ID = reply 187 { 188 req := &structs.IntentionQueryRequest{ 189 Datacenter: "dc1", 190 IntentionID: ixn.Intention.ID, 191 } 192 var resp structs.IndexedIntentions 193 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 194 assert.Len(resp.Intentions, 1) 195 actual := resp.Intentions[0] 196 createdAt = actual.CreatedAt 197 } 198 199 // Sleep a bit so that the updated at will definitely be different, not much 200 time.Sleep(1 * time.Millisecond) 201 202 // Update 203 ixn.Op = structs.IntentionOpUpdate 204 ixn.Intention.ID = reply 205 ixn.Intention.SourceName = "*" 206 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 207 208 // Read 209 ixn.Intention.ID = reply 210 { 211 req := &structs.IntentionQueryRequest{ 212 Datacenter: "dc1", 213 IntentionID: ixn.Intention.ID, 214 } 215 var resp structs.IndexedIntentions 216 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 217 assert.Len(resp.Intentions, 1) 218 actual := resp.Intentions[0] 219 assert.Equal(createdAt, actual.CreatedAt) 220 assert.WithinDuration(time.Now(), actual.UpdatedAt, 5*time.Second) 221 222 actual.CreateIndex, actual.ModifyIndex = 0, 0 223 actual.CreatedAt = ixn.Intention.CreatedAt 224 actual.UpdatedAt = ixn.Intention.UpdatedAt 225 ixn.Intention.UpdatePrecedence() 226 assert.Equal(ixn.Intention, actual) 227 } 228 } 229 230 // Shouldn't be able to update a non-existent intention 231 func TestIntentionApply_updateNonExist(t *testing.T) { 232 t.Parallel() 233 234 assert := assert.New(t) 235 dir1, s1 := testServer(t) 236 defer os.RemoveAll(dir1) 237 defer s1.Shutdown() 238 codec := rpcClient(t, s1) 239 defer codec.Close() 240 241 testrpc.WaitForLeader(t, s1.RPC, "dc1") 242 243 // Setup a basic record to create 244 ixn := structs.IntentionRequest{ 245 Datacenter: "dc1", 246 Op: structs.IntentionOpUpdate, 247 Intention: &structs.Intention{ 248 ID: generateUUID(), 249 SourceName: "test", 250 }, 251 } 252 var reply string 253 254 // Create 255 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 256 assert.NotNil(err) 257 assert.Contains(err, "Cannot modify non-existent intention") 258 } 259 260 // Test basic deleting 261 func TestIntentionApply_deleteGood(t *testing.T) { 262 t.Parallel() 263 264 assert := assert.New(t) 265 dir1, s1 := testServer(t) 266 defer os.RemoveAll(dir1) 267 defer s1.Shutdown() 268 codec := rpcClient(t, s1) 269 defer codec.Close() 270 271 testrpc.WaitForLeader(t, s1.RPC, "dc1") 272 273 // Setup a basic record to create 274 ixn := structs.IntentionRequest{ 275 Datacenter: "dc1", 276 Op: structs.IntentionOpCreate, 277 Intention: &structs.Intention{ 278 SourceNS: "test", 279 SourceName: "test", 280 DestinationNS: "test", 281 DestinationName: "test", 282 Action: structs.IntentionActionAllow, 283 }, 284 } 285 var reply string 286 287 // Create 288 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 289 assert.NotEmpty(reply) 290 291 // Delete 292 ixn.Op = structs.IntentionOpDelete 293 ixn.Intention.ID = reply 294 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 295 296 // Read 297 ixn.Intention.ID = reply 298 { 299 req := &structs.IntentionQueryRequest{ 300 Datacenter: "dc1", 301 IntentionID: ixn.Intention.ID, 302 } 303 var resp structs.IndexedIntentions 304 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 305 assert.NotNil(err) 306 assert.Contains(err, ErrIntentionNotFound.Error()) 307 } 308 } 309 310 // Test apply with a deny ACL 311 func TestIntentionApply_aclDeny(t *testing.T) { 312 t.Parallel() 313 314 assert := assert.New(t) 315 dir1, s1 := testServerWithConfig(t, func(c *Config) { 316 c.ACLDatacenter = "dc1" 317 c.ACLsEnabled = true 318 c.ACLMasterToken = "root" 319 c.ACLDefaultPolicy = "deny" 320 }) 321 defer os.RemoveAll(dir1) 322 defer s1.Shutdown() 323 codec := rpcClient(t, s1) 324 defer codec.Close() 325 326 testrpc.WaitForLeader(t, s1.RPC, "dc1") 327 328 // Create an ACL with write permissions 329 var token string 330 { 331 var rules = ` 332 service "foo" { 333 policy = "deny" 334 intentions = "write" 335 }` 336 337 req := structs.ACLRequest{ 338 Datacenter: "dc1", 339 Op: structs.ACLSet, 340 ACL: structs.ACL{ 341 Name: "User token", 342 Type: structs.ACLTokenTypeClient, 343 Rules: rules, 344 }, 345 WriteRequest: structs.WriteRequest{Token: "root"}, 346 } 347 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 348 } 349 350 // Setup a basic record to create 351 ixn := structs.IntentionRequest{ 352 Datacenter: "dc1", 353 Op: structs.IntentionOpCreate, 354 Intention: structs.TestIntention(t), 355 } 356 ixn.Intention.DestinationName = "foobar" 357 358 // Create without a token should error since default deny 359 var reply string 360 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 361 assert.True(acl.IsErrPermissionDenied(err)) 362 363 // Now add the token and try again. 364 ixn.WriteRequest.Token = token 365 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 366 367 // Read 368 ixn.Intention.ID = reply 369 { 370 req := &structs.IntentionQueryRequest{ 371 Datacenter: "dc1", 372 IntentionID: ixn.Intention.ID, 373 QueryOptions: structs.QueryOptions{Token: "root"}, 374 } 375 var resp structs.IndexedIntentions 376 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 377 assert.Len(resp.Intentions, 1) 378 actual := resp.Intentions[0] 379 assert.Equal(resp.Index, actual.ModifyIndex) 380 381 actual.CreateIndex, actual.ModifyIndex = 0, 0 382 actual.CreatedAt = ixn.Intention.CreatedAt 383 actual.UpdatedAt = ixn.Intention.UpdatedAt 384 ixn.Intention.UpdatePrecedence() 385 assert.Equal(ixn.Intention, actual) 386 } 387 } 388 389 // Test apply with delete and a default deny ACL 390 func TestIntentionApply_aclDelete(t *testing.T) { 391 t.Parallel() 392 393 assert := assert.New(t) 394 dir1, s1 := testServerWithConfig(t, func(c *Config) { 395 c.ACLDatacenter = "dc1" 396 c.ACLsEnabled = true 397 c.ACLMasterToken = "root" 398 c.ACLDefaultPolicy = "deny" 399 }) 400 defer os.RemoveAll(dir1) 401 defer s1.Shutdown() 402 codec := rpcClient(t, s1) 403 defer codec.Close() 404 405 testrpc.WaitForLeader(t, s1.RPC, "dc1") 406 407 // Create an ACL with write permissions 408 var token string 409 { 410 var rules = ` 411 service "foo" { 412 policy = "deny" 413 intentions = "write" 414 }` 415 416 req := structs.ACLRequest{ 417 Datacenter: "dc1", 418 Op: structs.ACLSet, 419 ACL: structs.ACL{ 420 Name: "User token", 421 Type: structs.ACLTokenTypeClient, 422 Rules: rules, 423 }, 424 WriteRequest: structs.WriteRequest{Token: "root"}, 425 } 426 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 427 } 428 429 // Setup a basic record to create 430 ixn := structs.IntentionRequest{ 431 Datacenter: "dc1", 432 Op: structs.IntentionOpCreate, 433 Intention: structs.TestIntention(t), 434 } 435 ixn.Intention.DestinationName = "foobar" 436 ixn.WriteRequest.Token = token 437 438 // Create 439 var reply string 440 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 441 442 // Try to do a delete with no token; this should get rejected. 443 ixn.Op = structs.IntentionOpDelete 444 ixn.Intention.ID = reply 445 ixn.WriteRequest.Token = "" 446 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 447 assert.True(acl.IsErrPermissionDenied(err)) 448 449 // Try again with the original token. This should go through. 450 ixn.WriteRequest.Token = token 451 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 452 453 // Verify it is gone 454 { 455 req := &structs.IntentionQueryRequest{ 456 Datacenter: "dc1", 457 IntentionID: ixn.Intention.ID, 458 } 459 var resp structs.IndexedIntentions 460 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 461 assert.NotNil(err) 462 assert.Contains(err.Error(), ErrIntentionNotFound.Error()) 463 } 464 } 465 466 // Test apply with update and a default deny ACL 467 func TestIntentionApply_aclUpdate(t *testing.T) { 468 t.Parallel() 469 470 assert := assert.New(t) 471 dir1, s1 := testServerWithConfig(t, func(c *Config) { 472 c.ACLDatacenter = "dc1" 473 c.ACLsEnabled = true 474 c.ACLMasterToken = "root" 475 c.ACLDefaultPolicy = "deny" 476 }) 477 defer os.RemoveAll(dir1) 478 defer s1.Shutdown() 479 codec := rpcClient(t, s1) 480 defer codec.Close() 481 482 testrpc.WaitForLeader(t, s1.RPC, "dc1") 483 484 // Create an ACL with write permissions 485 var token string 486 { 487 var rules = ` 488 service "foo" { 489 policy = "deny" 490 intentions = "write" 491 }` 492 493 req := structs.ACLRequest{ 494 Datacenter: "dc1", 495 Op: structs.ACLSet, 496 ACL: structs.ACL{ 497 Name: "User token", 498 Type: structs.ACLTokenTypeClient, 499 Rules: rules, 500 }, 501 WriteRequest: structs.WriteRequest{Token: "root"}, 502 } 503 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 504 } 505 506 // Setup a basic record to create 507 ixn := structs.IntentionRequest{ 508 Datacenter: "dc1", 509 Op: structs.IntentionOpCreate, 510 Intention: structs.TestIntention(t), 511 } 512 ixn.Intention.DestinationName = "foobar" 513 ixn.WriteRequest.Token = token 514 515 // Create 516 var reply string 517 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 518 519 // Try to do an update without a token; this should get rejected. 520 ixn.Op = structs.IntentionOpUpdate 521 ixn.Intention.ID = reply 522 ixn.WriteRequest.Token = "" 523 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 524 assert.True(acl.IsErrPermissionDenied(err)) 525 526 // Try again with the original token; this should go through. 527 ixn.WriteRequest.Token = token 528 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 529 } 530 531 // Test apply with a management token 532 func TestIntentionApply_aclManagement(t *testing.T) { 533 t.Parallel() 534 535 assert := assert.New(t) 536 dir1, s1 := testServerWithConfig(t, func(c *Config) { 537 c.ACLDatacenter = "dc1" 538 c.ACLsEnabled = true 539 c.ACLMasterToken = "root" 540 c.ACLDefaultPolicy = "deny" 541 }) 542 defer os.RemoveAll(dir1) 543 defer s1.Shutdown() 544 codec := rpcClient(t, s1) 545 defer codec.Close() 546 547 testrpc.WaitForLeader(t, s1.RPC, "dc1") 548 549 // Setup a basic record to create 550 ixn := structs.IntentionRequest{ 551 Datacenter: "dc1", 552 Op: structs.IntentionOpCreate, 553 Intention: structs.TestIntention(t), 554 } 555 ixn.Intention.DestinationName = "foobar" 556 ixn.WriteRequest.Token = "root" 557 558 // Create 559 var reply string 560 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 561 ixn.Intention.ID = reply 562 563 // Update 564 ixn.Op = structs.IntentionOpUpdate 565 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 566 567 // Delete 568 ixn.Op = structs.IntentionOpDelete 569 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 570 } 571 572 // Test update changing the name where an ACL won't allow it 573 func TestIntentionApply_aclUpdateChange(t *testing.T) { 574 t.Parallel() 575 576 assert := assert.New(t) 577 dir1, s1 := testServerWithConfig(t, func(c *Config) { 578 c.ACLDatacenter = "dc1" 579 c.ACLsEnabled = true 580 c.ACLMasterToken = "root" 581 c.ACLDefaultPolicy = "deny" 582 }) 583 defer os.RemoveAll(dir1) 584 defer s1.Shutdown() 585 codec := rpcClient(t, s1) 586 defer codec.Close() 587 588 testrpc.WaitForLeader(t, s1.RPC, "dc1") 589 590 // Create an ACL with write permissions 591 var token string 592 { 593 var rules = ` 594 service "foo" { 595 policy = "deny" 596 intentions = "write" 597 }` 598 599 req := structs.ACLRequest{ 600 Datacenter: "dc1", 601 Op: structs.ACLSet, 602 ACL: structs.ACL{ 603 Name: "User token", 604 Type: structs.ACLTokenTypeClient, 605 Rules: rules, 606 }, 607 WriteRequest: structs.WriteRequest{Token: "root"}, 608 } 609 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 610 } 611 612 // Setup a basic record to create 613 ixn := structs.IntentionRequest{ 614 Datacenter: "dc1", 615 Op: structs.IntentionOpCreate, 616 Intention: structs.TestIntention(t), 617 } 618 ixn.Intention.DestinationName = "bar" 619 ixn.WriteRequest.Token = "root" 620 621 // Create 622 var reply string 623 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 624 625 // Try to do an update without a token; this should get rejected. 626 ixn.Op = structs.IntentionOpUpdate 627 ixn.Intention.ID = reply 628 ixn.Intention.DestinationName = "foo" 629 ixn.WriteRequest.Token = token 630 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 631 assert.True(acl.IsErrPermissionDenied(err)) 632 } 633 634 // Test reading with ACLs 635 func TestIntentionGet_acl(t *testing.T) { 636 t.Parallel() 637 638 assert := assert.New(t) 639 dir1, s1 := testServerWithConfig(t, func(c *Config) { 640 c.ACLDatacenter = "dc1" 641 c.ACLsEnabled = true 642 c.ACLMasterToken = "root" 643 c.ACLDefaultPolicy = "deny" 644 }) 645 defer os.RemoveAll(dir1) 646 defer s1.Shutdown() 647 codec := rpcClient(t, s1) 648 defer codec.Close() 649 650 testrpc.WaitForLeader(t, s1.RPC, "dc1") 651 652 // Create an ACL with service write permissions. This will grant 653 // intentions read. 654 var token string 655 { 656 var rules = ` 657 service "foo" { 658 policy = "write" 659 }` 660 661 req := structs.ACLRequest{ 662 Datacenter: "dc1", 663 Op: structs.ACLSet, 664 ACL: structs.ACL{ 665 Name: "User token", 666 Type: structs.ACLTokenTypeClient, 667 Rules: rules, 668 }, 669 WriteRequest: structs.WriteRequest{Token: "root"}, 670 } 671 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 672 } 673 674 // Setup a basic record to create 675 ixn := structs.IntentionRequest{ 676 Datacenter: "dc1", 677 Op: structs.IntentionOpCreate, 678 Intention: structs.TestIntention(t), 679 } 680 ixn.Intention.DestinationName = "foobar" 681 ixn.WriteRequest.Token = "root" 682 683 // Create 684 var reply string 685 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 686 ixn.Intention.ID = reply 687 688 // Read without token should be error 689 { 690 req := &structs.IntentionQueryRequest{ 691 Datacenter: "dc1", 692 IntentionID: ixn.Intention.ID, 693 } 694 695 var resp structs.IndexedIntentions 696 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 697 assert.True(acl.IsErrPermissionDenied(err)) 698 assert.Len(resp.Intentions, 0) 699 } 700 701 // Read with token should work 702 { 703 req := &structs.IntentionQueryRequest{ 704 Datacenter: "dc1", 705 IntentionID: ixn.Intention.ID, 706 QueryOptions: structs.QueryOptions{Token: token}, 707 } 708 709 var resp structs.IndexedIntentions 710 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 711 assert.Len(resp.Intentions, 1) 712 } 713 } 714 715 func TestIntentionList(t *testing.T) { 716 t.Parallel() 717 718 assert := assert.New(t) 719 dir1, s1 := testServer(t) 720 defer os.RemoveAll(dir1) 721 defer s1.Shutdown() 722 723 codec := rpcClient(t, s1) 724 defer codec.Close() 725 testrpc.WaitForLeader(t, s1.RPC, "dc1") 726 727 // Test with no intentions inserted yet 728 { 729 req := &structs.DCSpecificRequest{ 730 Datacenter: "dc1", 731 } 732 var resp structs.IndexedIntentions 733 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 734 assert.NotNil(resp.Intentions) 735 assert.Len(resp.Intentions, 0) 736 } 737 } 738 739 // Test listing with ACLs 740 func TestIntentionList_acl(t *testing.T) { 741 t.Parallel() 742 743 assert := assert.New(t) 744 dir1, s1 := testServerWithConfig(t, func(c *Config) { 745 c.ACLDatacenter = "dc1" 746 c.ACLsEnabled = true 747 c.ACLMasterToken = "root" 748 c.ACLDefaultPolicy = "deny" 749 }) 750 defer os.RemoveAll(dir1) 751 defer s1.Shutdown() 752 codec := rpcClient(t, s1) 753 defer codec.Close() 754 755 testrpc.WaitForLeader(t, s1.RPC, "dc1") 756 757 // Create an ACL with service write permissions. This will grant 758 // intentions read. 759 var token string 760 { 761 var rules = ` 762 service "foo" { 763 policy = "write" 764 }` 765 766 req := structs.ACLRequest{ 767 Datacenter: "dc1", 768 Op: structs.ACLSet, 769 ACL: structs.ACL{ 770 Name: "User token", 771 Type: structs.ACLTokenTypeClient, 772 Rules: rules, 773 }, 774 WriteRequest: structs.WriteRequest{Token: "root"}, 775 } 776 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 777 } 778 779 // Create a few records 780 for _, name := range []string{"foobar", "bar", "baz"} { 781 ixn := structs.IntentionRequest{ 782 Datacenter: "dc1", 783 Op: structs.IntentionOpCreate, 784 Intention: structs.TestIntention(t), 785 } 786 ixn.Intention.DestinationName = name 787 ixn.WriteRequest.Token = "root" 788 789 // Create 790 var reply string 791 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 792 } 793 794 // Test with no token 795 { 796 req := &structs.DCSpecificRequest{ 797 Datacenter: "dc1", 798 } 799 var resp structs.IndexedIntentions 800 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 801 assert.Len(resp.Intentions, 0) 802 } 803 804 // Test with management token 805 { 806 req := &structs.DCSpecificRequest{ 807 Datacenter: "dc1", 808 QueryOptions: structs.QueryOptions{Token: "root"}, 809 } 810 var resp structs.IndexedIntentions 811 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 812 assert.Len(resp.Intentions, 3) 813 } 814 815 // Test with user token 816 { 817 req := &structs.DCSpecificRequest{ 818 Datacenter: "dc1", 819 QueryOptions: structs.QueryOptions{Token: token}, 820 } 821 var resp structs.IndexedIntentions 822 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 823 assert.Len(resp.Intentions, 1) 824 } 825 } 826 827 // Test basic matching. We don't need to exhaustively test inputs since this 828 // is tested in the agent/consul/state package. 829 func TestIntentionMatch_good(t *testing.T) { 830 t.Parallel() 831 832 assert := assert.New(t) 833 dir1, s1 := testServer(t) 834 defer os.RemoveAll(dir1) 835 defer s1.Shutdown() 836 codec := rpcClient(t, s1) 837 defer codec.Close() 838 839 testrpc.WaitForLeader(t, s1.RPC, "dc1") 840 841 // Create some records 842 { 843 insert := [][]string{ 844 {"foo", "*", "foo", "*"}, 845 {"foo", "*", "foo", "bar"}, 846 {"foo", "*", "foo", "baz"}, // shouldn't match 847 {"foo", "*", "bar", "bar"}, // shouldn't match 848 {"foo", "*", "bar", "*"}, // shouldn't match 849 {"foo", "*", "*", "*"}, 850 {"bar", "*", "foo", "bar"}, // duplicate destination different source 851 } 852 853 for _, v := range insert { 854 ixn := structs.IntentionRequest{ 855 Datacenter: "dc1", 856 Op: structs.IntentionOpCreate, 857 Intention: &structs.Intention{ 858 SourceNS: v[0], 859 SourceName: v[1], 860 DestinationNS: v[2], 861 DestinationName: v[3], 862 Action: structs.IntentionActionAllow, 863 }, 864 } 865 866 // Create 867 var reply string 868 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 869 } 870 } 871 872 // Match 873 req := &structs.IntentionQueryRequest{ 874 Datacenter: "dc1", 875 Match: &structs.IntentionQueryMatch{ 876 Type: structs.IntentionMatchDestination, 877 Entries: []structs.IntentionMatchEntry{ 878 { 879 Namespace: "foo", 880 Name: "bar", 881 }, 882 }, 883 }, 884 } 885 var resp structs.IndexedIntentionMatches 886 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 887 assert.Len(resp.Matches, 1) 888 889 expected := [][]string{ 890 {"bar", "*", "foo", "bar"}, 891 {"foo", "*", "foo", "bar"}, 892 {"foo", "*", "foo", "*"}, 893 {"foo", "*", "*", "*"}, 894 } 895 var actual [][]string 896 for _, ixn := range resp.Matches[0] { 897 actual = append(actual, []string{ 898 ixn.SourceNS, 899 ixn.SourceName, 900 ixn.DestinationNS, 901 ixn.DestinationName, 902 }) 903 } 904 assert.Equal(expected, actual) 905 } 906 907 // Test matching with ACLs 908 func TestIntentionMatch_acl(t *testing.T) { 909 t.Parallel() 910 911 assert := assert.New(t) 912 dir1, s1 := testServerWithConfig(t, func(c *Config) { 913 c.ACLDatacenter = "dc1" 914 c.ACLsEnabled = true 915 c.ACLMasterToken = "root" 916 c.ACLDefaultPolicy = "deny" 917 }) 918 defer os.RemoveAll(dir1) 919 defer s1.Shutdown() 920 codec := rpcClient(t, s1) 921 defer codec.Close() 922 923 testrpc.WaitForLeader(t, s1.RPC, "dc1") 924 925 // Create an ACL with service write permissions. This will grant 926 // intentions read. 927 var token string 928 { 929 var rules = ` 930 service "bar" { 931 policy = "write" 932 }` 933 934 req := structs.ACLRequest{ 935 Datacenter: "dc1", 936 Op: structs.ACLSet, 937 ACL: structs.ACL{ 938 Name: "User token", 939 Type: structs.ACLTokenTypeClient, 940 Rules: rules, 941 }, 942 WriteRequest: structs.WriteRequest{Token: "root"}, 943 } 944 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 945 } 946 947 // Create some records 948 { 949 insert := [][]string{ 950 {"foo", "*"}, 951 {"foo", "bar"}, 952 {"foo", "baz"}, // shouldn't match 953 {"bar", "bar"}, // shouldn't match 954 {"bar", "*"}, // shouldn't match 955 {"*", "*"}, 956 } 957 958 for _, v := range insert { 959 ixn := structs.IntentionRequest{ 960 Datacenter: "dc1", 961 Op: structs.IntentionOpCreate, 962 Intention: structs.TestIntention(t), 963 } 964 ixn.Intention.DestinationNS = v[0] 965 ixn.Intention.DestinationName = v[1] 966 ixn.WriteRequest.Token = "root" 967 968 // Create 969 var reply string 970 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 971 } 972 } 973 974 // Test with no token 975 { 976 req := &structs.IntentionQueryRequest{ 977 Datacenter: "dc1", 978 Match: &structs.IntentionQueryMatch{ 979 Type: structs.IntentionMatchDestination, 980 Entries: []structs.IntentionMatchEntry{ 981 { 982 Namespace: "foo", 983 Name: "bar", 984 }, 985 }, 986 }, 987 } 988 var resp structs.IndexedIntentionMatches 989 err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp) 990 assert.True(acl.IsErrPermissionDenied(err)) 991 assert.Len(resp.Matches, 0) 992 } 993 994 // Test with proper token 995 { 996 req := &structs.IntentionQueryRequest{ 997 Datacenter: "dc1", 998 Match: &structs.IntentionQueryMatch{ 999 Type: structs.IntentionMatchDestination, 1000 Entries: []structs.IntentionMatchEntry{ 1001 { 1002 Namespace: "foo", 1003 Name: "bar", 1004 }, 1005 }, 1006 }, 1007 QueryOptions: structs.QueryOptions{Token: token}, 1008 } 1009 var resp structs.IndexedIntentionMatches 1010 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 1011 assert.Len(resp.Matches, 1) 1012 1013 expected := [][]string{{"foo", "bar"}, {"foo", "*"}, {"*", "*"}} 1014 var actual [][]string 1015 for _, ixn := range resp.Matches[0] { 1016 actual = append(actual, []string{ixn.DestinationNS, ixn.DestinationName}) 1017 } 1018 1019 assert.Equal(expected, actual) 1020 } 1021 } 1022 1023 // Test the Check method defaults to allow with no ACL set. 1024 func TestIntentionCheck_defaultNoACL(t *testing.T) { 1025 t.Parallel() 1026 1027 require := require.New(t) 1028 dir1, s1 := testServer(t) 1029 defer os.RemoveAll(dir1) 1030 defer s1.Shutdown() 1031 codec := rpcClient(t, s1) 1032 defer codec.Close() 1033 1034 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1035 1036 // Test 1037 req := &structs.IntentionQueryRequest{ 1038 Datacenter: "dc1", 1039 Check: &structs.IntentionQueryCheck{ 1040 SourceNS: "foo", 1041 SourceName: "bar", 1042 DestinationNS: "foo", 1043 DestinationName: "qux", 1044 SourceType: structs.IntentionSourceConsul, 1045 }, 1046 } 1047 var resp structs.IntentionQueryCheckResponse 1048 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1049 require.True(resp.Allowed) 1050 } 1051 1052 // Test the Check method defaults to deny with whitelist ACLs. 1053 func TestIntentionCheck_defaultACLDeny(t *testing.T) { 1054 t.Parallel() 1055 1056 require := require.New(t) 1057 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1058 c.ACLDatacenter = "dc1" 1059 c.ACLsEnabled = true 1060 c.ACLMasterToken = "root" 1061 c.ACLDefaultPolicy = "deny" 1062 }) 1063 defer os.RemoveAll(dir1) 1064 defer s1.Shutdown() 1065 codec := rpcClient(t, s1) 1066 defer codec.Close() 1067 1068 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1069 1070 // Check 1071 req := &structs.IntentionQueryRequest{ 1072 Datacenter: "dc1", 1073 Check: &structs.IntentionQueryCheck{ 1074 SourceNS: "foo", 1075 SourceName: "bar", 1076 DestinationNS: "foo", 1077 DestinationName: "qux", 1078 SourceType: structs.IntentionSourceConsul, 1079 }, 1080 } 1081 req.Token = "root" 1082 var resp structs.IntentionQueryCheckResponse 1083 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1084 require.False(resp.Allowed) 1085 } 1086 1087 // Test the Check method defaults to deny with blacklist ACLs. 1088 func TestIntentionCheck_defaultACLAllow(t *testing.T) { 1089 t.Parallel() 1090 1091 require := require.New(t) 1092 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1093 c.ACLDatacenter = "dc1" 1094 c.ACLsEnabled = true 1095 c.ACLMasterToken = "root" 1096 c.ACLDefaultPolicy = "allow" 1097 }) 1098 defer os.RemoveAll(dir1) 1099 defer s1.Shutdown() 1100 codec := rpcClient(t, s1) 1101 defer codec.Close() 1102 1103 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1104 1105 // Check 1106 req := &structs.IntentionQueryRequest{ 1107 Datacenter: "dc1", 1108 Check: &structs.IntentionQueryCheck{ 1109 SourceNS: "foo", 1110 SourceName: "bar", 1111 DestinationNS: "foo", 1112 DestinationName: "qux", 1113 SourceType: structs.IntentionSourceConsul, 1114 }, 1115 } 1116 req.Token = "root" 1117 var resp structs.IntentionQueryCheckResponse 1118 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1119 require.True(resp.Allowed) 1120 } 1121 1122 // Test the Check method requires service:read permission. 1123 func TestIntentionCheck_aclDeny(t *testing.T) { 1124 t.Parallel() 1125 1126 require := require.New(t) 1127 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1128 c.ACLDatacenter = "dc1" 1129 c.ACLsEnabled = true 1130 c.ACLMasterToken = "root" 1131 c.ACLDefaultPolicy = "deny" 1132 }) 1133 defer os.RemoveAll(dir1) 1134 defer s1.Shutdown() 1135 codec := rpcClient(t, s1) 1136 defer codec.Close() 1137 1138 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1139 1140 // Create an ACL with service read permissions. This will grant permission. 1141 var token string 1142 { 1143 var rules = ` 1144 service "bar" { 1145 policy = "read" 1146 }` 1147 1148 req := structs.ACLRequest{ 1149 Datacenter: "dc1", 1150 Op: structs.ACLSet, 1151 ACL: structs.ACL{ 1152 Name: "User token", 1153 Type: structs.ACLTokenTypeClient, 1154 Rules: rules, 1155 }, 1156 WriteRequest: structs.WriteRequest{Token: "root"}, 1157 } 1158 require.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 1159 } 1160 1161 // Check 1162 req := &structs.IntentionQueryRequest{ 1163 Datacenter: "dc1", 1164 Check: &structs.IntentionQueryCheck{ 1165 SourceNS: "foo", 1166 SourceName: "qux", 1167 DestinationNS: "foo", 1168 DestinationName: "baz", 1169 SourceType: structs.IntentionSourceConsul, 1170 }, 1171 } 1172 req.Token = token 1173 var resp structs.IntentionQueryCheckResponse 1174 err := msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp) 1175 require.True(acl.IsErrPermissionDenied(err)) 1176 } 1177 1178 // Test the Check method returns allow/deny properly. 1179 func TestIntentionCheck_match(t *testing.T) { 1180 t.Parallel() 1181 1182 require := require.New(t) 1183 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1184 c.ACLDatacenter = "dc1" 1185 c.ACLsEnabled = true 1186 c.ACLMasterToken = "root" 1187 c.ACLDefaultPolicy = "deny" 1188 }) 1189 defer os.RemoveAll(dir1) 1190 defer s1.Shutdown() 1191 codec := rpcClient(t, s1) 1192 defer codec.Close() 1193 1194 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1195 1196 // Create an ACL with service read permissions. This will grant permission. 1197 var token string 1198 { 1199 var rules = ` 1200 service "bar" { 1201 policy = "read" 1202 }` 1203 1204 req := structs.ACLRequest{ 1205 Datacenter: "dc1", 1206 Op: structs.ACLSet, 1207 ACL: structs.ACL{ 1208 Name: "User token", 1209 Type: structs.ACLTokenTypeClient, 1210 Rules: rules, 1211 }, 1212 WriteRequest: structs.WriteRequest{Token: "root"}, 1213 } 1214 require.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 1215 } 1216 1217 // Create some intentions 1218 { 1219 insert := [][]string{ 1220 {"foo", "*", "foo", "*"}, 1221 {"foo", "*", "foo", "bar"}, 1222 {"bar", "*", "foo", "bar"}, // duplicate destination different source 1223 } 1224 1225 for _, v := range insert { 1226 ixn := structs.IntentionRequest{ 1227 Datacenter: "dc1", 1228 Op: structs.IntentionOpCreate, 1229 Intention: &structs.Intention{ 1230 SourceNS: v[0], 1231 SourceName: v[1], 1232 DestinationNS: v[2], 1233 DestinationName: v[3], 1234 Action: structs.IntentionActionAllow, 1235 }, 1236 } 1237 ixn.WriteRequest.Token = "root" 1238 1239 // Create 1240 var reply string 1241 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1242 } 1243 } 1244 1245 // Check 1246 req := &structs.IntentionQueryRequest{ 1247 Datacenter: "dc1", 1248 Check: &structs.IntentionQueryCheck{ 1249 SourceNS: "foo", 1250 SourceName: "qux", 1251 DestinationNS: "foo", 1252 DestinationName: "bar", 1253 SourceType: structs.IntentionSourceConsul, 1254 }, 1255 } 1256 req.Token = token 1257 var resp structs.IntentionQueryCheckResponse 1258 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1259 require.True(resp.Allowed) 1260 1261 // Test no match for sanity 1262 { 1263 req := &structs.IntentionQueryRequest{ 1264 Datacenter: "dc1", 1265 Check: &structs.IntentionQueryCheck{ 1266 SourceNS: "baz", 1267 SourceName: "qux", 1268 DestinationNS: "foo", 1269 DestinationName: "bar", 1270 SourceType: structs.IntentionSourceConsul, 1271 }, 1272 } 1273 req.Token = token 1274 var resp structs.IntentionQueryCheckResponse 1275 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1276 require.False(resp.Allowed) 1277 } 1278 }