github.phpd.cn/cilium/cilium@v1.6.12/pkg/labels/filter.go (about) 1 // Copyright 2016-2017 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 package labels 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "os" 21 "regexp" 22 "strings" 23 24 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 25 "github.com/cilium/cilium/pkg/lock" 26 "github.com/cilium/cilium/pkg/logging" 27 "github.com/cilium/cilium/pkg/logging/logfields" 28 ) 29 30 var ( 31 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "labels-filter") 32 validLabelPrefixesMU lock.RWMutex 33 validLabelPrefixes *labelPrefixCfg // Label prefixes used to filter from all labels 34 ) 35 36 const ( 37 // LPCfgFileVersion represents the version of a Label Prefix Configuration File 38 LPCfgFileVersion = 1 39 ) 40 41 // LabelPrefix is the cilium's representation of a container label. 42 // +k8s:deepcopy-gen=false 43 // +k8s:openapi-gen=false 44 type LabelPrefix struct { 45 // Ignore if true will cause this prefix to be ignored insted of being accepted 46 Ignore bool `json:"invert"` 47 Prefix string `json:"prefix"` 48 Source string `json:"source"` 49 expr *regexp.Regexp 50 } 51 52 // String returns a human readable representation of the LabelPrefix 53 func (p LabelPrefix) String() string { 54 s := fmt.Sprintf("%s:%s", p.Source, p.Prefix) 55 if p.Ignore { 56 s = "!" + s 57 } 58 59 return s 60 } 61 62 // matches returns true and the length of the matched section if the label is 63 // matched by the LabelPrefix. The Ignore flag has no effect at this point. 64 func (p LabelPrefix) matches(l Label) (bool, int) { 65 if p.Source != "" && p.Source != l.Source { 66 return false, 0 67 } 68 69 // If no regular expression is available, fall back to prefix matching 70 if p.expr == nil { 71 return strings.HasPrefix(l.Key, p.Prefix), len(p.Prefix) 72 } 73 74 res := p.expr.FindStringIndex(l.Key) 75 76 // No match if regexp was not found 77 if res == nil { 78 return false, 0 79 } 80 81 // Otherwise match if match was found at start of key 82 return res[0] == 0, res[1] 83 } 84 85 // parseLabelPrefix returns a LabelPrefix created from the string label parameter. 86 func parseLabelPrefix(label string) (*LabelPrefix, error) { 87 labelPrefix := LabelPrefix{} 88 i := strings.IndexByte(label, ':') 89 if i >= 0 { 90 labelPrefix.Source = label[:i] 91 labelPrefix.Prefix = label[i+1:] 92 } else { 93 labelPrefix.Prefix = label 94 } 95 96 if labelPrefix.Prefix[0] == '!' { 97 labelPrefix.Ignore = true 98 labelPrefix.Prefix = labelPrefix.Prefix[1:] 99 } 100 101 r, err := regexp.Compile(labelPrefix.Prefix) 102 if err != nil { 103 return nil, fmt.Errorf("unable to compile regexp: %s", err) 104 } 105 labelPrefix.expr = r 106 107 return &labelPrefix, nil 108 } 109 110 // ParseLabelPrefixCfg parses valid label prefixes from a file and from a slice 111 // of valid prefixes. Both are optional. If both are provided, both list are 112 // appended together. 113 func ParseLabelPrefixCfg(prefixes []string, file string) error { 114 cfg, err := readLabelPrefixCfgFrom(file) 115 if err != nil { 116 return fmt.Errorf("unable to read label prefix file: %s", err) 117 } 118 119 for _, label := range prefixes { 120 p, err := parseLabelPrefix(label) 121 if err != nil { 122 return err 123 } 124 125 if !p.Ignore { 126 cfg.whitelist = true 127 } 128 129 cfg.LabelPrefixes = append(cfg.LabelPrefixes, p) 130 } 131 132 validLabelPrefixes = cfg 133 134 log.Info("Valid label prefix configuration:") 135 for _, l := range validLabelPrefixes.LabelPrefixes { 136 log.Infof(" - %s", l) 137 } 138 139 return nil 140 } 141 142 // labelPrefixCfg is the label prefix configuration to filter labels of started 143 // containers. 144 // +k8s:openapi-gen=false 145 type labelPrefixCfg struct { 146 Version int `json:"version"` 147 LabelPrefixes []*LabelPrefix `json:"valid-prefixes"` 148 // whitelist if true, indicates that an inclusive rule has to match 149 // in order for the label to be considered 150 whitelist bool 151 } 152 153 // defaultLabelPrefixCfg returns a default LabelPrefixCfg using the latest 154 // LPCfgFileVersion 155 func defaultLabelPrefixCfg() *labelPrefixCfg { 156 cfg := &labelPrefixCfg{ 157 Version: LPCfgFileVersion, 158 LabelPrefixes: []*LabelPrefix{}, 159 } 160 161 expressions := []string{ 162 k8sConst.PodNamespaceLabel, // include io.kubernetes.pod.namespace 163 k8sConst.PodNamespaceMetaLabels, // include all namespace labels 164 k8sConst.AppKubernetes, // include app.kubernetes.io 165 "!io.kubernetes", // ignore all other io.kubernetes labels 166 "!kubernetes.io", // ignore all other kubernetes.io labels 167 "!.*beta.kubernetes.io", // ignore all beta.kubernetes.io labels 168 "!k8s.io", // ignore all k8s.io labels 169 "!pod-template-generation", // ignore pod-template-generation 170 "!pod-template-hash", // ignore pod-template-hash 171 "!controller-revision-hash", // ignore controller-revision-hash 172 "!annotation.*", // ignore all annotation labels 173 "!etcd_node", // ignore etcd_node label 174 } 175 176 for _, e := range expressions { 177 p, err := parseLabelPrefix(e) 178 if err != nil { 179 msg := fmt.Sprintf("BUG: Unable to parse default label prefix '%s': %s", e, err) 180 panic(msg) 181 } 182 cfg.LabelPrefixes = append(cfg.LabelPrefixes, p) 183 } 184 185 return cfg 186 } 187 188 // readLabelPrefixCfgFrom reads a label prefix configuration file from fileName. If the 189 // version is not supported by us it returns an error. 190 func readLabelPrefixCfgFrom(fileName string) (*labelPrefixCfg, error) { 191 // if not file is specified, the default is empty 192 if fileName == "" { 193 return defaultLabelPrefixCfg(), nil 194 } 195 196 f, err := os.Open(fileName) 197 if err != nil { 198 return nil, err 199 } 200 defer f.Close() 201 lpc := labelPrefixCfg{} 202 err = json.NewDecoder(f).Decode(&lpc) 203 if err != nil { 204 return nil, err 205 } 206 if lpc.Version != LPCfgFileVersion { 207 return nil, fmt.Errorf("unsupported version %d", lpc.Version) 208 } 209 for _, lp := range lpc.LabelPrefixes { 210 if lp.Prefix == "" { 211 return nil, fmt.Errorf("invalid label prefix file: prefix was empty") 212 } 213 if lp.Source == "" { 214 return nil, fmt.Errorf("invalid label prefix file: source was empty") 215 } 216 if !lp.Ignore { 217 lpc.whitelist = true 218 } 219 } 220 return &lpc, nil 221 } 222 223 func (cfg *labelPrefixCfg) filterLabels(lbls Labels) (identityLabels, informationLabels Labels) { 224 if lbls == nil { 225 return nil, nil 226 } 227 228 validLabelPrefixesMU.RLock() 229 defer validLabelPrefixesMU.RUnlock() 230 231 identityLabels = Labels{} 232 informationLabels = Labels{} 233 for k, v := range lbls { 234 included, ignored := 0, 0 235 236 for _, p := range cfg.LabelPrefixes { 237 if m, len := p.matches(v); m { 238 if p.Ignore { 239 // save length of shortest matching ignore 240 if ignored == 0 || len < ignored { 241 ignored = len 242 } 243 } else { 244 // save length of longest matching include 245 if len > included { 246 included = len 247 } 248 } 249 } 250 } 251 252 // A label is accepted if : 253 // - No inclusive LabelPrefix (Ignore flag not set) is 254 // configured and label is not ignored. 255 // - An inclusive LabelPrefix matches the label 256 // - If both an inclusive and ignore LabelPrefix match, the 257 // label is accepted if the matching section in the label 258 // is greater than the ignored matching section in label, 259 // e.g. when evaluating the label foo.bar, the prefix rules 260 // {!foo, foo.bar} will cause the label to be accepted 261 // because the inclusive prefix matches over a longer section. 262 if (!cfg.whitelist && ignored == 0) || included > ignored { 263 // Just want to make sure we don't have labels deleted in 264 // on side and disappearing in the other side... 265 identityLabels[k] = v 266 } else { 267 informationLabels[k] = v 268 } 269 } 270 return identityLabels, informationLabels 271 } 272 273 // FilterLabels returns Labels from the given labels that have the same source and the 274 // same prefix as one of lpc valid prefixes, as well as labels that do not match 275 // the aforementioned filtering criteria. 276 func FilterLabels(lbls Labels) (identityLabels, informationLabels Labels) { 277 return validLabelPrefixes.filterLabels(lbls) 278 }