github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/api/fqdn.go (about) 1 // Copyright 2018-2019 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 api 16 17 import ( 18 "fmt" 19 "regexp" 20 "strings" 21 22 "github.com/cilium/cilium/pkg/fqdn/matchpattern" 23 24 "github.com/miekg/dns" 25 ) 26 27 var ( 28 // allowedMatchNameChars tests that MatchName contains only valid DNS characters 29 allowedMatchNameChars = regexp.MustCompile("^[-a-zA-Z0-9.]+$") 30 31 // allowedPatternChars tests that the MatchPattern field contains only the 32 // characters we want in our wilcard scheme. 33 allowedPatternChars = regexp.MustCompile("^[-a-zA-Z0-9.*]+$") // the * inside the [] is a literal * 34 ) 35 36 type FQDNSelector struct { 37 // MatchName matches literal DNS names. A trailing "." is automatically added 38 // when missing. 39 MatchName string `json:"matchName,omitempty"` 40 41 // MatchPattern allows using wildcards to match DNS names. All wildcards are 42 // case insensitive. The wildcards are: 43 // - "*" matches 0 or more DNS valid characters, and may occur anywhere in 44 // the pattern. As a special case a "*" as the leftmost character, without a 45 // following "." matches all subdomains as well as the name to the right. 46 // A trailing "." is automatically added when missing. 47 // 48 // Examples: 49 // `*.cilium.io` matches subomains of cilium at that level 50 // www.cilium.io and blog.cilium.io match, cilium.io and google.com do not 51 // `*cilium.io` matches cilium.io and all subdomains 1 level below 52 // www.cilium.io, blog.cilium.io and cilium.io match, google.com does not 53 // sub*.cilium.io matches subdomains of cilium where the subdomain component 54 // begins with "sub" 55 // sub.cilium.io and subdomain.cilium.io match, www.cilium.io, 56 // blog.cilium.io, cilium.io and google.com do not 57 MatchPattern string `json:"matchPattern,omitempty"` 58 } 59 60 func (s *FQDNSelector) String() string { 61 return fmt.Sprintf("MatchName: %s, MatchPattern: %s", s.MatchName, s.MatchPattern) 62 } 63 64 // sanitize for FQDNSelector is a little wonky. While we do more processing 65 // when using MatchName the basic requirement is that is a valid regexp. We 66 // test that it can compile here. 67 func (s *FQDNSelector) sanitize() error { 68 if len(s.MatchName) > 0 && len(s.MatchPattern) > 0 { 69 return fmt.Errorf("only one of MatchName or MatchPattern is allowed in an FQDNSelector") 70 } 71 if len(s.MatchName) > 0 && !allowedMatchNameChars.MatchString(s.MatchName) { 72 return fmt.Errorf("Invalid characters in MatchName: \"%s\". Only 0-9, a-z, A-Z and . and - characters are allowed", s.MatchName) 73 } 74 75 if len(s.MatchPattern) > 0 && !allowedPatternChars.MatchString(s.MatchPattern) { 76 return fmt.Errorf("Invalid characters in MatchPattern: \"%s\". Only 0-9, a-z, A-Z and ., - and * characters are allowed", s.MatchPattern) 77 } 78 _, err := matchpattern.Validate(s.MatchPattern) 79 return err 80 } 81 82 // ToRegex converts the given FQDNSelector to its corresponding regular 83 // expression. If the MatchName field is set in the selector, it performs all 84 // needed formatting to ensure that the field is a valid regular expression. 85 func (s *FQDNSelector) ToRegex() (*regexp.Regexp, error) { 86 var preparedMatch string 87 if s.MatchName != "" { 88 preparedMatch = strings.ToLower(dns.Fqdn(s.MatchName)) 89 } else { 90 preparedMatch = matchpattern.Sanitize(s.MatchPattern) 91 } 92 93 regex, err := matchpattern.Validate(preparedMatch) 94 return regex, err 95 } 96 97 // PortRuleDNS is a list of allowed DNS lookups. 98 type PortRuleDNS FQDNSelector 99 100 // Sanitize checks that the matchName in the portRule can be compiled as a 101 // regex. It does not check that a DNS name is a valid DNS name. 102 func (r *PortRuleDNS) Sanitize() error { 103 if len(r.MatchName) > 0 && !allowedMatchNameChars.MatchString(r.MatchName) { 104 return fmt.Errorf("Invalid characters in MatchName: \"%s\". Only 0-9, a-z, A-Z and . and - characters are allowed", r.MatchName) 105 } 106 107 if len(r.MatchPattern) > 0 && !allowedPatternChars.MatchString(r.MatchPattern) { 108 return fmt.Errorf("Invalid characters in MatchPattern: \"%s\". Only 0-9, a-z, A-Z and ., - and * characters are allowed", r.MatchPattern) 109 } 110 _, err := matchpattern.Validate(r.MatchPattern) 111 return err 112 } 113 114 // GetAsEndpointSelectors returns a FQDNSelector as a single EntityNone 115 // EndpointSelector slice. 116 // Note that toFQDNs behaves differently than most other rules. The presence of 117 // any toFQDNs rules means the endpoint must enforce policy, but the IPs are later 118 // added as toCIDRSet entries and processed as such. 119 func (s *FQDNSelector) GetAsEndpointSelectors() EndpointSelectorSlice { 120 return []EndpointSelector{EndpointSelectorNone} 121 } 122 123 // FQDNSelectorSlice is a wrapper type for []FQDNSelector to make is simpler to 124 // bind methods. 125 type FQDNSelectorSlice []FQDNSelector 126 127 // GetAsEndpointSelectors will return a single EntityNone if any 128 // toFQDNs rules exist, and a nil slice otherwise. 129 func (s FQDNSelectorSlice) GetAsEndpointSelectors() EndpointSelectorSlice { 130 for _, rule := range s { 131 return rule.GetAsEndpointSelectors() 132 } 133 return nil 134 }