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) {}