github.com/fafucoder/cilium@v1.6.11/proxylib/testparsers/headerparser.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  //
    16  // Accompanying file `headerparser.policy` contains an example policy
    17  // for this protocol. Install it with:
    18  // $ cilium policy import proxylib/testparsers/headerparser.policy
    19  //
    20  
    21  package testparsers
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  
    27  	. "github.com/cilium/cilium/proxylib/proxylib"
    28  
    29  	"github.com/cilium/proxy/go/cilium/api"
    30  	log "github.com/sirupsen/logrus"
    31  )
    32  
    33  //
    34  // Header parser used for testing
    35  //
    36  
    37  type HeaderRule struct {
    38  	hasPrefix []byte
    39  	contains  []byte
    40  	hasSuffix []byte
    41  }
    42  
    43  // Matches returns true if the HeaderRule matches
    44  func (rule *HeaderRule) Matches(data interface{}) bool {
    45  	log.Debugf("headerparser checking rule %v", *rule)
    46  
    47  	// Trim whitespace from both ends
    48  	bs := bytes.TrimSpace(data.([]byte))
    49  
    50  	if len(rule.hasPrefix) > 0 && !bytes.HasPrefix(bs, rule.hasPrefix) {
    51  		log.Debugf("headerparser HasPrefix %s does not match %s", bs, rule.hasPrefix)
    52  		return false
    53  	}
    54  
    55  	if len(rule.contains) > 0 && !bytes.Contains(bs, rule.contains) {
    56  		log.Debugf("headerparser Contains %s does not match %s", bs, rule.contains)
    57  		return false
    58  	}
    59  
    60  	if len(rule.hasSuffix) > 0 && !bytes.HasSuffix(bs, rule.hasSuffix) {
    61  		log.Debugf("headerparser HasSuffix %s does not match %s", bs, rule.hasSuffix)
    62  		return false
    63  	}
    64  	log.Debug("headerparser rule matched!")
    65  
    66  	return true
    67  }
    68  
    69  // L7HeaderRuleParser parses protobuf L7 rules to and array of HeaderRules
    70  func L7HeaderRuleParser(rule *cilium.PortNetworkPolicyRule) []L7NetworkPolicyRule {
    71  	var rules []L7NetworkPolicyRule
    72  	l7Rules := rule.GetL7Rules()
    73  	if l7Rules == nil {
    74  		return rules
    75  	}
    76  	for _, l7Rule := range l7Rules.GetL7Rules() {
    77  		var hr HeaderRule
    78  		for k, v := range l7Rule.Rule {
    79  			switch k {
    80  			case "prefix":
    81  				hr.hasPrefix = []byte(v)
    82  			case "contains":
    83  				hr.contains = []byte(v)
    84  			case "suffix":
    85  				hr.hasSuffix = []byte(v)
    86  			default:
    87  				ParseError(fmt.Sprintf("Unsupported key: %s", k), rule)
    88  			}
    89  		}
    90  		log.Debugf("Parsed HeaderRule pair: %v", hr)
    91  		rules = append(rules, &hr)
    92  	}
    93  	return rules
    94  }
    95  
    96  type HeaderParserFactory struct{}
    97  
    98  var headerParserFactory *HeaderParserFactory
    99  
   100  const (
   101  	parserName = "test.headerparser"
   102  )
   103  
   104  func init() {
   105  	log.Debug("init(): Registering headerParserFactory")
   106  	RegisterParserFactory(parserName, headerParserFactory)
   107  	RegisterL7RuleParser(parserName, L7HeaderRuleParser)
   108  }
   109  
   110  type HeaderParser struct {
   111  	connection *Connection
   112  }
   113  
   114  func (p *HeaderParserFactory) Create(connection *Connection) Parser {
   115  	log.Debugf("HeaderParserFactory: Create: %v", connection)
   116  	return &HeaderParser{connection: connection}
   117  }
   118  
   119  //
   120  // Parses individual lines and verifies them against the policy
   121  //
   122  func (p *HeaderParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) {
   123  	line, ok := getLine(data)
   124  	line_len := len(line)
   125  
   126  	if !reply {
   127  		log.Debugf("HeaderParser: Request: %s", line)
   128  	} else {
   129  		log.Debugf("HeaderParser: Response: %s", line)
   130  	}
   131  
   132  	if !ok {
   133  		if line_len > 0 {
   134  			// Partial line received, but no newline, ask for more
   135  			return MORE, 1
   136  		} else {
   137  			// Nothing received, don't know if more will be coming; do nothing
   138  			return NOP, 0
   139  		}
   140  	}
   141  
   142  	// Replies pass unconditionally
   143  	if reply || p.connection.Matches(line) {
   144  		p.connection.Log(cilium.EntryType_Request,
   145  			&cilium.LogEntry_GenericL7{
   146  				GenericL7: &cilium.L7LogEntry{
   147  					Proto: parserName,
   148  					Fields: map[string]string{
   149  						"status": "PASS",
   150  					},
   151  				},
   152  			})
   153  		return PASS, line_len
   154  	}
   155  
   156  	// Inject Error response to the reverse direction
   157  	p.connection.Inject(!reply, []byte(fmt.Sprintf("Line dropped: %s", line)))
   158  	// Drop the line in the current direction
   159  	p.connection.Log(cilium.EntryType_Denied,
   160  		&cilium.LogEntry_GenericL7{
   161  			GenericL7: &cilium.L7LogEntry{
   162  				Proto: parserName,
   163  				Fields: map[string]string{
   164  					"status": "DROP",
   165  				},
   166  			},
   167  		})
   168  
   169  	return DROP, line_len
   170  }