github.com/kubearmor/cilium@v1.6.12/proxylib/memcached/parser.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 // text memcache protocol parser based on https://github.com/memcached/memcached/blob/master/doc/protocol.txt 16 17 package memcache 18 19 import ( 20 "bytes" 21 "fmt" 22 "regexp" 23 24 "github.com/cilium/cilium/proxylib/proxylib" 25 26 "github.com/cilium/cilium/proxylib/memcached/binary" 27 "github.com/cilium/cilium/proxylib/memcached/meta" 28 "github.com/cilium/cilium/proxylib/memcached/text" 29 30 "github.com/cilium/proxy/go/cilium/api" 31 log "github.com/sirupsen/logrus" 32 ) 33 34 // Rule matches against memcached requests 35 type Rule struct { 36 // allowed commands 37 commands memcacheCommandSet 38 // compiled regex 39 keyExact []byte 40 keyPrefix []byte 41 regex *regexp.Regexp 42 43 empty bool 44 } 45 46 // Matches returns true if the Rule matches 47 func (rule *Rule) Matches(data interface{}) bool { 48 log.Debugf("memcache checking rule %v", *rule) 49 50 packetMeta, ok := data.(meta.MemcacheMeta) 51 52 if !ok { 53 log.Debugf("Wrong type supplied to Rule.Matches") 54 return false 55 } 56 57 if rule.empty { 58 return true 59 } 60 61 if packetMeta.IsBinary() { 62 if !rule.matchOpcode(packetMeta.Opcode) { 63 return false 64 } 65 } else { 66 if !rule.matchCommand(packetMeta.Command) { 67 return false 68 } 69 } 70 71 if len(rule.keyExact) > 0 { 72 for _, key := range packetMeta.Keys { 73 if !bytes.Equal(rule.keyExact, key) { 74 return false 75 } 76 } 77 return true 78 } 79 80 if len(rule.keyPrefix) > 0 { 81 for _, key := range packetMeta.Keys { 82 if !bytes.HasPrefix(key, rule.keyPrefix) { 83 return false 84 } 85 } 86 return true 87 } 88 89 if rule.regex != nil { 90 for _, key := range packetMeta.Keys { 91 if !rule.regex.Match(key) { 92 return false 93 } 94 } 95 return true 96 } 97 98 log.Debugf("No key rule specified, accepted by command match") 99 return true 100 } 101 102 func (rule *Rule) matchCommand(cmd string) bool { 103 _, ok := rule.commands.text[cmd] 104 return ok 105 } 106 107 func (rule *Rule) matchOpcode(code byte) bool { 108 _, ok := rule.commands.binary[code] 109 return ok 110 } 111 112 // L7RuleParser parses protobuf L7 rules to and array of Rule 113 // May panic 114 func L7RuleParser(rule *cilium.PortNetworkPolicyRule) []proxylib.L7NetworkPolicyRule { 115 var rules []proxylib.L7NetworkPolicyRule 116 l7Rules := rule.GetL7Rules() 117 if l7Rules == nil { 118 return rules 119 } 120 for _, l7Rule := range l7Rules.GetL7Rules() { 121 var br Rule 122 var commandFound = false 123 for k, v := range l7Rule.Rule { 124 switch k { 125 case "command": 126 br.commands, commandFound = MemcacheOpCodeMap[v] 127 case "keyExact": 128 br.keyExact = []byte(v) 129 case "keyPrefix": 130 br.keyPrefix = []byte(v) 131 case "keyRegex": 132 br.regex = regexp.MustCompile(v) 133 default: 134 proxylib.ParseError(fmt.Sprintf("Unsupported key: %s", k), rule) 135 } 136 } 137 if !commandFound { 138 if len(br.keyExact) > 0 || len(br.keyPrefix) > 0 || br.regex != nil { 139 proxylib.ParseError("command not specified but key was provided", rule) 140 } else { 141 br.empty = true 142 } 143 } 144 log.Debugf("Parsed Rule pair: %v", br) 145 rules = append(rules, &br) 146 } 147 return rules 148 } 149 150 // ParserFactory implements proxylib.ParserFactory 151 type ParserFactory struct{} 152 153 // Create creates memcached parser 154 func (p *ParserFactory) Create(connection *proxylib.Connection) proxylib.Parser { 155 log.Debugf("ParserFactory: Create: %v", connection) 156 return &Parser{ 157 connection: connection, 158 } 159 } 160 161 // compile time check for interface implementation 162 var _ proxylib.ParserFactory = &ParserFactory{} 163 164 var memcacheParserFactory *ParserFactory 165 166 const ( 167 parserName = "memcache" 168 ) 169 170 func init() { 171 log.Debug("init(): Registering memcacheParserFactory") 172 proxylib.RegisterParserFactory(parserName, memcacheParserFactory) 173 proxylib.RegisterL7RuleParser(parserName, L7RuleParser) 174 } 175 176 // Parser implements proxylib.Parser 177 type Parser struct { 178 connection *proxylib.Connection 179 parser proxylib.Parser 180 isBinary *bool 181 } 182 183 var _ proxylib.Parser = &Parser{} 184 185 // OnData parses memcached data 186 func (p *Parser) OnData(reply, endStream bool, dataBuffers [][]byte) (proxylib.OpType, int) { 187 if p.parser == nil { 188 var magicByte byte 189 if len(dataBuffers) > 0 && len(dataBuffers[0]) > 0 { 190 magicByte = dataBuffers[0][0] 191 } else { 192 return proxylib.NOP, 0 193 } 194 195 if magicByte >= 128 { 196 p.parser = binary.ParserFactoryInstance.Create(p.connection) 197 } else { 198 p.parser = text.ParserFactoryInstance.Create(p.connection) 199 } 200 } 201 return p.parser.OnData(reply, endStream, dataBuffers) 202 } 203 204 type memcacheCommandSet struct { 205 text map[string]struct{} 206 binary map[byte]struct{} 207 } 208 209 // empty var for filling map below which will be used as set 210 var e = struct{}{} 211 212 // MemcacheOpCodeMap maps operation names and groups used in policy rules to sets of operation names and opcodes that are allowed in such a policy rule. 213 // for more information on protocol check https://github.com/memcached/memcached/wiki/Protocols 214 var MemcacheOpCodeMap = map[string]memcacheCommandSet{ 215 "add": { 216 text: map[string]struct{}{"add": e}, 217 binary: map[byte]struct{}{2: e, 18: e}, 218 }, 219 "set": { 220 text: map[string]struct{}{"set": e}, 221 binary: map[byte]struct{}{1: e, 17: e}, 222 }, 223 "replace": { 224 text: map[string]struct{}{"replace": e}, 225 binary: map[byte]struct{}{3: e, 19: e}, 226 }, 227 "append": { 228 text: map[string]struct{}{"append": e}, 229 binary: map[byte]struct{}{14: e, 25: e}, 230 }, 231 "prepend": { 232 text: map[string]struct{}{"prepend": e}, 233 binary: map[byte]struct{}{15: e, 26: e}, 234 }, 235 "cas": { 236 text: map[string]struct{}{"cas": e}, 237 binary: map[byte]struct{}{}, 238 }, 239 "incr": { 240 text: map[string]struct{}{"incr": e}, 241 binary: map[byte]struct{}{5: e, 21: e}, 242 }, 243 "decr": { 244 text: map[string]struct{}{"decr": e}, 245 binary: map[byte]struct{}{6: e, 22: e}, 246 }, 247 "storage": { 248 text: map[string]struct{}{ 249 "add": e, 250 "set": e, 251 "replace": e, 252 "append": e, 253 "prepend": e, 254 "cas": e, 255 "incr": e, 256 "decr": e, 257 }, 258 binary: map[byte]struct{}{ 259 1: e, 260 2: e, 261 3: e, 262 5: e, 263 6: e, 264 17: e, 265 18: e, 266 19: e, 267 21: e, 268 22: e, 269 25: e, 270 26: e, 271 }, 272 }, 273 274 "get": { 275 text: map[string]struct{}{"get": e, "gets": e}, 276 binary: map[byte]struct{}{ 277 0: e, 278 9: e, 279 12: e, 280 13: e, 281 }, 282 }, 283 284 "delete": { 285 text: map[string]struct{}{"delete": e}, 286 binary: map[byte]struct{}{ 287 4: e, 288 20: e, 289 }, 290 }, 291 292 "touch": { 293 text: map[string]struct{}{"touch": e}, 294 binary: map[byte]struct{}{28: e}, 295 }, 296 297 "gat": { 298 text: map[string]struct{}{"gat": e, "gats": e}, 299 binary: map[byte]struct{}{29: e, 30: e}, 300 }, 301 302 "writeGroup": { 303 text: map[string]struct{}{ 304 "add": e, 305 "set": e, 306 "replace": e, 307 "append": e, 308 "prepend": e, 309 "cas": e, 310 "incr": e, 311 "decr": e, 312 "delete": e, 313 "touch": e, 314 }, 315 binary: map[byte]struct{}{ 316 1: e, 317 2: e, 318 3: e, 319 4: e, 320 5: e, 321 6: e, 322 17: e, 323 18: e, 324 19: e, 325 20: e, 326 21: e, 327 22: e, 328 25: e, 329 26: e, 330 28: e, 331 }, 332 }, 333 334 "slabs": { 335 text: map[string]struct{}{"slabs": e}, 336 binary: map[byte]struct{}{}, 337 }, 338 339 "lru": { 340 text: map[string]struct{}{"lru": e}, 341 binary: map[byte]struct{}{}, 342 }, 343 344 "lru_crawler": { 345 text: map[string]struct{}{"lru_crawler": e}, 346 binary: map[byte]struct{}{}, 347 }, 348 349 "watch": { 350 text: map[string]struct{}{"watch": e}, 351 binary: map[byte]struct{}{}, 352 }, 353 354 "stats": { 355 text: map[string]struct{}{"stats": e}, 356 binary: map[byte]struct{}{16: e}, 357 }, 358 359 "flush_all": { 360 text: map[string]struct{}{"flush_all": e}, 361 binary: map[byte]struct{}{8: e, 24: e}, 362 }, 363 364 "cache_memlimit": { 365 text: map[string]struct{}{"cache_memlimit": e}, 366 binary: map[byte]struct{}{}, 367 }, 368 369 "version": { 370 text: map[string]struct{}{"version": e}, 371 binary: map[byte]struct{}{11: e}, 372 }, 373 374 "misbehave": { 375 text: map[string]struct{}{"misbehave": e}, 376 binary: map[byte]struct{}{}, 377 }, 378 379 "quit": { 380 text: map[string]struct{}{"quit": e}, 381 binary: map[byte]struct{}{7: e, 23: e}, 382 }, 383 384 "noop": { 385 text: map[string]struct{}{}, 386 binary: map[byte]struct{}{10: e}, 387 }, 388 "verbosity": { 389 text: map[string]struct{}{}, 390 binary: map[byte]struct{}{27: e}, 391 }, 392 "sasl-list-mechs": { 393 text: map[string]struct{}{}, 394 binary: map[byte]struct{}{32: e}, 395 }, 396 "sasl-auth": { 397 text: map[string]struct{}{}, 398 binary: map[byte]struct{}{33: e}}, 399 "sasl-step": { 400 text: map[string]struct{}{}, 401 binary: map[byte]struct{}{34: e}}, 402 "rget": { 403 text: map[string]struct{}{}, 404 binary: map[byte]struct{}{48: e}}, 405 "rset": { 406 text: map[string]struct{}{}, 407 binary: map[byte]struct{}{49: e}}, 408 "rsetq": { 409 text: map[string]struct{}{}, 410 binary: map[byte]struct{}{50: e}}, 411 "rappend": { 412 text: map[string]struct{}{}, 413 binary: map[byte]struct{}{51: e}}, 414 "rappendq": { 415 text: map[string]struct{}{}, 416 binary: map[byte]struct{}{52: e}}, 417 "rprepend": { 418 text: map[string]struct{}{}, 419 binary: map[byte]struct{}{53: e}}, 420 "rprependq": { 421 text: map[string]struct{}{}, 422 binary: map[byte]struct{}{54: e}}, 423 "rdelete": { 424 text: map[string]struct{}{}, 425 binary: map[byte]struct{}{55: e}}, 426 "rdeleteq": { 427 text: map[string]struct{}{}, 428 binary: map[byte]struct{}{56: e}}, 429 "rincr": { 430 text: map[string]struct{}{}, 431 binary: map[byte]struct{}{57: e}}, 432 "rincrq": { 433 text: map[string]struct{}{}, 434 binary: map[byte]struct{}{58: e}}, 435 "rdecr": { 436 text: map[string]struct{}{}, 437 binary: map[byte]struct{}{59: e}}, 438 "rdecrq": { 439 text: map[string]struct{}{}, 440 binary: map[byte]struct{}{60: e}}, 441 "set-vbucket": { 442 text: map[string]struct{}{}, 443 binary: map[byte]struct{}{61: e}}, 444 "get-vbucket": { 445 text: map[string]struct{}{}, 446 binary: map[byte]struct{}{62: e}}, 447 "del-vbucket": { 448 text: map[string]struct{}{}, 449 binary: map[byte]struct{}{63: e}}, 450 "tap-connect": { 451 text: map[string]struct{}{}, 452 binary: map[byte]struct{}{64: e}}, 453 "tap-mutation": { 454 text: map[string]struct{}{}, 455 binary: map[byte]struct{}{65: e}}, 456 "tap-delete": { 457 text: map[string]struct{}{}, 458 binary: map[byte]struct{}{66: e}}, 459 "tap-flush": { 460 text: map[string]struct{}{}, 461 binary: map[byte]struct{}{67: e}}, 462 "tap-opaque": { 463 text: map[string]struct{}{}, 464 binary: map[byte]struct{}{68: e}}, 465 "tap-vbucket-set": { 466 text: map[string]struct{}{}, 467 binary: map[byte]struct{}{69: e}}, 468 "tap-checkpoint-start": { 469 text: map[string]struct{}{}, 470 binary: map[byte]struct{}{70: e}}, 471 "tap-checkpoint-end": { 472 text: map[string]struct{}{}, 473 binary: map[byte]struct{}{71: e}}, 474 }