github.com/elfadel/cilium@v1.6.12/pkg/fqdn/dnspoller.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 package fqdn 16 17 import ( 18 "context" 19 "time" 20 21 "github.com/cilium/cilium/pkg/controller" 22 "github.com/cilium/cilium/pkg/lock" 23 ) 24 25 // DNSPollerInterval is the time between 2 complete DNS lookup runs of the 26 // DNSPoller controller 27 // Note: This cannot be less than 1*time.Second, as it is used as a default 28 // for MinTTL in DNSPollerConfig 29 const DNSPollerInterval = 5 * time.Second 30 31 // StartDNSPoller spawns a singleton DNS polling controller. The controller 32 // will, periodically, run a DNS lookup for each ToFQDN target DNS name 33 // inserted with StartPollForDNSName. 34 // Note: Repeated calls will replace earlier instances of the controller. 35 func StartDNSPoller(poller *DNSPoller) { 36 log.Debug("Starting DNS poller for ToFQDN rules") 37 controller.NewManager().UpdateController("dns-poller", controller.ControllerParams{ 38 RunInterval: DNSPollerInterval, 39 DoFunc: poller.LookupUpdateDNS, 40 StopFunc: func(ctx context.Context) error { 41 log.Debug("Stopping DNS poller for ToFQDN rules") 42 return nil 43 }, 44 }) 45 } 46 47 // DNSPoller periodically runs lookups for registered DNS names. It will emit 48 // regenerated policy rules when the IPs change. CNAMEs (and DNAMEs) are not 49 // handled directly, but will depend on the resolver's behavior. 50 // fqdn.Config can be opitonally used to set how the DNS lookups are 51 // executed (via LookupDNSNames) and how generated policy rules are handled 52 // (via UpdateSelectors). 53 type DNSPoller struct { 54 lock.Mutex // this guards both maps and their contents 55 56 // ruleManager is the backing NameManager that tells this poller which names to 57 // poll, and where to submit DNS updates. 58 ruleManager *NameManager 59 60 // config is a copy from when this instance was initialized. 61 // It is read-only once set 62 config Config 63 64 // DNSHistory is the collection of still-valid DNS responses intercepted 65 // for the poller. 66 // This is not protected by the mutex due to internally is using a mutex. 67 DNSHistory *DNSCache 68 } 69 70 // NewDNSPoller creates an initialized DNSPoller. It does not start the controller (use .Start) 71 func NewDNSPoller(config Config, ruleManager *NameManager) *DNSPoller { 72 if config.MinTTL == 0 { 73 config.MinTTL = 2 * int(DNSPollerInterval/time.Second) 74 } 75 76 if config.LookupDNSNames == nil { 77 config.LookupDNSNames = DNSLookupDefaultResolver 78 } 79 80 if config.PollerResponseNotify == nil { 81 config.PollerResponseNotify = noopPollerResponseNotify 82 } 83 84 return &DNSPoller{ 85 config: config, 86 ruleManager: ruleManager, 87 DNSHistory: NewDNSCacheWithLimit(config.MinTTL, config.OverLimit), 88 } 89 } 90 91 // LookupUpdateDNS runs a DNS lookup for each stored DNS name, storing updates 92 // into ruleManager, which may emit regenerated policy rules. 93 // The general steps are: 94 // 1- take a snapshot of DNS names to lookup from .ruleManager, into dnsNamesToPoll 95 // 2- Do a DNS lookup for each DNS name (map key) in poller via LookupDNSNames 96 // 3- Update IPs for each dnsName in .ruleManager. If the IPs have changed for the 97 // name, it will generate and emit them. 98 func (poller *DNSPoller) LookupUpdateDNS(ctx context.Context) error { 99 // Collect the DNS names that need lookups. This avoids locking 100 // poller during lookups. 101 dnsNamesToPoll := poller.ruleManager.GetDNSNames() 102 103 // lookup the DNS names. Names with failures will not be updated (and we 104 // will use the most recent data below) 105 lookupTime := time.Now() 106 updatedDNSIPs, errorDNSNames := poller.config.LookupDNSNames(dnsNamesToPoll) 107 for dnsName, err := range errorDNSNames { 108 log.WithError(err).WithField("matchName", dnsName). 109 Warn("Cannot resolve FQDN. Traffic egressing to this destination may be incorrectly dropped due to stale data.") 110 } 111 for qname, response := range updatedDNSIPs { 112 poller.DNSHistory.Update(lookupTime, qname, response.IPs, response.TTL) 113 poller.config.PollerResponseNotify(lookupTime, qname, response) 114 } 115 116 _, err := poller.ruleManager.UpdateGenerateDNS(ctx, lookupTime, updatedDNSIPs) 117 return err 118 } 119 120 // noopPollerResponseNotify is used when no PollerResponseNotify is set. 121 func noopPollerResponseNotify(lookupTime time.Time, qname string, response *DNSIPRecords) {}