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 msgpackrpc "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 actual.Hash = ixn.Intention.Hash 71 ixn.Intention.UpdatePrecedence() 72 assert.Equal(ixn.Intention, actual) 73 } 74} 75 76// Test the source type defaults 77func TestIntentionApply_defaultSourceType(t *testing.T) { 78 t.Parallel() 79 80 assert := assert.New(t) 81 dir1, s1 := testServer(t) 82 defer os.RemoveAll(dir1) 83 defer s1.Shutdown() 84 codec := rpcClient(t, s1) 85 defer codec.Close() 86 87 testrpc.WaitForLeader(t, s1.RPC, "dc1") 88 89 // Setup a basic record to create 90 ixn := structs.IntentionRequest{ 91 Datacenter: "dc1", 92 Op: structs.IntentionOpCreate, 93 Intention: &structs.Intention{ 94 SourceNS: structs.IntentionDefaultNamespace, 95 SourceName: "test", 96 DestinationNS: structs.IntentionDefaultNamespace, 97 DestinationName: "test", 98 Action: structs.IntentionActionAllow, 99 }, 100 } 101 var reply string 102 103 // Create 104 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 105 assert.NotEmpty(reply) 106 107 // Read 108 ixn.Intention.ID = reply 109 { 110 req := &structs.IntentionQueryRequest{ 111 Datacenter: "dc1", 112 IntentionID: ixn.Intention.ID, 113 } 114 var resp structs.IndexedIntentions 115 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 116 assert.Len(resp.Intentions, 1) 117 actual := resp.Intentions[0] 118 assert.Equal(structs.IntentionSourceConsul, actual.SourceType) 119 } 120} 121 122// Shouldn't be able to create with an ID set 123func TestIntentionApply_createWithID(t *testing.T) { 124 t.Parallel() 125 126 assert := assert.New(t) 127 dir1, s1 := testServer(t) 128 defer os.RemoveAll(dir1) 129 defer s1.Shutdown() 130 codec := rpcClient(t, s1) 131 defer codec.Close() 132 133 testrpc.WaitForLeader(t, s1.RPC, "dc1") 134 135 // Setup a basic record to create 136 ixn := structs.IntentionRequest{ 137 Datacenter: "dc1", 138 Op: structs.IntentionOpCreate, 139 Intention: &structs.Intention{ 140 ID: generateUUID(), 141 SourceName: "test", 142 }, 143 } 144 var reply string 145 146 // Create 147 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 148 assert.NotNil(err) 149 assert.Contains(err, "ID must be empty") 150} 151 152// Test basic updating 153func TestIntentionApply_updateGood(t *testing.T) { 154 t.Parallel() 155 156 assert := assert.New(t) 157 dir1, s1 := testServer(t) 158 defer os.RemoveAll(dir1) 159 defer s1.Shutdown() 160 codec := rpcClient(t, s1) 161 defer codec.Close() 162 163 testrpc.WaitForLeader(t, s1.RPC, "dc1") 164 165 // Setup a basic record to create 166 ixn := structs.IntentionRequest{ 167 Datacenter: "dc1", 168 Op: structs.IntentionOpCreate, 169 Intention: &structs.Intention{ 170 SourceNS: structs.IntentionDefaultNamespace, 171 SourceName: "test", 172 DestinationNS: structs.IntentionDefaultNamespace, 173 DestinationName: "test", 174 Action: structs.IntentionActionAllow, 175 SourceType: structs.IntentionSourceConsul, 176 Meta: map[string]string{}, 177 }, 178 } 179 var reply string 180 181 // Create 182 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 183 assert.NotEmpty(reply) 184 185 // Read CreatedAt 186 var createdAt time.Time 187 ixn.Intention.ID = reply 188 { 189 req := &structs.IntentionQueryRequest{ 190 Datacenter: "dc1", 191 IntentionID: ixn.Intention.ID, 192 } 193 var resp structs.IndexedIntentions 194 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 195 assert.Len(resp.Intentions, 1) 196 actual := resp.Intentions[0] 197 createdAt = actual.CreatedAt 198 } 199 200 // Sleep a bit so that the updated at will definitely be different, not much 201 time.Sleep(1 * time.Millisecond) 202 203 // Update 204 ixn.Op = structs.IntentionOpUpdate 205 ixn.Intention.ID = reply 206 ixn.Intention.SourceName = "*" 207 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 208 209 // Read 210 ixn.Intention.ID = reply 211 { 212 req := &structs.IntentionQueryRequest{ 213 Datacenter: "dc1", 214 IntentionID: ixn.Intention.ID, 215 } 216 var resp structs.IndexedIntentions 217 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 218 assert.Len(resp.Intentions, 1) 219 actual := resp.Intentions[0] 220 assert.Equal(createdAt, actual.CreatedAt) 221 assert.WithinDuration(time.Now(), actual.UpdatedAt, 5*time.Second) 222 223 actual.CreateIndex, actual.ModifyIndex = 0, 0 224 actual.CreatedAt = ixn.Intention.CreatedAt 225 actual.UpdatedAt = ixn.Intention.UpdatedAt 226 actual.Hash = ixn.Intention.Hash 227 ixn.Intention.UpdatePrecedence() 228 assert.Equal(ixn.Intention, actual) 229 } 230} 231 232// Shouldn't be able to update a non-existent intention 233func TestIntentionApply_updateNonExist(t *testing.T) { 234 t.Parallel() 235 236 assert := assert.New(t) 237 dir1, s1 := testServer(t) 238 defer os.RemoveAll(dir1) 239 defer s1.Shutdown() 240 codec := rpcClient(t, s1) 241 defer codec.Close() 242 243 testrpc.WaitForLeader(t, s1.RPC, "dc1") 244 245 // Setup a basic record to create 246 ixn := structs.IntentionRequest{ 247 Datacenter: "dc1", 248 Op: structs.IntentionOpUpdate, 249 Intention: &structs.Intention{ 250 ID: generateUUID(), 251 SourceName: "test", 252 }, 253 } 254 var reply string 255 256 // Create 257 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 258 assert.NotNil(err) 259 assert.Contains(err, "Cannot modify non-existent intention") 260} 261 262// Test basic deleting 263func TestIntentionApply_deleteGood(t *testing.T) { 264 t.Parallel() 265 266 assert := assert.New(t) 267 dir1, s1 := testServer(t) 268 defer os.RemoveAll(dir1) 269 defer s1.Shutdown() 270 codec := rpcClient(t, s1) 271 defer codec.Close() 272 273 testrpc.WaitForLeader(t, s1.RPC, "dc1") 274 275 // Setup a basic record to create 276 ixn := structs.IntentionRequest{ 277 Datacenter: "dc1", 278 Op: structs.IntentionOpCreate, 279 Intention: &structs.Intention{ 280 SourceNS: "test", 281 SourceName: "test", 282 DestinationNS: "test", 283 DestinationName: "test", 284 Action: structs.IntentionActionAllow, 285 }, 286 } 287 var reply string 288 289 // Create 290 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 291 assert.NotEmpty(reply) 292 293 // Delete 294 ixn.Op = structs.IntentionOpDelete 295 ixn.Intention.ID = reply 296 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 297 298 // Read 299 ixn.Intention.ID = reply 300 { 301 req := &structs.IntentionQueryRequest{ 302 Datacenter: "dc1", 303 IntentionID: ixn.Intention.ID, 304 } 305 var resp structs.IndexedIntentions 306 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 307 assert.NotNil(err) 308 assert.Contains(err, ErrIntentionNotFound.Error()) 309 } 310} 311 312// Test apply with a deny ACL 313func TestIntentionApply_aclDeny(t *testing.T) { 314 t.Parallel() 315 316 assert := assert.New(t) 317 dir1, s1 := testServerWithConfig(t, func(c *Config) { 318 c.ACLDatacenter = "dc1" 319 c.ACLsEnabled = true 320 c.ACLMasterToken = "root" 321 c.ACLDefaultPolicy = "deny" 322 }) 323 defer os.RemoveAll(dir1) 324 defer s1.Shutdown() 325 codec := rpcClient(t, s1) 326 defer codec.Close() 327 328 testrpc.WaitForLeader(t, s1.RPC, "dc1") 329 330 // Create an ACL with write permissions 331 var token string 332 { 333 var rules = ` 334service "foo" { 335 policy = "deny" 336 intentions = "write" 337}` 338 339 req := structs.ACLRequest{ 340 Datacenter: "dc1", 341 Op: structs.ACLSet, 342 ACL: structs.ACL{ 343 Name: "User token", 344 Type: structs.ACLTokenTypeClient, 345 Rules: rules, 346 }, 347 WriteRequest: structs.WriteRequest{Token: "root"}, 348 } 349 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 350 } 351 352 // Setup a basic record to create 353 ixn := structs.IntentionRequest{ 354 Datacenter: "dc1", 355 Op: structs.IntentionOpCreate, 356 Intention: structs.TestIntention(t), 357 } 358 ixn.Intention.DestinationName = "foobar" 359 360 // Create without a token should error since default deny 361 var reply string 362 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 363 assert.True(acl.IsErrPermissionDenied(err)) 364 365 // Now add the token and try again. 366 ixn.WriteRequest.Token = token 367 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 368 369 // Read 370 ixn.Intention.ID = reply 371 { 372 req := &structs.IntentionQueryRequest{ 373 Datacenter: "dc1", 374 IntentionID: ixn.Intention.ID, 375 QueryOptions: structs.QueryOptions{Token: "root"}, 376 } 377 var resp structs.IndexedIntentions 378 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 379 assert.Len(resp.Intentions, 1) 380 actual := resp.Intentions[0] 381 assert.Equal(resp.Index, actual.ModifyIndex) 382 383 actual.CreateIndex, actual.ModifyIndex = 0, 0 384 actual.CreatedAt = ixn.Intention.CreatedAt 385 actual.UpdatedAt = ixn.Intention.UpdatedAt 386 actual.Hash = ixn.Intention.Hash 387 ixn.Intention.UpdatePrecedence() 388 assert.Equal(ixn.Intention, actual) 389 } 390} 391 392func TestIntention_WildcardACLEnforcement(t *testing.T) { 393 t.Parallel() 394 395 _, srv, codec := testACLServerWithConfig(t, nil, false) 396 waitForLeaderEstablishment(t, srv) 397 398 // create some test policies. 399 400 writeToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service_prefix "" { policy = "deny" intentions = "write" }`) 401 require.NoError(t, err) 402 readToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service_prefix "" { policy = "deny" intentions = "read" }`) 403 require.NoError(t, err) 404 exactToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service "*" { policy = "deny" intentions = "write" }`) 405 require.NoError(t, err) 406 wildcardPrefixToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service_prefix "*" { policy = "deny" intentions = "write" }`) 407 require.NoError(t, err) 408 fooToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service "foo" { policy = "deny" intentions = "write" }`) 409 require.NoError(t, err) 410 denyToken, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service_prefix "" { policy = "deny" intentions = "deny" }`) 411 require.NoError(t, err) 412 413 doIntentionCreate := func(t *testing.T, token string, deny bool) string { 414 t.Helper() 415 ixn := structs.IntentionRequest{ 416 Datacenter: "dc1", 417 Op: structs.IntentionOpCreate, 418 Intention: &structs.Intention{ 419 SourceNS: "default", 420 SourceName: "*", 421 DestinationNS: "default", 422 DestinationName: "*", 423 Action: structs.IntentionActionAllow, 424 SourceType: structs.IntentionSourceConsul, 425 }, 426 WriteRequest: structs.WriteRequest{Token: token}, 427 } 428 var reply string 429 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 430 if deny { 431 require.Error(t, err) 432 require.True(t, acl.IsErrPermissionDenied(err)) 433 return "" 434 } else { 435 require.NoError(t, err) 436 require.NotEmpty(t, reply) 437 return reply 438 } 439 } 440 441 t.Run("deny-write-for-read-token", func(t *testing.T) { 442 // This tests ensures that tokens with only read access to all intentions 443 // cannot create a wildcard intention 444 doIntentionCreate(t, readToken.SecretID, true) 445 }) 446 447 t.Run("deny-write-for-exact-wildcard-rule", func(t *testing.T) { 448 // This test ensures that having a rules like: 449 // service "*" { 450 // intentions = "write" 451 // } 452 // will not actually allow creating an intention with a wildcard service name 453 doIntentionCreate(t, exactToken.SecretID, true) 454 }) 455 456 t.Run("deny-write-for-prefix-wildcard-rule", func(t *testing.T) { 457 // This test ensures that having a rules like: 458 // service_prefix "*" { 459 // intentions = "write" 460 // } 461 // will not actually allow creating an intention with a wildcard service name 462 doIntentionCreate(t, wildcardPrefixToken.SecretID, true) 463 }) 464 465 var intentionID string 466 allowWriteOk := t.Run("allow-write", func(t *testing.T) { 467 // tests that a token with all the required privileges can create 468 // intentions with a wildcard destination 469 intentionID = doIntentionCreate(t, writeToken.SecretID, false) 470 }) 471 472 requireAllowWrite := func(t *testing.T) { 473 t.Helper() 474 if !allowWriteOk { 475 t.Skip("Skipping because the allow-write subtest failed") 476 } 477 } 478 479 doIntentionRead := func(t *testing.T, token string, deny bool) { 480 t.Helper() 481 requireAllowWrite(t) 482 req := &structs.IntentionQueryRequest{ 483 Datacenter: "dc1", 484 IntentionID: intentionID, 485 QueryOptions: structs.QueryOptions{Token: token}, 486 } 487 488 var resp structs.IndexedIntentions 489 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 490 if deny { 491 require.Error(t, err) 492 require.True(t, acl.IsErrPermissionDenied(err)) 493 } else { 494 require.NoError(t, err) 495 require.Len(t, resp.Intentions, 1) 496 require.Equal(t, "*", resp.Intentions[0].DestinationName) 497 } 498 } 499 500 t.Run("allow-read-for-write-token", func(t *testing.T) { 501 doIntentionRead(t, writeToken.SecretID, false) 502 }) 503 504 t.Run("allow-read-for-read-token", func(t *testing.T) { 505 doIntentionRead(t, readToken.SecretID, false) 506 }) 507 508 t.Run("allow-read-for-exact-wildcard-token", func(t *testing.T) { 509 // this is allowed because, the effect of the policy is to grant 510 // intention:write on the service named "*". When reading the 511 // intention we will validate that the token has read permissions 512 // for any intention that would match the wildcard. 513 doIntentionRead(t, exactToken.SecretID, false) 514 }) 515 516 t.Run("allow-read-for-prefix-wildcard-token", func(t *testing.T) { 517 // this is allowed for the same reasons as for the 518 // exact-wildcard-token case 519 doIntentionRead(t, wildcardPrefixToken.SecretID, false) 520 }) 521 522 t.Run("deny-read-for-deny-token", func(t *testing.T) { 523 doIntentionRead(t, denyToken.SecretID, true) 524 }) 525 526 doIntentionList := func(t *testing.T, token string, deny bool) { 527 t.Helper() 528 requireAllowWrite(t) 529 req := &structs.DCSpecificRequest{ 530 Datacenter: "dc1", 531 QueryOptions: structs.QueryOptions{Token: token}, 532 } 533 534 var resp structs.IndexedIntentions 535 err := msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp) 536 // even with permission denied this should return success but with an empty list 537 require.NoError(t, err) 538 if deny { 539 require.Empty(t, resp.Intentions) 540 } else { 541 require.Len(t, resp.Intentions, 1) 542 require.Equal(t, "*", resp.Intentions[0].DestinationName) 543 } 544 } 545 546 t.Run("allow-list-for-write-token", func(t *testing.T) { 547 doIntentionList(t, writeToken.SecretID, false) 548 }) 549 550 t.Run("allow-list-for-read-token", func(t *testing.T) { 551 doIntentionList(t, readToken.SecretID, false) 552 }) 553 554 t.Run("allow-list-for-exact-wildcard-token", func(t *testing.T) { 555 doIntentionList(t, exactToken.SecretID, false) 556 }) 557 558 t.Run("allow-list-for-prefix-wildcard-token", func(t *testing.T) { 559 doIntentionList(t, wildcardPrefixToken.SecretID, false) 560 }) 561 562 t.Run("deny-list-for-deny-token", func(t *testing.T) { 563 doIntentionList(t, denyToken.SecretID, true) 564 }) 565 566 doIntentionMatch := func(t *testing.T, token string, deny bool) { 567 t.Helper() 568 requireAllowWrite(t) 569 req := &structs.IntentionQueryRequest{ 570 Datacenter: "dc1", 571 Match: &structs.IntentionQueryMatch{ 572 Type: structs.IntentionMatchDestination, 573 Entries: []structs.IntentionMatchEntry{ 574 structs.IntentionMatchEntry{ 575 Namespace: "default", 576 Name: "*", 577 }, 578 }, 579 }, 580 QueryOptions: structs.QueryOptions{Token: token}, 581 } 582 583 var resp structs.IndexedIntentionMatches 584 err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp) 585 if deny { 586 require.Error(t, err) 587 require.Empty(t, resp.Matches) 588 } else { 589 require.NoError(t, err) 590 require.Len(t, resp.Matches, 1) 591 require.Len(t, resp.Matches[0], 1) 592 require.Equal(t, "*", resp.Matches[0][0].DestinationName) 593 } 594 } 595 596 t.Run("allow-match-for-write-token", func(t *testing.T) { 597 doIntentionMatch(t, writeToken.SecretID, false) 598 }) 599 600 t.Run("allow-match-for-read-token", func(t *testing.T) { 601 doIntentionMatch(t, readToken.SecretID, false) 602 }) 603 604 t.Run("allow-match-for-exact-wildcard-token", func(t *testing.T) { 605 doIntentionMatch(t, exactToken.SecretID, false) 606 }) 607 608 t.Run("allow-match-for-prefix-wildcard-token", func(t *testing.T) { 609 doIntentionMatch(t, wildcardPrefixToken.SecretID, false) 610 }) 611 612 t.Run("deny-match-for-deny-token", func(t *testing.T) { 613 doIntentionMatch(t, denyToken.SecretID, true) 614 }) 615 616 doIntentionUpdate := func(t *testing.T, token string, dest string, deny bool) { 617 t.Helper() 618 requireAllowWrite(t) 619 ixn := structs.IntentionRequest{ 620 Datacenter: "dc1", 621 Op: structs.IntentionOpUpdate, 622 Intention: &structs.Intention{ 623 ID: intentionID, 624 SourceNS: "default", 625 SourceName: "*", 626 DestinationNS: "default", 627 DestinationName: dest, 628 Action: structs.IntentionActionAllow, 629 SourceType: structs.IntentionSourceConsul, 630 }, 631 WriteRequest: structs.WriteRequest{Token: token}, 632 } 633 var reply string 634 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 635 if deny { 636 require.Error(t, err) 637 require.True(t, acl.IsErrPermissionDenied(err)) 638 } else { 639 require.NoError(t, err) 640 } 641 } 642 643 t.Run("deny-update-for-foo-token", func(t *testing.T) { 644 doIntentionUpdate(t, fooToken.SecretID, "foo", true) 645 }) 646 647 t.Run("allow-update-for-prefix-token", func(t *testing.T) { 648 // this tests that regardless of going from a wildcard intention 649 // to a non-wildcard or the opposite direction that the permissions 650 // are checked correctly. This also happens to leave the intention 651 // in a state ready for verifying similar things with deletion 652 doIntentionUpdate(t, writeToken.SecretID, "foo", false) 653 doIntentionUpdate(t, writeToken.SecretID, "*", false) 654 }) 655 656 doIntentionDelete := func(t *testing.T, token string, deny bool) { 657 t.Helper() 658 requireAllowWrite(t) 659 ixn := structs.IntentionRequest{ 660 Datacenter: "dc1", 661 Op: structs.IntentionOpDelete, 662 Intention: &structs.Intention{ 663 ID: intentionID, 664 }, 665 WriteRequest: structs.WriteRequest{Token: token}, 666 } 667 var reply string 668 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 669 if deny { 670 require.Error(t, err) 671 require.True(t, acl.IsErrPermissionDenied(err)) 672 } else { 673 require.NoError(t, err) 674 } 675 } 676 677 t.Run("deny-delete-for-read-token", func(t *testing.T) { 678 doIntentionDelete(t, readToken.SecretID, true) 679 }) 680 681 t.Run("deny-delete-for-exact-wildcard-rule", func(t *testing.T) { 682 // This test ensures that having a rules like: 683 // service "*" { 684 // intentions = "write" 685 // } 686 // will not actually allow deleting an intention with a wildcard service name 687 doIntentionDelete(t, exactToken.SecretID, true) 688 }) 689 690 t.Run("deny-delete-for-prefix-wildcard-rule", func(t *testing.T) { 691 // This test ensures that having a rules like: 692 // service_prefix "*" { 693 // intentions = "write" 694 // } 695 // will not actually allow creating an intention with a wildcard service name 696 doIntentionDelete(t, wildcardPrefixToken.SecretID, true) 697 }) 698 699 t.Run("allow-delete", func(t *testing.T) { 700 // tests that a token with all the required privileges can delete 701 // intentions with a wildcard destination 702 doIntentionDelete(t, writeToken.SecretID, false) 703 }) 704} 705 706// Test apply with delete and a default deny ACL 707func TestIntentionApply_aclDelete(t *testing.T) { 708 t.Parallel() 709 710 assert := assert.New(t) 711 dir1, s1 := testServerWithConfig(t, func(c *Config) { 712 c.ACLDatacenter = "dc1" 713 c.ACLsEnabled = true 714 c.ACLMasterToken = "root" 715 c.ACLDefaultPolicy = "deny" 716 }) 717 defer os.RemoveAll(dir1) 718 defer s1.Shutdown() 719 codec := rpcClient(t, s1) 720 defer codec.Close() 721 722 testrpc.WaitForLeader(t, s1.RPC, "dc1") 723 724 // Create an ACL with write permissions 725 var token string 726 { 727 var rules = ` 728service "foo" { 729 policy = "deny" 730 intentions = "write" 731}` 732 733 req := structs.ACLRequest{ 734 Datacenter: "dc1", 735 Op: structs.ACLSet, 736 ACL: structs.ACL{ 737 Name: "User token", 738 Type: structs.ACLTokenTypeClient, 739 Rules: rules, 740 }, 741 WriteRequest: structs.WriteRequest{Token: "root"}, 742 } 743 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 744 } 745 746 // Setup a basic record to create 747 ixn := structs.IntentionRequest{ 748 Datacenter: "dc1", 749 Op: structs.IntentionOpCreate, 750 Intention: structs.TestIntention(t), 751 } 752 ixn.Intention.DestinationName = "foobar" 753 ixn.WriteRequest.Token = token 754 755 // Create 756 var reply string 757 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 758 759 // Try to do a delete with no token; this should get rejected. 760 ixn.Op = structs.IntentionOpDelete 761 ixn.Intention.ID = reply 762 ixn.WriteRequest.Token = "" 763 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 764 assert.True(acl.IsErrPermissionDenied(err)) 765 766 // Try again with the original token. This should go through. 767 ixn.WriteRequest.Token = token 768 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 769 770 // Verify it is gone 771 { 772 req := &structs.IntentionQueryRequest{ 773 Datacenter: "dc1", 774 IntentionID: ixn.Intention.ID, 775 } 776 var resp structs.IndexedIntentions 777 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 778 assert.NotNil(err) 779 assert.Contains(err.Error(), ErrIntentionNotFound.Error()) 780 } 781} 782 783// Test apply with update and a default deny ACL 784func TestIntentionApply_aclUpdate(t *testing.T) { 785 t.Parallel() 786 787 assert := assert.New(t) 788 dir1, s1 := testServerWithConfig(t, func(c *Config) { 789 c.ACLDatacenter = "dc1" 790 c.ACLsEnabled = true 791 c.ACLMasterToken = "root" 792 c.ACLDefaultPolicy = "deny" 793 }) 794 defer os.RemoveAll(dir1) 795 defer s1.Shutdown() 796 codec := rpcClient(t, s1) 797 defer codec.Close() 798 799 testrpc.WaitForLeader(t, s1.RPC, "dc1") 800 801 // Create an ACL with write permissions 802 var token string 803 { 804 var rules = ` 805service "foo" { 806 policy = "deny" 807 intentions = "write" 808}` 809 810 req := structs.ACLRequest{ 811 Datacenter: "dc1", 812 Op: structs.ACLSet, 813 ACL: structs.ACL{ 814 Name: "User token", 815 Type: structs.ACLTokenTypeClient, 816 Rules: rules, 817 }, 818 WriteRequest: structs.WriteRequest{Token: "root"}, 819 } 820 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 821 } 822 823 // Setup a basic record to create 824 ixn := structs.IntentionRequest{ 825 Datacenter: "dc1", 826 Op: structs.IntentionOpCreate, 827 Intention: structs.TestIntention(t), 828 } 829 ixn.Intention.DestinationName = "foobar" 830 ixn.WriteRequest.Token = token 831 832 // Create 833 var reply string 834 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 835 836 // Try to do an update without a token; this should get rejected. 837 ixn.Op = structs.IntentionOpUpdate 838 ixn.Intention.ID = reply 839 ixn.WriteRequest.Token = "" 840 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 841 assert.True(acl.IsErrPermissionDenied(err)) 842 843 // Try again with the original token; this should go through. 844 ixn.WriteRequest.Token = token 845 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 846} 847 848// Test apply with a management token 849func TestIntentionApply_aclManagement(t *testing.T) { 850 t.Parallel() 851 852 assert := assert.New(t) 853 dir1, s1 := testServerWithConfig(t, func(c *Config) { 854 c.ACLDatacenter = "dc1" 855 c.ACLsEnabled = true 856 c.ACLMasterToken = "root" 857 c.ACLDefaultPolicy = "deny" 858 }) 859 defer os.RemoveAll(dir1) 860 defer s1.Shutdown() 861 codec := rpcClient(t, s1) 862 defer codec.Close() 863 864 testrpc.WaitForLeader(t, s1.RPC, "dc1") 865 866 // Setup a basic record to create 867 ixn := structs.IntentionRequest{ 868 Datacenter: "dc1", 869 Op: structs.IntentionOpCreate, 870 Intention: structs.TestIntention(t), 871 } 872 ixn.Intention.DestinationName = "foobar" 873 ixn.WriteRequest.Token = "root" 874 875 // Create 876 var reply string 877 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 878 ixn.Intention.ID = reply 879 880 // Update 881 ixn.Op = structs.IntentionOpUpdate 882 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 883 884 // Delete 885 ixn.Op = structs.IntentionOpDelete 886 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 887} 888 889// Test update changing the name where an ACL won't allow it 890func TestIntentionApply_aclUpdateChange(t *testing.T) { 891 t.Parallel() 892 893 assert := assert.New(t) 894 dir1, s1 := testServerWithConfig(t, func(c *Config) { 895 c.ACLDatacenter = "dc1" 896 c.ACLsEnabled = true 897 c.ACLMasterToken = "root" 898 c.ACLDefaultPolicy = "deny" 899 }) 900 defer os.RemoveAll(dir1) 901 defer s1.Shutdown() 902 codec := rpcClient(t, s1) 903 defer codec.Close() 904 905 testrpc.WaitForLeader(t, s1.RPC, "dc1") 906 907 // Create an ACL with write permissions 908 var token string 909 { 910 var rules = ` 911service "foo" { 912 policy = "deny" 913 intentions = "write" 914}` 915 916 req := structs.ACLRequest{ 917 Datacenter: "dc1", 918 Op: structs.ACLSet, 919 ACL: structs.ACL{ 920 Name: "User token", 921 Type: structs.ACLTokenTypeClient, 922 Rules: rules, 923 }, 924 WriteRequest: structs.WriteRequest{Token: "root"}, 925 } 926 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 927 } 928 929 // Setup a basic record to create 930 ixn := structs.IntentionRequest{ 931 Datacenter: "dc1", 932 Op: structs.IntentionOpCreate, 933 Intention: structs.TestIntention(t), 934 } 935 ixn.Intention.DestinationName = "bar" 936 ixn.WriteRequest.Token = "root" 937 938 // Create 939 var reply string 940 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 941 942 // Try to do an update without a token; this should get rejected. 943 ixn.Op = structs.IntentionOpUpdate 944 ixn.Intention.ID = reply 945 ixn.Intention.DestinationName = "foo" 946 ixn.WriteRequest.Token = token 947 err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply) 948 assert.True(acl.IsErrPermissionDenied(err)) 949} 950 951// Test reading with ACLs 952func TestIntentionGet_acl(t *testing.T) { 953 t.Parallel() 954 955 assert := assert.New(t) 956 dir1, s1 := testServerWithConfig(t, func(c *Config) { 957 c.ACLDatacenter = "dc1" 958 c.ACLsEnabled = true 959 c.ACLMasterToken = "root" 960 c.ACLDefaultPolicy = "deny" 961 }) 962 defer os.RemoveAll(dir1) 963 defer s1.Shutdown() 964 codec := rpcClient(t, s1) 965 defer codec.Close() 966 967 testrpc.WaitForLeader(t, s1.RPC, "dc1") 968 969 // Create an ACL with service write permissions. This will grant 970 // intentions read. 971 var token string 972 { 973 var rules = ` 974service "foo" { 975 policy = "write" 976}` 977 978 req := structs.ACLRequest{ 979 Datacenter: "dc1", 980 Op: structs.ACLSet, 981 ACL: structs.ACL{ 982 Name: "User token", 983 Type: structs.ACLTokenTypeClient, 984 Rules: rules, 985 }, 986 WriteRequest: structs.WriteRequest{Token: "root"}, 987 } 988 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 989 } 990 991 // Setup a basic record to create 992 ixn := structs.IntentionRequest{ 993 Datacenter: "dc1", 994 Op: structs.IntentionOpCreate, 995 Intention: structs.TestIntention(t), 996 } 997 ixn.Intention.DestinationName = "foobar" 998 ixn.WriteRequest.Token = "root" 999 1000 // Create 1001 var reply string 1002 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1003 ixn.Intention.ID = reply 1004 1005 // Read without token should be error 1006 { 1007 req := &structs.IntentionQueryRequest{ 1008 Datacenter: "dc1", 1009 IntentionID: ixn.Intention.ID, 1010 } 1011 1012 var resp structs.IndexedIntentions 1013 err := msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp) 1014 assert.True(acl.IsErrPermissionDenied(err)) 1015 assert.Len(resp.Intentions, 0) 1016 } 1017 1018 // Read with token should work 1019 { 1020 req := &structs.IntentionQueryRequest{ 1021 Datacenter: "dc1", 1022 IntentionID: ixn.Intention.ID, 1023 QueryOptions: structs.QueryOptions{Token: token}, 1024 } 1025 1026 var resp structs.IndexedIntentions 1027 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Get", req, &resp)) 1028 assert.Len(resp.Intentions, 1) 1029 } 1030} 1031 1032func TestIntentionList(t *testing.T) { 1033 t.Parallel() 1034 1035 assert := assert.New(t) 1036 dir1, s1 := testServer(t) 1037 defer os.RemoveAll(dir1) 1038 defer s1.Shutdown() 1039 1040 codec := rpcClient(t, s1) 1041 defer codec.Close() 1042 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1043 1044 // Test with no intentions inserted yet 1045 { 1046 req := &structs.DCSpecificRequest{ 1047 Datacenter: "dc1", 1048 } 1049 var resp structs.IndexedIntentions 1050 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 1051 assert.NotNil(resp.Intentions) 1052 assert.Len(resp.Intentions, 0) 1053 } 1054} 1055 1056// Test listing with ACLs 1057func TestIntentionList_acl(t *testing.T) { 1058 t.Parallel() 1059 1060 dir1, s1 := testServerWithConfig(t, testServerACLConfig(nil)) 1061 defer os.RemoveAll(dir1) 1062 defer s1.Shutdown() 1063 codec := rpcClient(t, s1) 1064 defer codec.Close() 1065 1066 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1067 waitForNewACLs(t, s1) 1068 1069 token, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service_prefix "foo" { policy = "write" }`) 1070 require.NoError(t, err) 1071 1072 // Create a few records 1073 for _, name := range []string{"foobar", "bar", "baz"} { 1074 ixn := structs.IntentionRequest{ 1075 Datacenter: "dc1", 1076 Op: structs.IntentionOpCreate, 1077 Intention: structs.TestIntention(t), 1078 } 1079 ixn.Intention.SourceNS = "default" 1080 ixn.Intention.DestinationNS = "default" 1081 ixn.Intention.DestinationName = name 1082 ixn.WriteRequest.Token = TestDefaultMasterToken 1083 1084 // Create 1085 var reply string 1086 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1087 } 1088 1089 // Test with no token 1090 t.Run("no-token", func(t *testing.T) { 1091 req := &structs.DCSpecificRequest{ 1092 Datacenter: "dc1", 1093 } 1094 var resp structs.IndexedIntentions 1095 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 1096 require.Len(t, resp.Intentions, 0) 1097 }) 1098 1099 // Test with management token 1100 t.Run("master-token", func(t *testing.T) { 1101 req := &structs.DCSpecificRequest{ 1102 Datacenter: "dc1", 1103 QueryOptions: structs.QueryOptions{Token: TestDefaultMasterToken}, 1104 } 1105 var resp structs.IndexedIntentions 1106 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 1107 require.Len(t, resp.Intentions, 3) 1108 }) 1109 1110 // Test with user token 1111 t.Run("user-token", func(t *testing.T) { 1112 req := &structs.DCSpecificRequest{ 1113 Datacenter: "dc1", 1114 QueryOptions: structs.QueryOptions{Token: token.SecretID}, 1115 } 1116 var resp structs.IndexedIntentions 1117 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 1118 require.Len(t, resp.Intentions, 1) 1119 }) 1120 1121 t.Run("filtered", func(t *testing.T) { 1122 req := &structs.DCSpecificRequest{ 1123 Datacenter: "dc1", 1124 QueryOptions: structs.QueryOptions{ 1125 Token: TestDefaultMasterToken, 1126 Filter: "DestinationName == foobar", 1127 }, 1128 } 1129 1130 var resp structs.IndexedIntentions 1131 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.List", req, &resp)) 1132 require.Len(t, resp.Intentions, 1) 1133 }) 1134} 1135 1136// Test basic matching. We don't need to exhaustively test inputs since this 1137// is tested in the agent/consul/state package. 1138func TestIntentionMatch_good(t *testing.T) { 1139 t.Parallel() 1140 1141 assert := assert.New(t) 1142 dir1, s1 := testServer(t) 1143 defer os.RemoveAll(dir1) 1144 defer s1.Shutdown() 1145 codec := rpcClient(t, s1) 1146 defer codec.Close() 1147 1148 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1149 1150 // Create some records 1151 { 1152 insert := [][]string{ 1153 {"foo", "*", "foo", "*"}, 1154 {"foo", "*", "foo", "bar"}, 1155 {"foo", "*", "foo", "baz"}, // shouldn't match 1156 {"foo", "*", "bar", "bar"}, // shouldn't match 1157 {"foo", "*", "bar", "*"}, // shouldn't match 1158 {"foo", "*", "*", "*"}, 1159 {"bar", "*", "foo", "bar"}, // duplicate destination different source 1160 } 1161 1162 for _, v := range insert { 1163 ixn := structs.IntentionRequest{ 1164 Datacenter: "dc1", 1165 Op: structs.IntentionOpCreate, 1166 Intention: &structs.Intention{ 1167 SourceNS: v[0], 1168 SourceName: v[1], 1169 DestinationNS: v[2], 1170 DestinationName: v[3], 1171 Action: structs.IntentionActionAllow, 1172 }, 1173 } 1174 1175 // Create 1176 var reply string 1177 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1178 } 1179 } 1180 1181 // Match 1182 req := &structs.IntentionQueryRequest{ 1183 Datacenter: "dc1", 1184 Match: &structs.IntentionQueryMatch{ 1185 Type: structs.IntentionMatchDestination, 1186 Entries: []structs.IntentionMatchEntry{ 1187 { 1188 Namespace: "foo", 1189 Name: "bar", 1190 }, 1191 }, 1192 }, 1193 } 1194 var resp structs.IndexedIntentionMatches 1195 assert.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 1196 assert.Len(resp.Matches, 1) 1197 1198 expected := [][]string{ 1199 {"bar", "*", "foo", "bar"}, 1200 {"foo", "*", "foo", "bar"}, 1201 {"foo", "*", "foo", "*"}, 1202 {"foo", "*", "*", "*"}, 1203 } 1204 var actual [][]string 1205 for _, ixn := range resp.Matches[0] { 1206 actual = append(actual, []string{ 1207 ixn.SourceNS, 1208 ixn.SourceName, 1209 ixn.DestinationNS, 1210 ixn.DestinationName, 1211 }) 1212 } 1213 assert.Equal(expected, actual) 1214} 1215 1216// Test matching with ACLs 1217func TestIntentionMatch_acl(t *testing.T) { 1218 t.Parallel() 1219 1220 _, srv, codec := testACLServerWithConfig(t, nil, false) 1221 waitForLeaderEstablishment(t, srv) 1222 1223 token, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service "bar" { policy = "write" }`) 1224 require.NoError(t, err) 1225 1226 // Create some records 1227 { 1228 insert := []string{ 1229 "*", 1230 "bar", 1231 "baz", 1232 } 1233 1234 for _, v := range insert { 1235 ixn := structs.IntentionRequest{ 1236 Datacenter: "dc1", 1237 Op: structs.IntentionOpCreate, 1238 Intention: structs.TestIntention(t), 1239 } 1240 ixn.Intention.DestinationName = v 1241 ixn.WriteRequest.Token = TestDefaultMasterToken 1242 1243 // Create 1244 var reply string 1245 require.Nil(t, msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1246 } 1247 } 1248 1249 // Test with no token 1250 { 1251 req := &structs.IntentionQueryRequest{ 1252 Datacenter: "dc1", 1253 Match: &structs.IntentionQueryMatch{ 1254 Type: structs.IntentionMatchDestination, 1255 Entries: []structs.IntentionMatchEntry{ 1256 { 1257 Namespace: "default", 1258 Name: "bar", 1259 }, 1260 }, 1261 }, 1262 } 1263 var resp structs.IndexedIntentionMatches 1264 err := msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp) 1265 require.True(t, acl.IsErrPermissionDenied(err)) 1266 require.Len(t, resp.Matches, 0) 1267 } 1268 1269 // Test with proper token 1270 { 1271 req := &structs.IntentionQueryRequest{ 1272 Datacenter: "dc1", 1273 Match: &structs.IntentionQueryMatch{ 1274 Type: structs.IntentionMatchDestination, 1275 Entries: []structs.IntentionMatchEntry{ 1276 { 1277 Namespace: "default", 1278 Name: "bar", 1279 }, 1280 }, 1281 }, 1282 QueryOptions: structs.QueryOptions{Token: token.SecretID}, 1283 } 1284 var resp structs.IndexedIntentionMatches 1285 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Match", req, &resp)) 1286 require.Len(t, resp.Matches, 1) 1287 1288 expected := []string{"bar", "*"} 1289 var actual []string 1290 for _, ixn := range resp.Matches[0] { 1291 actual = append(actual, ixn.DestinationName) 1292 } 1293 1294 require.ElementsMatch(t, expected, actual) 1295 } 1296} 1297 1298// Test the Check method defaults to allow with no ACL set. 1299func TestIntentionCheck_defaultNoACL(t *testing.T) { 1300 t.Parallel() 1301 1302 require := require.New(t) 1303 dir1, s1 := testServer(t) 1304 defer os.RemoveAll(dir1) 1305 defer s1.Shutdown() 1306 codec := rpcClient(t, s1) 1307 defer codec.Close() 1308 1309 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1310 1311 // Test 1312 req := &structs.IntentionQueryRequest{ 1313 Datacenter: "dc1", 1314 Check: &structs.IntentionQueryCheck{ 1315 SourceNS: "foo", 1316 SourceName: "bar", 1317 DestinationNS: "foo", 1318 DestinationName: "qux", 1319 SourceType: structs.IntentionSourceConsul, 1320 }, 1321 } 1322 var resp structs.IntentionQueryCheckResponse 1323 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1324 require.True(resp.Allowed) 1325} 1326 1327// Test the Check method defaults to deny with allowlist ACLs. 1328func TestIntentionCheck_defaultACLDeny(t *testing.T) { 1329 t.Parallel() 1330 1331 require := require.New(t) 1332 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1333 c.ACLDatacenter = "dc1" 1334 c.ACLsEnabled = true 1335 c.ACLMasterToken = "root" 1336 c.ACLDefaultPolicy = "deny" 1337 }) 1338 defer os.RemoveAll(dir1) 1339 defer s1.Shutdown() 1340 codec := rpcClient(t, s1) 1341 defer codec.Close() 1342 1343 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1344 1345 // Check 1346 req := &structs.IntentionQueryRequest{ 1347 Datacenter: "dc1", 1348 Check: &structs.IntentionQueryCheck{ 1349 SourceNS: "foo", 1350 SourceName: "bar", 1351 DestinationNS: "foo", 1352 DestinationName: "qux", 1353 SourceType: structs.IntentionSourceConsul, 1354 }, 1355 } 1356 req.Token = "root" 1357 var resp structs.IntentionQueryCheckResponse 1358 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1359 require.False(resp.Allowed) 1360} 1361 1362// Test the Check method defaults to deny with denylist ACLs. 1363func TestIntentionCheck_defaultACLAllow(t *testing.T) { 1364 t.Parallel() 1365 1366 require := require.New(t) 1367 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1368 c.ACLDatacenter = "dc1" 1369 c.ACLsEnabled = true 1370 c.ACLMasterToken = "root" 1371 c.ACLDefaultPolicy = "allow" 1372 }) 1373 defer os.RemoveAll(dir1) 1374 defer s1.Shutdown() 1375 codec := rpcClient(t, s1) 1376 defer codec.Close() 1377 1378 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1379 1380 // Check 1381 req := &structs.IntentionQueryRequest{ 1382 Datacenter: "dc1", 1383 Check: &structs.IntentionQueryCheck{ 1384 SourceNS: "foo", 1385 SourceName: "bar", 1386 DestinationNS: "foo", 1387 DestinationName: "qux", 1388 SourceType: structs.IntentionSourceConsul, 1389 }, 1390 } 1391 req.Token = "root" 1392 var resp structs.IntentionQueryCheckResponse 1393 require.Nil(msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1394 require.True(resp.Allowed) 1395} 1396 1397// Test the Check method requires service:read permission. 1398func TestIntentionCheck_aclDeny(t *testing.T) { 1399 t.Parallel() 1400 1401 require := require.New(t) 1402 dir1, s1 := testServerWithConfig(t, func(c *Config) { 1403 c.ACLDatacenter = "dc1" 1404 c.ACLsEnabled = true 1405 c.ACLMasterToken = "root" 1406 c.ACLDefaultPolicy = "deny" 1407 }) 1408 defer os.RemoveAll(dir1) 1409 defer s1.Shutdown() 1410 codec := rpcClient(t, s1) 1411 defer codec.Close() 1412 1413 testrpc.WaitForLeader(t, s1.RPC, "dc1") 1414 1415 // Create an ACL with service read permissions. This will grant permission. 1416 var token string 1417 { 1418 var rules = ` 1419service "bar" { 1420 policy = "read" 1421}` 1422 1423 req := structs.ACLRequest{ 1424 Datacenter: "dc1", 1425 Op: structs.ACLSet, 1426 ACL: structs.ACL{ 1427 Name: "User token", 1428 Type: structs.ACLTokenTypeClient, 1429 Rules: rules, 1430 }, 1431 WriteRequest: structs.WriteRequest{Token: "root"}, 1432 } 1433 require.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token)) 1434 } 1435 1436 // Check 1437 req := &structs.IntentionQueryRequest{ 1438 Datacenter: "dc1", 1439 Check: &structs.IntentionQueryCheck{ 1440 SourceNS: "foo", 1441 SourceName: "qux", 1442 DestinationNS: "foo", 1443 DestinationName: "baz", 1444 SourceType: structs.IntentionSourceConsul, 1445 }, 1446 } 1447 req.Token = token 1448 var resp structs.IntentionQueryCheckResponse 1449 err := msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp) 1450 require.True(acl.IsErrPermissionDenied(err)) 1451} 1452 1453// Test the Check method returns allow/deny properly. 1454func TestIntentionCheck_match(t *testing.T) { 1455 t.Parallel() 1456 1457 _, srv, codec := testACLServerWithConfig(t, nil, false) 1458 waitForLeaderEstablishment(t, srv) 1459 1460 token, err := upsertTestTokenWithPolicyRules(codec, TestDefaultMasterToken, "dc1", `service "api" { policy = "read" }`) 1461 require.NoError(t, err) 1462 1463 // Create some intentions 1464 { 1465 insert := [][]string{ 1466 {"web", "db"}, 1467 {"api", "db"}, 1468 {"web", "api"}, 1469 } 1470 1471 for _, v := range insert { 1472 ixn := structs.IntentionRequest{ 1473 Datacenter: "dc1", 1474 Op: structs.IntentionOpCreate, 1475 Intention: &structs.Intention{ 1476 SourceNS: "default", 1477 SourceName: v[0], 1478 DestinationNS: "default", 1479 DestinationName: v[1], 1480 Action: structs.IntentionActionAllow, 1481 }, 1482 WriteRequest: structs.WriteRequest{Token: TestDefaultMasterToken}, 1483 } 1484 // Create 1485 var reply string 1486 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn, &reply)) 1487 } 1488 } 1489 1490 // Check 1491 req := &structs.IntentionQueryRequest{ 1492 Datacenter: "dc1", 1493 Check: &structs.IntentionQueryCheck{ 1494 SourceNS: "default", 1495 SourceName: "web", 1496 DestinationNS: "default", 1497 DestinationName: "api", 1498 SourceType: structs.IntentionSourceConsul, 1499 }, 1500 QueryOptions: structs.QueryOptions{Token: token.SecretID}, 1501 } 1502 var resp structs.IntentionQueryCheckResponse 1503 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1504 require.True(t, resp.Allowed) 1505 1506 // Test no match for sanity 1507 { 1508 req := &structs.IntentionQueryRequest{ 1509 Datacenter: "dc1", 1510 Check: &structs.IntentionQueryCheck{ 1511 SourceNS: "default", 1512 SourceName: "db", 1513 DestinationNS: "default", 1514 DestinationName: "api", 1515 SourceType: structs.IntentionSourceConsul, 1516 }, 1517 QueryOptions: structs.QueryOptions{Token: token.SecretID}, 1518 } 1519 var resp structs.IntentionQueryCheckResponse 1520 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Intention.Check", req, &resp)) 1521 require.False(t, resp.Allowed) 1522 } 1523} 1524