github.com/zhyoulun/cilium@v1.6.12/proxylib/cassandra/cassandraparser_test.go (about) 1 // Copyright 2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build !privileged_tests 16 17 package cassandra 18 19 import ( 20 "encoding/hex" 21 "testing" 22 23 // "github.com/cilium/cilium/pkg/logging" 24 "github.com/cilium/cilium/proxylib/accesslog" 25 "github.com/cilium/cilium/proxylib/proxylib" 26 "github.com/cilium/cilium/proxylib/test" 27 28 // log "github.com/sirupsen/logrus" 29 . "gopkg.in/check.v1" 30 ) 31 32 // Hook up gocheck into the "go test" runner. 33 func Test(t *testing.T) { 34 // logging.ToggleDebugLogs(true) 35 // log.SetLevel(log.DebugLevel) 36 37 TestingT(t) 38 } 39 40 type CassandraSuite struct { 41 logServer *test.AccessLogServer 42 ins *proxylib.Instance 43 } 44 45 var _ = Suite(&CassandraSuite{}) 46 47 // Set up access log server and Library instance for all the test cases 48 func (s *CassandraSuite) SetUpSuite(c *C) { 49 s.logServer = test.StartAccessLogServer("access_log.sock", 10) 50 c.Assert(s.logServer, Not(IsNil)) 51 s.ins = proxylib.NewInstance("node1", accesslog.NewClient(s.logServer.Path)) 52 c.Assert(s.ins, Not(IsNil)) 53 } 54 55 func (s *CassandraSuite) checkAccessLogs(c *C, expPasses, expDrops int) { 56 passes, drops := s.logServer.Clear() 57 c.Check(passes, Equals, expPasses, Commentf("Unxpected number of passed access log messages")) 58 c.Check(drops, Equals, expDrops, Commentf("Unxpected number of passed access log messages")) 59 } 60 61 func (s *CassandraSuite) TearDownTest(c *C) { 62 s.logServer.Clear() 63 } 64 65 func (s *CassandraSuite) TearDownSuite(c *C) { 66 s.logServer.Close() 67 } 68 69 // util function used for Cassandra tests, as we have cassandra requests 70 // as hex strings 71 func hexData(c *C, dataHex ...string) [][]byte { 72 data := make([][]byte, 0, len(dataHex)) 73 for i := range dataHex { 74 dataRaw, err := hex.DecodeString(dataHex[i]) 75 c.Assert(err, IsNil) 76 data = append(data, dataRaw) 77 } 78 return data 79 } 80 81 func (s *CassandraSuite) TestCassandraOnDataNoHeader(c *C) { 82 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "no-policy") 83 data := hexData(c, "0400") 84 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 85 proxylib.MORE, 9-len(data[0])) 86 } 87 88 func (s *CassandraSuite) TestCassandraOnDataOptionsReq(c *C) { 89 s.ins.CheckInsertPolicyText(c, "1", []string{` 90 name: "cp6" 91 policy: 2 92 ingress_per_port_policies: < 93 port: 80 94 rules: < 95 remote_policies: 1 96 remote_policies: 3 97 remote_policies: 4 98 l7_proto: "cassandra" 99 l7_rules: < 100 l7_rules: < 101 rule: < 102 key: "query_action" 103 value: "select" 104 > 105 > 106 > 107 > 108 > 109 `}) 110 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp6") 111 112 data := hexData(c, "040000000500000000") 113 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 114 proxylib.PASS, len(data[0]), 115 proxylib.MORE, 9) 116 } 117 118 // this passes a large query request that is missing just the last byte 119 func (s *CassandraSuite) TestCassandraOnDataPartialReq(c *C) { 120 s.ins.CheckInsertPolicyText(c, "1", []string{` 121 name: "cp5" 122 policy: 2 123 ingress_per_port_policies: < 124 port: 80 125 rules: < 126 remote_policies: 1 127 remote_policies: 3 128 remote_policies: 4 129 l7_proto: "cassandra" 130 l7_rules: < 131 l7_rules: < 132 rule: < 133 key: "query_table" 134 value: ".*" 135 > 136 > 137 > 138 > 139 > 140 `}) 141 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp5") 142 data := hexData(c, "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c270001") 143 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 144 proxylib.MORE, 1) 145 } 146 147 func (s *CassandraSuite) TestCassandraOnDataQueryReq(c *C) { 148 s.ins.CheckInsertPolicyText(c, "1", []string{` 149 name: "cp4" 150 policy: 2 151 ingress_per_port_policies: < 152 port: 80 153 rules: < 154 remote_policies: 1 155 remote_policies: 3 156 remote_policies: 4 157 l7_proto: "cassandra" 158 l7_rules: < 159 l7_rules: < 160 rule: < 161 key: "query_table" 162 value: ".*" 163 > 164 > 165 > 166 > 167 > 168 `}) 169 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp4") 170 data := hexData(c, "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") 171 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 172 proxylib.PASS, len(data[0]), 173 proxylib.MORE, 9) 174 } 175 176 func (s *CassandraSuite) TestCassandraOnDataSplitQueryReq(c *C) { 177 s.ins.CheckInsertPolicyText(c, "1", []string{` 178 name: "cp3" 179 policy: 2 180 ingress_per_port_policies: < 181 port: 80 182 rules: < 183 remote_policies: 1 184 remote_policies: 3 185 remote_policies: 4 186 l7_proto: "cassandra" 187 l7_rules: < 188 l7_rules: < 189 rule: < 190 key: "query_table" 191 value: ".*" 192 > 193 > 194 > 195 > 196 > 197 `}) 198 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp3") 199 data := hexData(c, "04000004070000007600", "00006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") 200 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 201 proxylib.PASS, len(data[0])+len(data[1]), 202 proxylib.MORE, 9) 203 } 204 205 func (s *CassandraSuite) TestCassandraOnDataMultiReq(c *C) { 206 s.ins.CheckInsertPolicyText(c, "1", []string{` 207 name: "cp2" 208 policy: 2 209 ingress_per_port_policies: < 210 port: 80 211 rules: < 212 remote_policies: 1 213 remote_policies: 3 214 remote_policies: 4 215 l7_proto: "cassandra" 216 l7_rules: < 217 l7_rules: < 218 rule: < 219 key: "query_table" 220 value: ".*" 221 > 222 > 223 > 224 > 225 > 226 `}) 227 228 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp2") 229 230 data := hexData(c, "040000000500000000", 231 "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") 232 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 233 proxylib.PASS, len(data[0]), 234 proxylib.PASS, len(data[1]), 235 proxylib.MORE, 9) 236 } 237 238 func (s *CassandraSuite) TestSimpleCassandraPolicy(c *C) { 239 s.ins.CheckInsertPolicyText(c, "1", []string{` 240 name: "cp1" 241 policy: 2 242 ingress_per_port_policies: < 243 port: 80 244 rules: < 245 remote_policies: 1 246 remote_policies: 3 247 remote_policies: 4 248 l7_proto: "cassandra" 249 l7_rules: < 250 l7_rules: < 251 rule: < 252 key: "query_table" 253 value: "no-match" 254 > 255 > 256 > 257 > 258 > 259 `}) 260 261 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 262 263 unauthMsg := createUnauthMsg(0x4) 264 data := hexData(c, "040000000500000000", 265 "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") 266 conn.CheckOnDataOK(c, false, false, &data, unauthMsg, 267 proxylib.PASS, len(data[0]), 268 proxylib.DROP, len(data[1]), 269 proxylib.MORE, 9) 270 271 // All passes are not access-logged 272 s.checkAccessLogs(c, 0, 1) 273 } 274 275 func createUnauthMsg(streamID byte) []byte { 276 unauthMsg := make([]byte, len(unauthMsgBase)) 277 copy(unauthMsg, unauthMsgBase) 278 unauthMsg[0] = 0x84 279 unauthMsg[2] = 0x0 280 unauthMsg[3] = streamID 281 return unauthMsg 282 } 283 284 // this test confirms that we correctly parse and allow a valid batch requests 285 func (s *CassandraSuite) TestCassandraBatchRequestPolicy(c *C) { 286 s.ins.CheckInsertPolicyText(c, "1", []string{` 287 name: "cp1" 288 policy: 2 289 ingress_per_port_policies: < 290 port: 80 291 rules: < 292 remote_policies: 1 293 remote_policies: 3 294 remote_policies: 4 295 l7_proto: "cassandra" 296 l7_rules: < 297 l7_rules: < 298 rule: < 299 key: "query_table" 300 value: "db1.*" 301 > 302 > 303 > 304 > 305 > 306 `}) 307 308 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 309 310 batchMsg := []byte{ 311 0x04, // version 312 0x0, // flags, (uint8) 313 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 314 0x0d, // opcode batch (uint8) 315 0x0, 0x0, 0x0, 0x3c, // request length of 60 (uint32) - update if body changes 316 0x0, // batch type == logged 317 0x0, 0x2, // two batch messages 318 319 // first batch message 320 0x0, // type: non-prepared query 321 0x0, 0x0, 0x0, 0x14, // [long string] length (20) 322 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '1', 323 0x0, 0x0, // # of bound values 324 325 // second batch message 326 0x0, // type: non-prepared query 327 0x0, 0x0, 0x0, 0x14, // [long string] length (20) 328 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '2', 329 0x0, 0x0, // # of bound values 330 331 0x0, 0x0, // consistency level [short] 332 0x0, // batch flags 333 } 334 data := [][]byte{batchMsg} 335 336 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 337 proxylib.PASS, len(data[0]), 338 proxylib.MORE, 9) 339 340 // batch requests are access-logged individually 341 s.checkAccessLogs(c, 2, 0) 342 } 343 344 // this test confirms that we correctly parse and deny a batch request 345 // if any of the requests are denied. 346 func (s *CassandraSuite) TestCassandraBatchRequestPolicyDenied(c *C) { 347 s.ins.CheckInsertPolicyText(c, "1", []string{` 348 name: "cp1" 349 policy: 2 350 ingress_per_port_policies: < 351 port: 80 352 rules: < 353 remote_policies: 1 354 remote_policies: 3 355 remote_policies: 4 356 l7_proto: "cassandra" 357 l7_rules: < 358 l7_rules: < 359 rule: < 360 key: "query_table" 361 value: "db1.*" 362 > 363 > 364 > 365 > 366 > 367 `}) 368 369 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 370 371 batchMsg := []byte{ 372 0x04, // version 373 0x0, // flags, (uint8) 374 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 375 0x0d, // opcode batch (uint8) 376 0x0, 0x0, 0x0, 0x3c, // request length of 60 (uint32) - update if body changes 377 0x0, // batch type == logged 378 0x0, 0x2, // two batch messages 379 380 // first batch message 381 0x0, // type: non-prepared query 382 0x0, 0x0, 0x0, 0x14, // [long string] length (20) 383 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '1', 384 0x0, 0x0, // # of bound values 385 386 // second batch message (accesses db2.t2, which should be denied) 387 0x0, // type: non-prepared query 388 0x0, 0x0, 0x0, 0x14, // [long string] length (20) 389 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '2', '.', 't', '2', 390 0x0, 0x0, // # of bound values 391 392 0x0, 0x0, // consistency level [short] 393 0x0, // batch flags 394 } 395 data := [][]byte{batchMsg} 396 397 unauthMsg := createUnauthMsg(0x4) 398 conn.CheckOnDataOK(c, false, false, &data, unauthMsg, 399 proxylib.DROP, len(data[0]), 400 proxylib.MORE, 9) 401 402 // batch requests are access-logged individually 403 // Note: in this case, both accesses are denied, as a batch 404 // request is either entirely allowed or denied 405 s.checkAccessLogs(c, 0, 2) 406 } 407 408 // test batch requests with prepared statements 409 func (s *CassandraSuite) TestCassandraBatchRequestPreparedStatement(c *C) { 410 s.ins.CheckInsertPolicyText(c, "1", []string{` 411 name: "cp1" 412 policy: 2 413 ingress_per_port_policies: < 414 port: 80 415 rules: < 416 remote_policies: 1 417 remote_policies: 3 418 remote_policies: 4 419 l7_proto: "cassandra" 420 l7_rules: < 421 l7_rules: < 422 rule: < 423 key: "query_table" 424 value: "db3.*" 425 > 426 > 427 > 428 > 429 > 430 `}) 431 432 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 433 434 cassParser, ok := (conn.Parser).(*CassandraParser) 435 if !ok { 436 panic("failed to cast conn.Parser to *CassandraParser\n") 437 } 438 preparedQueryID1 := "aaaa" 439 cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/batch/select/db3.t1" 440 preparedQueryID2 := "bbbb" 441 cassParser.preparedQueryPathByPreparedID[preparedQueryID2] = "/batch/select/db3.t2" 442 443 batchMsg := []byte{ 444 0x04, // version 445 0x0, // flags, (uint8) 446 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 447 0x0d, // opcode batch (uint8) 448 0x0, 0x0, 0x0, 0x18, // request length of 60 (uint32) - update if body changes 449 0x0, // batch type == logged 450 0x0, 0x2, // two batch messages 451 452 // first batch message 453 0x1, // type: prepared query 454 0x0, 0x4, // [short] length (4) 455 'a', 'a', 'a', 'a', 456 0x0, 0x0, // # of bound values 457 458 // second batch message 459 0x1, // type: non-prepared query 460 0x0, 0x4, // [short] length (4) 461 'b', 'b', 'b', 'b', 462 0x0, 0x0, // # of bound values 463 464 0x0, 0x0, // consistency level [short] 465 0x0, // batch flags 466 } 467 data := [][]byte{batchMsg} 468 469 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 470 proxylib.PASS, len(data[0]), 471 proxylib.MORE, 9) 472 473 // batch requests are access-logged individually 474 s.checkAccessLogs(c, 2, 0) 475 } 476 477 // test batch requests with prepared statements, including a deny 478 func (s *CassandraSuite) TestCassandraBatchRequestPreparedStatementDenied(c *C) { 479 s.ins.CheckInsertPolicyText(c, "1", []string{` 480 name: "cp1" 481 policy: 2 482 ingress_per_port_policies: < 483 port: 80 484 rules: < 485 remote_policies: 1 486 remote_policies: 3 487 remote_policies: 4 488 l7_proto: "cassandra" 489 l7_rules: < 490 l7_rules: < 491 rule: < 492 key: "query_table" 493 value: "db3.*" 494 > 495 > 496 > 497 > 498 > 499 `}) 500 501 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 502 503 cassParser, ok := (conn.Parser).(*CassandraParser) 504 if !ok { 505 panic("failed to cast conn.Parser to *CassandraParser\n") 506 } 507 preparedQueryID1 := "aaaa" 508 cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/batch/select/db3.t1" 509 preparedQueryID2 := "bbbb" 510 cassParser.preparedQueryPathByPreparedID[preparedQueryID2] = "/batch/select/db4.t2" 511 512 batchMsg := []byte{ 513 0x04, // version 514 0x0, // flags, (uint8) 515 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 516 0x0d, // opcode batch (uint8) 517 0x0, 0x0, 0x0, 0x18, // request length of 60 (uint32) - update if body changes 518 0x0, // batch type == logged 519 0x0, 0x2, // two batch messages 520 521 // first batch message 522 0x1, // type: prepared query 523 0x0, 0x4, // [short] length (4) 524 'a', 'a', 'a', 'a', 525 0x0, 0x0, // # of bound values 526 527 // second batch message (accesses table db4, which should be denied) 528 0x1, // type: non-prepared query 529 0x0, 0x4, // [short] length (4) 530 'b', 'b', 'b', 'b', 531 0x0, 0x0, // # of bound values 532 533 0x0, 0x0, // consistency level [short] 534 0x0, // batch flags 535 } 536 data := [][]byte{batchMsg} 537 538 unauthMsg := createUnauthMsg(0x4) 539 conn.CheckOnDataOK(c, false, false, &data, unauthMsg, 540 proxylib.DROP, len(data[0]), 541 proxylib.MORE, 9) 542 543 // batch requests are access-logged individually 544 s.checkAccessLogs(c, 0, 2) 545 } 546 547 // test execute statement, allow request 548 func (s *CassandraSuite) TestCassandraExecutePreparedStatement(c *C) { 549 s.ins.CheckInsertPolicyText(c, "1", []string{` 550 name: "cp1" 551 policy: 2 552 ingress_per_port_policies: < 553 port: 80 554 rules: < 555 remote_policies: 1 556 remote_policies: 3 557 remote_policies: 4 558 l7_proto: "cassandra" 559 l7_rules: < 560 l7_rules: < 561 rule: < 562 key: "query_table" 563 value: "db3.*" 564 > 565 > 566 > 567 > 568 > 569 `}) 570 571 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 572 573 cassParser, ok := (conn.Parser).(*CassandraParser) 574 if !ok { 575 panic("failed to cast conn.Parser to *CassandraParser\n") 576 } 577 preparedQueryID1 := "aaaa" 578 cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/query/select/db3.t1" 579 580 executeMsg := []byte{ 581 0x04, // version 582 0x0, // flags, (uint8) 583 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 584 0x0a, // opcode execute (uint8) 585 0x0, 0x0, 0x0, 0x09, // request length (uint32) - update if body changes 586 587 // Execute request 588 0x0, 0x4, // short bytes len (4) 589 'a', 'a', 'a', 'a', 590 591 // the rest of this is values that can be ignored by our parser, 592 // but we add some here to make sure that we're properly passing 593 // based on total request length. 594 'x', 'y', 'z', 595 } 596 data := [][]byte{executeMsg} 597 598 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 599 proxylib.PASS, len(data[0]), 600 proxylib.MORE, 9) 601 602 s.checkAccessLogs(c, 1, 0) 603 } 604 605 // test execute statement with unknown prepared-id 606 func (s *CassandraSuite) TestCassandraExecutePreparedStatementUnknownID(c *C) { 607 608 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 609 610 executeMsg := []byte{ 611 0x04, // version 612 0x0, // flags, (uint8) 613 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 614 0x0a, // opcode execute (uint8) 615 0x0, 0x0, 0x0, 0x06, // request length (uint32) - update if body changes 616 617 // Execute request 618 0x0, 0x4, // short bytes len (4) 619 'a', 'a', 'a', 'a', 620 } 621 data := [][]byte{executeMsg} 622 623 unpreparedMsg := createUnpreparedMsg(0x04, []byte{0x0, 0x4}, "aaaa") 624 625 conn.CheckOnDataOK(c, false, false, &data, unpreparedMsg, 626 proxylib.DROP, len(data[0]), 627 proxylib.MORE, 9) 628 629 s.checkAccessLogs(c, 0, 1) 630 } 631 632 // test parsing of a prepared query reply 633 func (s *CassandraSuite) TestCassandraPreparedResultReply(c *C) { 634 635 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 636 637 cassParser, ok := (conn.Parser).(*CassandraParser) 638 if !ok { 639 panic("failed to cast conn.Parser to *CassandraParser\n") 640 } 641 642 // make sure there is a stream-id (4) that matches the request below 643 // this would have been populated by a "prepare" request 644 cassParser.preparedQueryPathByStreamID[uint16(4)] = "/query/select/db3.t1" 645 646 preparedResultMsg := []byte{ 647 0x84, // reply + version 648 0x0, // flags, (uint8) 649 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) 650 0x08, // opcode result (uint8) 651 0x0, 0x0, 0x0, 0x16, // request length 22 (uint32) - update if body changes 652 653 // Prepared Result request 654 0x0, 0x0, 0x0, 0x4, // [int] result type 655 0x0, 0x4, // prepared-id len (short) 656 'a', 'a', 'a', 'a', // prepared-id 657 0x0, 0x0, 0x0, 0x0, // prepared results flags 658 0x0, 0x0, 0x0, 0x0, // column-count 659 0x0, 0x0, 0x0, 0x0, // pk-count 660 } 661 data := [][]byte{preparedResultMsg} 662 663 conn.CheckOnDataOK(c, true, false, &data, []byte{}, 664 proxylib.PASS, len(data[0]), 665 proxylib.MORE, 9) 666 667 // these replies are not access logged 668 s.checkAccessLogs(c, 0, 0) 669 } 670 671 // test additional queries 672 func (s *CassandraSuite) TestCassandraAdditionalQueries(c *C) { 673 s.ins.CheckInsertPolicyText(c, "1", []string{` 674 name: "cp1" 675 policy: 2 676 ingress_per_port_policies: < 677 port: 80 678 rules: < 679 remote_policies: 1 680 remote_policies: 3 681 remote_policies: 4 682 l7_proto: "cassandra" 683 l7_rules: < 684 l7_rules: < 685 rule: < 686 key: "query_table" 687 value: "db4.t1" 688 > 689 > 690 > 691 > 692 > 693 `}) 694 695 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 696 697 queries := []string{"CREATE TABLE db4.t1 (f1 varchar, f2 timeuuid, PRIMARY KEY ((f1), f2))", 698 "INSERT INTO db4.t1 (f1, f2, f3) values ('dan', now(), 'Cilium!')", 699 "UPDATE db4.t1 SET f1 = 'donald' where f2 in (1,2,3)", 700 "DROP TABLE db4.t1", 701 "TRUNCATE db4.t1", 702 "CREATE TABLE IF NOT EXISTS db4.t1 (f1 varchar, PRIMARY KEY(f1))", 703 } 704 705 queryMsgBase := []byte{ 706 0x04, // version 707 0x0, // flags, (uint8) 708 0x0, 0x5, // stream-id (uint16) (test request uses 0x0005 as stream ID) 709 0x07, // opcode query (uint8) 710 0x0, 0x0, 0x0, 0x0, // length of request - must be set 711 712 // Query Req 713 0x0, 0x0, 0x0, 0x0, // length of query (int) - must be set 714 // query string goes here 715 } 716 717 data := make([][]byte, len(queries)) 718 for i := 0; i < len(queries); i++ { 719 queryLen := len(queries[i]) 720 721 queryMsg := append(queryMsgBase, []byte(queries[i])...) 722 723 // this works as long as query is less than 251 bytes 724 queryMsg[8] = byte(4 + queryLen) 725 queryMsg[12] = byte(queryLen) 726 727 data[i] = queryMsg 728 } 729 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 730 proxylib.PASS, len(data[0]), 731 proxylib.PASS, len(data[1]), 732 proxylib.PASS, len(data[2]), 733 proxylib.PASS, len(data[3]), 734 proxylib.PASS, len(data[4]), 735 proxylib.PASS, len(data[5]), 736 proxylib.MORE, 9) 737 738 s.checkAccessLogs(c, 6, 0) 739 } 740 741 // test use query, following by query that does not include the keyspace 742 func (s *CassandraSuite) TestCassandraUseQuery(c *C) { 743 s.ins.CheckInsertPolicyText(c, "1", []string{` 744 name: "cp1" 745 policy: 2 746 ingress_per_port_policies: < 747 port: 80 748 rules: < 749 remote_policies: 1 750 remote_policies: 3 751 remote_policies: 4 752 l7_proto: "cassandra" 753 l7_rules: < 754 l7_rules: < 755 rule: < 756 key: "query_table" 757 value: "db5.t1" 758 > 759 > 760 > 761 > 762 > 763 `}) 764 765 conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "cp1") 766 767 // note: the second insert command intentionally does not include a keyspace, so that it will only 768 // be allowed if we properly propagate the keyspace from the previous use command 769 queries := []string{"USE db5", "INSERT INTO t1 (f1, f2, f3) values ('dan', now(), 'Cilium!')"} 770 771 queryMsgBase := []byte{ 772 0x04, // version 773 0x0, // flags, (uint8) 774 0x0, 0x5, // stream-id (uint16) (test request uses 0x0005 as stream ID) 775 0x07, // opcode query (uint8) 776 0x0, 0x0, 0x0, 0x0, // length of request - must be set 777 778 // Query Req 779 0x0, 0x0, 0x0, 0x0, // length of query (int) - must be set 780 // query string goes here 781 } 782 783 data := make([][]byte, len(queries)) 784 for i := 0; i < len(queries); i++ { 785 queryLen := len(queries[i]) 786 787 queryMsg := append(queryMsgBase, []byte(queries[i])...) 788 789 // this works as long as query is less than 251 bytes 790 queryMsg[8] = byte(4 + queryLen) 791 queryMsg[12] = byte(queryLen) 792 793 data[i] = queryMsg 794 } 795 conn.CheckOnDataOK(c, false, false, &data, []byte{}, 796 proxylib.PASS, len(data[0]), 797 proxylib.PASS, len(data[1]), 798 proxylib.MORE, 9) 799 800 // use command will not show up in access log, so only expect one msg 801 s.checkAccessLogs(c, 1, 0) 802 }