1package consul 2 3import ( 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 17func 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 76func 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 122func 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 152func 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 231func 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 261func 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 311func 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.ACLMasterToken = "root" 318 c.ACLDefaultPolicy = "deny" 319 }) 320 defer os.RemoveAll(dir1) 321 defer s1.Shutdown() 322 codec := rpcClient(t, s1) 323 defer codec.Close() 324 325 testrpc.WaitForLeader(t, s1.RPC, "dc1") 326 327 // Create an ACL with write permissions 328 var token string 329 { 330 var rules = ` 331service "foo" { 332 policy = "deny" 333 intentions = "write" 334}` 335 336 req := structs.ACLRequest{ 337 Datacenter: "dc1", 338 Op: structs.ACLSet, 339 ACL: structs.ACL{ 340 Name: "User token", 341 Type: structs.ACLTypeClient, 342 Rules: rules, 343 }, 344 WriteRequest: structs.WriteRequest{Token: "root"}, 345 } 346 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 347 } 348 349 // Setup a basic record to create 350 ixn := structs.IntentionRequest{ 351 Datacenter: "dc1", 352 Op: structs.IntentionOpCreate, 353 Intention: structs.TestIntention(t), 354 } 355 ixn.Intention.DestinationName = "foobar" 356 357 // Create without a token should error since default deny 358 var reply string 359 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 360 assert.True(acl.IsErrPermissionDenied(err)) 361 362 // Now add the token and try again. 363 ixn.WriteRequest.Token = token 364 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 365 366 // Read 367 ixn.Intention.ID = reply 368 { 369 req := &structs.IntentionQueryRequest{ 370 Datacenter: "dc1", 371 IntentionID: ixn.Intention.ID, 372 QueryOptions: structs.QueryOptions{Token: "root"}, 373 } 374 var resp structs.IndexedIntentions 375 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 376 assert.Len(resp.Intentions, 1) 377 actual := resp.Intentions[0] 378 assert.Equal(resp.Index, actual.ModifyIndex) 379 380 actual.CreateIndex, actual.ModifyIndex = 0, 0 381 actual.CreatedAt = ixn.Intention.CreatedAt 382 actual.UpdatedAt = ixn.Intention.UpdatedAt 383 ixn.Intention.UpdatePrecedence() 384 assert.Equal(ixn.Intention, actual) 385 } 386} 387 388// Test apply with delete and a default deny ACL 389func TestIntentionApply_aclDelete(t *testing.T) { 390 t.Parallel() 391 392 assert := assert.New(t) 393 dir1, s1 := testServerWithConfig(t, func(c *Config) { 394 c.ACLDatacenter = "dc1" 395 c.ACLMasterToken = "root" 396 c.ACLDefaultPolicy = "deny" 397 }) 398 defer os.RemoveAll(dir1) 399 defer s1.Shutdown() 400 codec := rpcClient(t, s1) 401 defer codec.Close() 402 403 testrpc.WaitForLeader(t, s1.RPC, "dc1") 404 405 // Create an ACL with write permissions 406 var token string 407 { 408 var rules = ` 409service "foo" { 410 policy = "deny" 411 intentions = "write" 412}` 413 414 req := structs.ACLRequest{ 415 Datacenter: "dc1", 416 Op: structs.ACLSet, 417 ACL: structs.ACL{ 418 Name: "User token", 419 Type: structs.ACLTypeClient, 420 Rules: rules, 421 }, 422 WriteRequest: structs.WriteRequest{Token: "root"}, 423 } 424 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 425 } 426 427 // Setup a basic record to create 428 ixn := structs.IntentionRequest{ 429 Datacenter: "dc1", 430 Op: structs.IntentionOpCreate, 431 Intention: structs.TestIntention(t), 432 } 433 ixn.Intention.DestinationName = "foobar" 434 ixn.WriteRequest.Token = token 435 436 // Create 437 var reply string 438 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 439 440 // Try to do a delete with no token; this should get rejected. 441 ixn.Op = structs.IntentionOpDelete 442 ixn.Intention.ID = reply 443 ixn.WriteRequest.Token = "" 444 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 445 assert.True(acl.IsErrPermissionDenied(err)) 446 447 // Try again with the original token. This should go through. 448 ixn.WriteRequest.Token = token 449 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 450 451 // Verify it is gone 452 { 453 req := &structs.IntentionQueryRequest{ 454 Datacenter: "dc1", 455 IntentionID: ixn.Intention.ID, 456 } 457 var resp structs.IndexedIntentions 458 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 459 assert.NotNil(err) 460 assert.Contains(err.Error(), ErrIntentionNotFound.Error()) 461 } 462} 463 464// Test apply with update and a default deny ACL 465func TestIntentionApply_aclUpdate(t *testing.T) { 466 t.Parallel() 467 468 assert := assert.New(t) 469 dir1, s1 := testServerWithConfig(t, func(c *Config) { 470 c.ACLDatacenter = "dc1" 471 c.ACLMasterToken = "root" 472 c.ACLDefaultPolicy = "deny" 473 }) 474 defer os.RemoveAll(dir1) 475 defer s1.Shutdown() 476 codec := rpcClient(t, s1) 477 defer codec.Close() 478 479 testrpc.WaitForLeader(t, s1.RPC, "dc1") 480 481 // Create an ACL with write permissions 482 var token string 483 { 484 var rules = ` 485service "foo" { 486 policy = "deny" 487 intentions = "write" 488}` 489 490 req := structs.ACLRequest{ 491 Datacenter: "dc1", 492 Op: structs.ACLSet, 493 ACL: structs.ACL{ 494 Name: "User token", 495 Type: structs.ACLTypeClient, 496 Rules: rules, 497 }, 498 WriteRequest: structs.WriteRequest{Token: "root"}, 499 } 500 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 501 } 502 503 // Setup a basic record to create 504 ixn := structs.IntentionRequest{ 505 Datacenter: "dc1", 506 Op: structs.IntentionOpCreate, 507 Intention: structs.TestIntention(t), 508 } 509 ixn.Intention.DestinationName = "foobar" 510 ixn.WriteRequest.Token = token 511 512 // Create 513 var reply string 514 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 515 516 // Try to do an update without a token; this should get rejected. 517 ixn.Op = structs.IntentionOpUpdate 518 ixn.Intention.ID = reply 519 ixn.WriteRequest.Token = "" 520 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 521 assert.True(acl.IsErrPermissionDenied(err)) 522 523 // Try again with the original token; this should go through. 524 ixn.WriteRequest.Token = token 525 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 526} 527 528// Test apply with a management token 529func TestIntentionApply_aclManagement(t *testing.T) { 530 t.Parallel() 531 532 assert := assert.New(t) 533 dir1, s1 := testServerWithConfig(t, func(c *Config) { 534 c.ACLDatacenter = "dc1" 535 c.ACLMasterToken = "root" 536 c.ACLDefaultPolicy = "deny" 537 }) 538 defer os.RemoveAll(dir1) 539 defer s1.Shutdown() 540 codec := rpcClient(t, s1) 541 defer codec.Close() 542 543 testrpc.WaitForLeader(t, s1.RPC, "dc1") 544 545 // Setup a basic record to create 546 ixn := structs.IntentionRequest{ 547 Datacenter: "dc1", 548 Op: structs.IntentionOpCreate, 549 Intention: structs.TestIntention(t), 550 } 551 ixn.Intention.DestinationName = "foobar" 552 ixn.WriteRequest.Token = "root" 553 554 // Create 555 var reply string 556 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 557 ixn.Intention.ID = reply 558 559 // Update 560 ixn.Op = structs.IntentionOpUpdate 561 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 562 563 // Delete 564 ixn.Op = structs.IntentionOpDelete 565 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 566} 567 568// Test update changing the name where an ACL won't allow it 569func TestIntentionApply_aclUpdateChange(t *testing.T) { 570 t.Parallel() 571 572 assert := assert.New(t) 573 dir1, s1 := testServerWithConfig(t, func(c *Config) { 574 c.ACLDatacenter = "dc1" 575 c.ACLMasterToken = "root" 576 c.ACLDefaultPolicy = "deny" 577 }) 578 defer os.RemoveAll(dir1) 579 defer s1.Shutdown() 580 codec := rpcClient(t, s1) 581 defer codec.Close() 582 583 testrpc.WaitForLeader(t, s1.RPC, "dc1") 584 585 // Create an ACL with write permissions 586 var token string 587 { 588 var rules = ` 589service "foo" { 590 policy = "deny" 591 intentions = "write" 592}` 593 594 req := structs.ACLRequest{ 595 Datacenter: "dc1", 596 Op: structs.ACLSet, 597 ACL: structs.ACL{ 598 Name: "User token", 599 Type: structs.ACLTypeClient, 600 Rules: rules, 601 }, 602 WriteRequest: structs.WriteRequest{Token: "root"}, 603 } 604 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 605 } 606 607 // Setup a basic record to create 608 ixn := structs.IntentionRequest{ 609 Datacenter: "dc1", 610 Op: structs.IntentionOpCreate, 611 Intention: structs.TestIntention(t), 612 } 613 ixn.Intention.DestinationName = "bar" 614 ixn.WriteRequest.Token = "root" 615 616 // Create 617 var reply string 618 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 619 620 // Try to do an update without a token; this should get rejected. 621 ixn.Op = structs.IntentionOpUpdate 622 ixn.Intention.ID = reply 623 ixn.Intention.DestinationName = "foo" 624 ixn.WriteRequest.Token = token 625 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 626 assert.True(acl.IsErrPermissionDenied(err)) 627} 628 629// Test reading with ACLs 630func TestIntentionGet_acl(t *testing.T) { 631 t.Parallel() 632 633 assert := assert.New(t) 634 dir1, s1 := testServerWithConfig(t, func(c *Config) { 635 c.ACLDatacenter = "dc1" 636 c.ACLMasterToken = "root" 637 c.ACLDefaultPolicy = "deny" 638 }) 639 defer os.RemoveAll(dir1) 640 defer s1.Shutdown() 641 codec := rpcClient(t, s1) 642 defer codec.Close() 643 644 testrpc.WaitForLeader(t, s1.RPC, "dc1") 645 646 // Create an ACL with service write permissions. This will grant 647 // intentions read. 648 var token string 649 { 650 var rules = ` 651service "foo" { 652 policy = "write" 653}` 654 655 req := structs.ACLRequest{ 656 Datacenter: "dc1", 657 Op: structs.ACLSet, 658 ACL: structs.ACL{ 659 Name: "User token", 660 Type: structs.ACLTypeClient, 661 Rules: rules, 662 }, 663 WriteRequest: structs.WriteRequest{Token: "root"}, 664 } 665 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 666 } 667 668 // Setup a basic record to create 669 ixn := structs.IntentionRequest{ 670 Datacenter: "dc1", 671 Op: structs.IntentionOpCreate, 672 Intention: structs.TestIntention(t), 673 } 674 ixn.Intention.DestinationName = "foobar" 675 ixn.WriteRequest.Token = "root" 676 677 // Create 678 var reply string 679 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 680 ixn.Intention.ID = reply 681 682 // Read without token should be error 683 { 684 req := &structs.IntentionQueryRequest{ 685 Datacenter: "dc1", 686 IntentionID: ixn.Intention.ID, 687 } 688 689 var resp structs.IndexedIntentions 690 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 691 assert.True(acl.IsErrPermissionDenied(err)) 692 assert.Len(resp.Intentions, 0) 693 } 694 695 // Read with token should work 696 { 697 req := &structs.IntentionQueryRequest{ 698 Datacenter: "dc1", 699 IntentionID: ixn.Intention.ID, 700 QueryOptions: structs.QueryOptions{Token: token}, 701 } 702 703 var resp structs.IndexedIntentions 704 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 705 assert.Len(resp.Intentions, 1) 706 } 707} 708 709func TestIntentionList(t *testing.T) { 710 t.Parallel() 711 712 assert := assert.New(t) 713 dir1, s1 := testServer(t) 714 defer os.RemoveAll(dir1) 715 defer s1.Shutdown() 716 717 codec := rpcClient(t, s1) 718 defer codec.Close() 719 testrpc.WaitForLeader(t, s1.RPC, "dc1") 720 721 // Test with no intentions inserted yet 722 { 723 req := &structs.DCSpecificRequest{ 724 Datacenter: "dc1", 725 } 726 var resp structs.IndexedIntentions 727 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 728 assert.NotNil(resp.Intentions) 729 assert.Len(resp.Intentions, 0) 730 } 731} 732 733// Test listing with ACLs 734func TestIntentionList_acl(t *testing.T) { 735 t.Parallel() 736 737 assert := assert.New(t) 738 dir1, s1 := testServerWithConfig(t, func(c *Config) { 739 c.ACLDatacenter = "dc1" 740 c.ACLMasterToken = "root" 741 c.ACLDefaultPolicy = "deny" 742 }) 743 defer os.RemoveAll(dir1) 744 defer s1.Shutdown() 745 codec := rpcClient(t, s1) 746 defer codec.Close() 747 748 testrpc.WaitForLeader(t, s1.RPC, "dc1") 749 750 // Create an ACL with service write permissions. This will grant 751 // intentions read. 752 var token string 753 { 754 var rules = ` 755service "foo" { 756 policy = "write" 757}` 758 759 req := structs.ACLRequest{ 760 Datacenter: "dc1", 761 Op: structs.ACLSet, 762 ACL: structs.ACL{ 763 Name: "User token", 764 Type: structs.ACLTypeClient, 765 Rules: rules, 766 }, 767 WriteRequest: structs.WriteRequest{Token: "root"}, 768 } 769 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 770 } 771 772 // Create a few records 773 for _, name := range []string{"foobar", "bar", "baz"} { 774 ixn := structs.IntentionRequest{ 775 Datacenter: "dc1", 776 Op: structs.IntentionOpCreate, 777 Intention: structs.TestIntention(t), 778 } 779 ixn.Intention.DestinationName = name 780 ixn.WriteRequest.Token = "root" 781 782 // Create 783 var reply string 784 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 785 } 786 787 // Test with no token 788 { 789 req := &structs.DCSpecificRequest{ 790 Datacenter: "dc1", 791 } 792 var resp structs.IndexedIntentions 793 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 794 assert.Len(resp.Intentions, 0) 795 } 796 797 // Test with management token 798 { 799 req := &structs.DCSpecificRequest{ 800 Datacenter: "dc1", 801 QueryOptions: structs.QueryOptions{Token: "root"}, 802 } 803 var resp structs.IndexedIntentions 804 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 805 assert.Len(resp.Intentions, 3) 806 } 807 808 // Test with user token 809 { 810 req := &structs.DCSpecificRequest{ 811 Datacenter: "dc1", 812 QueryOptions: structs.QueryOptions{Token: token}, 813 } 814 var resp structs.IndexedIntentions 815 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 816 assert.Len(resp.Intentions, 1) 817 } 818} 819 820// Test basic matching. We don't need to exhaustively test inputs since this 821// is tested in the agent/consul/state package. 822func TestIntentionMatch_good(t *testing.T) { 823 t.Parallel() 824 825 assert := assert.New(t) 826 dir1, s1 := testServer(t) 827 defer os.RemoveAll(dir1) 828 defer s1.Shutdown() 829 codec := rpcClient(t, s1) 830 defer codec.Close() 831 832 testrpc.WaitForLeader(t, s1.RPC, "dc1") 833 834 // Create some records 835 { 836 insert := [][]string{ 837 {"foo", "*", "foo", "*"}, 838 {"foo", "*", "foo", "bar"}, 839 {"foo", "*", "foo", "baz"}, // shouldn't match 840 {"foo", "*", "bar", "bar"}, // shouldn't match 841 {"foo", "*", "bar", "*"}, // shouldn't match 842 {"foo", "*", "*", "*"}, 843 {"bar", "*", "foo", "bar"}, // duplicate destination different source 844 } 845 846 for _, v := range insert { 847 ixn := structs.IntentionRequest{ 848 Datacenter: "dc1", 849 Op: structs.IntentionOpCreate, 850 Intention: &structs.Intention{ 851 SourceNS: v[0], 852 SourceName: v[1], 853 DestinationNS: v[2], 854 DestinationName: v[3], 855 Action: structs.IntentionActionAllow, 856 }, 857 } 858 859 // Create 860 var reply string 861 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 862 } 863 } 864 865 // Match 866 req := &structs.IntentionQueryRequest{ 867 Datacenter: "dc1", 868 Match: &structs.IntentionQueryMatch{ 869 Type: structs.IntentionMatchDestination, 870 Entries: []structs.IntentionMatchEntry{ 871 { 872 Namespace: "foo", 873 Name: "bar", 874 }, 875 }, 876 }, 877 } 878 var resp structs.IndexedIntentionMatches 879 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 880 assert.Len(resp.Matches, 1) 881 882 expected := [][]string{ 883 {"bar", "*", "foo", "bar"}, 884 {"foo", "*", "foo", "bar"}, 885 {"foo", "*", "foo", "*"}, 886 {"foo", "*", "*", "*"}, 887 } 888 var actual [][]string 889 for _, ixn := range resp.Matches[0] { 890 actual = append(actual, []string{ 891 ixn.SourceNS, 892 ixn.SourceName, 893 ixn.DestinationNS, 894 ixn.DestinationName, 895 }) 896 } 897 assert.Equal(expected, actual) 898} 899 900// Test matching with ACLs 901func TestIntentionMatch_acl(t *testing.T) { 902 t.Parallel() 903 904 assert := assert.New(t) 905 dir1, s1 := testServerWithConfig(t, func(c *Config) { 906 c.ACLDatacenter = "dc1" 907 c.ACLMasterToken = "root" 908 c.ACLDefaultPolicy = "deny" 909 }) 910 defer os.RemoveAll(dir1) 911 defer s1.Shutdown() 912 codec := rpcClient(t, s1) 913 defer codec.Close() 914 915 testrpc.WaitForLeader(t, s1.RPC, "dc1") 916 917 // Create an ACL with service write permissions. This will grant 918 // intentions read. 919 var token string 920 { 921 var rules = ` 922service "bar" { 923 policy = "write" 924}` 925 926 req := structs.ACLRequest{ 927 Datacenter: "dc1", 928 Op: structs.ACLSet, 929 ACL: structs.ACL{ 930 Name: "User token", 931 Type: structs.ACLTypeClient, 932 Rules: rules, 933 }, 934 WriteRequest: structs.WriteRequest{Token: "root"}, 935 } 936 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 937 } 938 939 // Create some records 940 { 941 insert := [][]string{ 942 {"foo", "*"}, 943 {"foo", "bar"}, 944 {"foo", "baz"}, // shouldn't match 945 {"bar", "bar"}, // shouldn't match 946 {"bar", "*"}, // shouldn't match 947 {"*", "*"}, 948 } 949 950 for _, v := range insert { 951 ixn := structs.IntentionRequest{ 952 Datacenter: "dc1", 953 Op: structs.IntentionOpCreate, 954 Intention: structs.TestIntention(t), 955 } 956 ixn.Intention.DestinationNS = v[0] 957 ixn.Intention.DestinationName = v[1] 958 ixn.WriteRequest.Token = "root" 959 960 // Create 961 var reply string 962 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 963 } 964 } 965 966 // Test with no token 967 { 968 req := &structs.IntentionQueryRequest{ 969 Datacenter: "dc1", 970 Match: &structs.IntentionQueryMatch{ 971 Type: structs.IntentionMatchDestination, 972 Entries: []structs.IntentionMatchEntry{ 973 { 974 Namespace: "foo", 975 Name: "bar", 976 }, 977 }, 978 }, 979 } 980 var resp structs.IndexedIntentionMatches 981 err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp) 982 assert.True(acl.IsErrPermissionDenied(err)) 983 assert.Len(resp.Matches, 0) 984 } 985 986 // Test with proper token 987 { 988 req := &structs.IntentionQueryRequest{ 989 Datacenter: "dc1", 990 Match: &structs.IntentionQueryMatch{ 991 Type: structs.IntentionMatchDestination, 992 Entries: []structs.IntentionMatchEntry{ 993 { 994 Namespace: "foo", 995 Name: "bar", 996 }, 997 }, 998 }, 999 QueryOptions: structs.QueryOptions{Token: token}, 1000 } 1001 var resp structs.IndexedIntentionMatches 1002 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 1003 assert.Len(resp.Matches, 1) 1004 1005 expected := [][]string{{"foo", "bar"}, {"foo", "*"}, {"*", "*"}} 1006 var actual [][]string 1007 for _, ixn := range resp.Matches[0] { 1008 actual = append(actual, []string{ixn.DestinationNS, ixn.DestinationName}) 1009 } 1010 1011 assert.Equal(expected, actual) 1012 } 1013} 1014 1015// Test the Check method defaults to allow with no ACL set. 1016func TestIntentionCheck_defaultNoACL(t *testing.T) { 1017 t.Parallel() 1018 1019 require := require.New(t) 1020 dir1, s1 := testServer(t) 1021 defer os.RemoveAll(dir1) 1022 defer s1.Shutdown() 1023 codec := rpcClient(t, s1) 1024 defer codec.Close() 1025 1026 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1027 1028 // Test 1029 req := &structs.IntentionQueryRequest{ 1030 Datacenter: "dc1", 1031 Check: &structs.IntentionQueryCheck{ 1032 SourceNS: "foo", 1033 SourceName: "bar", 1034 DestinationNS: "foo", 1035 DestinationName: "qux", 1036 SourceType: structs.IntentionSourceConsul, 1037 }, 1038 } 1039 var resp structs.IntentionQueryCheckResponse 1040 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1041 require.True(resp.Allowed) 1042} 1043 1044// Test the Check method defaults to deny with whitelist ACLs. 1045func TestIntentionCheck_defaultACLDeny(t *testing.T) { 1046 t.Parallel() 1047 1048 require := require.New(t) 1049 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1050 c.ACLDatacenter = "dc1" 1051 c.ACLMasterToken = "root" 1052 c.ACLDefaultPolicy = "deny" 1053 }) 1054 defer os.RemoveAll(dir1) 1055 defer s1.Shutdown() 1056 codec := rpcClient(t, s1) 1057 defer codec.Close() 1058 1059 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1060 1061 // Check 1062 req := &structs.IntentionQueryRequest{ 1063 Datacenter: "dc1", 1064 Check: &structs.IntentionQueryCheck{ 1065 SourceNS: "foo", 1066 SourceName: "bar", 1067 DestinationNS: "foo", 1068 DestinationName: "qux", 1069 SourceType: structs.IntentionSourceConsul, 1070 }, 1071 } 1072 req.Token = "root" 1073 var resp structs.IntentionQueryCheckResponse 1074 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1075 require.False(resp.Allowed) 1076} 1077 1078// Test the Check method defaults to deny with blacklist ACLs. 1079func TestIntentionCheck_defaultACLAllow(t *testing.T) { 1080 t.Parallel() 1081 1082 require := require.New(t) 1083 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1084 c.ACLDatacenter = "dc1" 1085 c.ACLMasterToken = "root" 1086 c.ACLDefaultPolicy = "allow" 1087 }) 1088 defer os.RemoveAll(dir1) 1089 defer s1.Shutdown() 1090 codec := rpcClient(t, s1) 1091 defer codec.Close() 1092 1093 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1094 1095 // Check 1096 req := &structs.IntentionQueryRequest{ 1097 Datacenter: "dc1", 1098 Check: &structs.IntentionQueryCheck{ 1099 SourceNS: "foo", 1100 SourceName: "bar", 1101 DestinationNS: "foo", 1102 DestinationName: "qux", 1103 SourceType: structs.IntentionSourceConsul, 1104 }, 1105 } 1106 req.Token = "root" 1107 var resp structs.IntentionQueryCheckResponse 1108 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1109 require.True(resp.Allowed) 1110} 1111 1112// Test the Check method requires service:read permission. 1113func TestIntentionCheck_aclDeny(t *testing.T) { 1114 t.Parallel() 1115 1116 require := require.New(t) 1117 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1118 c.ACLDatacenter = "dc1" 1119 c.ACLMasterToken = "root" 1120 c.ACLDefaultPolicy = "deny" 1121 }) 1122 defer os.RemoveAll(dir1) 1123 defer s1.Shutdown() 1124 codec := rpcClient(t, s1) 1125 defer codec.Close() 1126 1127 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1128 1129 // Create an ACL with service read permissions. This will grant permission. 1130 var token string 1131 { 1132 var rules = ` 1133service "bar" { 1134 policy = "read" 1135}` 1136 1137 req := structs.ACLRequest{ 1138 Datacenter: "dc1", 1139 Op: structs.ACLSet, 1140 ACL: structs.ACL{ 1141 Name: "User token", 1142 Type: structs.ACLTypeClient, 1143 Rules: rules, 1144 }, 1145 WriteRequest: structs.WriteRequest{Token: "root"}, 1146 } 1147 require.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 1148 } 1149 1150 // Check 1151 req := &structs.IntentionQueryRequest{ 1152 Datacenter: "dc1", 1153 Check: &structs.IntentionQueryCheck{ 1154 SourceNS: "foo", 1155 SourceName: "qux", 1156 DestinationNS: "foo", 1157 DestinationName: "baz", 1158 SourceType: structs.IntentionSourceConsul, 1159 }, 1160 } 1161 req.Token = token 1162 var resp structs.IntentionQueryCheckResponse 1163 err := msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp) 1164 require.True(acl.IsErrPermissionDenied(err)) 1165} 1166 1167// Test the Check method returns allow/deny properly. 1168func TestIntentionCheck_match(t *testing.T) { 1169 t.Parallel() 1170 1171 require := require.New(t) 1172 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1173 c.ACLDatacenter = "dc1" 1174 c.ACLMasterToken = "root" 1175 c.ACLDefaultPolicy = "deny" 1176 }) 1177 defer os.RemoveAll(dir1) 1178 defer s1.Shutdown() 1179 codec := rpcClient(t, s1) 1180 defer codec.Close() 1181 1182 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1183 1184 // Create an ACL with service read permissions. This will grant permission. 1185 var token string 1186 { 1187 var rules = ` 1188service "bar" { 1189 policy = "read" 1190}` 1191 1192 req := structs.ACLRequest{ 1193 Datacenter: "dc1", 1194 Op: structs.ACLSet, 1195 ACL: structs.ACL{ 1196 Name: "User token", 1197 Type: structs.ACLTypeClient, 1198 Rules: rules, 1199 }, 1200 WriteRequest: structs.WriteRequest{Token: "root"}, 1201 } 1202 require.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 1203 } 1204 1205 // Create some intentions 1206 { 1207 insert := [][]string{ 1208 {"foo", "*", "foo", "*"}, 1209 {"foo", "*", "foo", "bar"}, 1210 {"bar", "*", "foo", "bar"}, // duplicate destination different source 1211 } 1212 1213 for _, v := range insert { 1214 ixn := structs.IntentionRequest{ 1215 Datacenter: "dc1", 1216 Op: structs.IntentionOpCreate, 1217 Intention: &structs.Intention{ 1218 SourceNS: v[0], 1219 SourceName: v[1], 1220 DestinationNS: v[2], 1221 DestinationName: v[3], 1222 Action: structs.IntentionActionAllow, 1223 }, 1224 } 1225 ixn.WriteRequest.Token = "root" 1226 1227 // Create 1228 var reply string 1229 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1230 } 1231 } 1232 1233 // Check 1234 req := &structs.IntentionQueryRequest{ 1235 Datacenter: "dc1", 1236 Check: &structs.IntentionQueryCheck{ 1237 SourceNS: "foo", 1238 SourceName: "qux", 1239 DestinationNS: "foo", 1240 DestinationName: "bar", 1241 SourceType: structs.IntentionSourceConsul, 1242 }, 1243 } 1244 req.Token = token 1245 var resp structs.IntentionQueryCheckResponse 1246 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1247 require.True(resp.Allowed) 1248 1249 // Test no match for sanity 1250 { 1251 req := &structs.IntentionQueryRequest{ 1252 Datacenter: "dc1", 1253 Check: &structs.IntentionQueryCheck{ 1254 SourceNS: "baz", 1255 SourceName: "qux", 1256 DestinationNS: "foo", 1257 DestinationName: "bar", 1258 SourceType: structs.IntentionSourceConsul, 1259 }, 1260 } 1261 req.Token = token 1262 var resp structs.IntentionQueryCheckResponse 1263 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1264 require.False(resp.Allowed) 1265 } 1266} 1267