github.com/datadog/cilium@v1.6.12/daemon/fqdn.go (about)

     1  // Copyright 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 main
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"net"
    23  	"regexp"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"time"
    28  
    29  	"github.com/cilium/cilium/api/v1/models"
    30  	. "github.com/cilium/cilium/api/v1/server/restapi/policy"
    31  	"github.com/cilium/cilium/pkg/api"
    32  	"github.com/cilium/cilium/pkg/controller"
    33  	"github.com/cilium/cilium/pkg/endpoint"
    34  	"github.com/cilium/cilium/pkg/endpointmanager"
    35  	"github.com/cilium/cilium/pkg/fqdn"
    36  	"github.com/cilium/cilium/pkg/fqdn/dnsproxy"
    37  	"github.com/cilium/cilium/pkg/fqdn/matchpattern"
    38  	"github.com/cilium/cilium/pkg/identity"
    39  	secIDCache "github.com/cilium/cilium/pkg/identity/cache"
    40  	"github.com/cilium/cilium/pkg/ipcache"
    41  	"github.com/cilium/cilium/pkg/logging/logfields"
    42  	"github.com/cilium/cilium/pkg/metrics"
    43  	"github.com/cilium/cilium/pkg/node"
    44  	"github.com/cilium/cilium/pkg/option"
    45  	"github.com/cilium/cilium/pkg/policy"
    46  	policyApi "github.com/cilium/cilium/pkg/policy/api"
    47  	"github.com/cilium/cilium/pkg/proxy"
    48  	"github.com/cilium/cilium/pkg/proxy/accesslog"
    49  	"github.com/cilium/cilium/pkg/proxy/logger"
    50  	"github.com/cilium/cilium/pkg/u8proto"
    51  	"github.com/go-openapi/runtime/middleware"
    52  	"github.com/go-openapi/strfmt"
    53  	"github.com/sirupsen/logrus"
    54  
    55  	"github.com/miekg/dns"
    56  )
    57  
    58  const (
    59  	upstream       = "upstreamTime"
    60  	processingTime = "processingTime"
    61  
    62  	metricErrorTimeout = "timeout"
    63  	metricErrorProxy   = "proxyErr"
    64  	metricErrorDenied  = "denied"
    65  	metricErrorAllow   = "allow"
    66  )
    67  
    68  func identitiesForFQDNSelectorIPs(selectorsWithIPsToUpdate map[policyApi.FQDNSelector][]net.IP) (map[policyApi.FQDNSelector][]*identity.Identity, error) {
    69  	var err error
    70  
    71  	// Used to track identities which are allocated in calls to
    72  	// AllocateCIDRs. If we for some reason cannot allocate new CIDRs,
    73  	// we have to undo all of our changes and release the identities.
    74  	// This is best effort, as releasing can fail as well.
    75  	usedIdentities := make([]*identity.Identity, 0)
    76  	selectorIdentitySliceMapping := make(map[policyApi.FQDNSelector][]*identity.Identity)
    77  
    78  	// Allocate identities for each IPNet and then map to selector
    79  	for selector, selectorIPs := range selectorsWithIPsToUpdate {
    80  		log.WithFields(logrus.Fields{
    81  			"fqdnSelector": selector,
    82  			"ips":          selectorIPs,
    83  		}).Debug("getting identities for IPs associated with FQDNSelector")
    84  		var currentlyAllocatedIdentities []*identity.Identity
    85  		if currentlyAllocatedIdentities, err = ipcache.AllocateCIDRsForIPs(selectorIPs); err != nil {
    86  			secIDCache.ReleaseSlice(context.TODO(), nil, usedIdentities)
    87  			log.WithError(err).WithField("prefixes", selectorIPs).Warn(
    88  				"failed to allocate identities for IPs")
    89  			return nil, err
    90  		}
    91  		usedIdentities = append(usedIdentities, currentlyAllocatedIdentities...)
    92  		selectorIdentitySliceMapping[selector] = currentlyAllocatedIdentities
    93  	}
    94  
    95  	return selectorIdentitySliceMapping, nil
    96  }
    97  
    98  func (d *Daemon) updateSelectorCacheFQDNs(ctx context.Context, selectors map[policyApi.FQDNSelector][]*identity.Identity, selectorsWithoutIPs []policyApi.FQDNSelector) (wg *sync.WaitGroup) {
    99  	// Update mapping of selector to set of IPs in selector cache.
   100  	for selector, identitySlice := range selectors {
   101  		log.WithFields(logrus.Fields{
   102  			"fqdnSelectorString": selector,
   103  			"identitySlice":      identitySlice}).Debug("updating FQDN selector")
   104  		numIds := make([]identity.NumericIdentity, 0, len(identitySlice))
   105  		for _, numId := range identitySlice {
   106  			// Nil check here? Hopefully not necessary...
   107  			numIds = append(numIds, numId.ID)
   108  		}
   109  		d.policy.GetSelectorCache().UpdateFQDNSelector(selector, numIds)
   110  	}
   111  
   112  	if len(selectorsWithoutIPs) > 0 {
   113  		// Selectors which no longer map to IPs (due to TTL expiry, cache being
   114  		// cleared forcibly via CLI, etc.) still exist in the selector cache
   115  		// since policy is imported which allows it, but the selector does
   116  		// not map to any IPs anymore.
   117  		log.WithFields(logrus.Fields{
   118  			"fqdnSelectors": selectorsWithoutIPs,
   119  		}).Debug("removing all identities from FQDN selectors")
   120  		d.policy.GetSelectorCache().RemoveIdentitiesFQDNSelectors(selectorsWithoutIPs)
   121  	}
   122  
   123  	// There may be nothing to update - in this case, we exit and do not need
   124  	// to trigger policy updates for all endpoints.
   125  	if len(selectors) == 0 && len(selectorsWithoutIPs) == 0 {
   126  		return &sync.WaitGroup{}
   127  	}
   128  	return endpointmanager.UpdatePolicyMaps(ctx)
   129  }
   130  
   131  // bootstrapFQDN initializes the toFQDNs related subsystems: DNSPoller,
   132  // d.dnsNameManager, and the DNS proxy.
   133  // dnsNameManager and DNSPoller will use the default resolver and, implicitly, the
   134  // default DNS cache. The proxy binds to all interfaces, and uses the
   135  // configured DNS proxy port (this may be 0 and so OS-assigned).
   136  func (d *Daemon) bootstrapFQDN(restoredEndpoints *endpointRestoreState, preCachePath string) (err error) {
   137  	cfg := fqdn.Config{
   138  		MinTTL:               option.Config.ToFQDNsMinTTL,
   139  		OverLimit:            option.Config.ToFQDNsMaxIPsPerHost,
   140  		Cache:                fqdn.NewDNSCache(option.Config.ToFQDNsMinTTL),
   141  		LookupDNSNames:       fqdn.DNSLookupDefaultResolver,
   142  		UpdateSelectors:      d.updateSelectors,
   143  		PollerResponseNotify: d.pollerResponseNotify,
   144  	}
   145  
   146  	rg := fqdn.NewNameManager(cfg)
   147  	d.policy.GetSelectorCache().SetLocalIdentityNotifier(rg)
   148  	d.dnsNameManager = rg
   149  	d.dnsPoller = fqdn.NewDNSPoller(cfg, d.dnsNameManager)
   150  	if option.Config.ToFQDNsEnablePoller {
   151  		fqdn.StartDNSPoller(d.dnsPoller)
   152  	}
   153  
   154  	// Controller to cleanup TTL expired entries from the DNS policies.
   155  	dnsGCJobName := "dns-garbage-collector-job"
   156  	controller.NewManager().UpdateController(dnsGCJobName, controller.ControllerParams{
   157  		RunInterval: 1 * time.Minute,
   158  		DoFunc: func(ctx context.Context) error {
   159  
   160  			namesToClean := []string{}
   161  			// cleanup poller cache
   162  			namesToClean = append(namesToClean, d.dnsPoller.DNSHistory.GC()...)
   163  
   164  			// cleanup caches for all existing endpoints as well.
   165  			endpoints := endpointmanager.GetEndpoints()
   166  			for _, ep := range endpoints {
   167  				namesToClean = append(namesToClean, ep.DNSHistory.GC()...)
   168  			}
   169  
   170  			namesToClean = fqdn.KeepUniqueNames(namesToClean)
   171  			if len(namesToClean) == 0 {
   172  				return nil
   173  			}
   174  
   175  			// Collect DNS data into the global cache. This aggregates all endpoint
   176  			// data (and the poller) into one place for use elsewhere.
   177  			// In the case where a lookup occurs in a race with .ReplaceFromCache the
   178  			// result is consistent:
   179  			// - If before, the ReplaceFromCache will use the new data when pulling
   180  			// in from each EP cache.
   181  			// - If after, the normal update process occurs after .ReplaceFromCache
   182  			// releases its locks.
   183  			caches := []*fqdn.DNSCache{d.dnsPoller.DNSHistory}
   184  			for _, ep := range endpoints {
   185  				caches = append(caches, ep.DNSHistory)
   186  			}
   187  			cfg.Cache.ReplaceFromCacheByNames(namesToClean, caches...)
   188  
   189  			metrics.FQDNGarbageCollectorCleanedTotal.Add(float64(len(namesToClean)))
   190  			log.WithField(logfields.Controller, dnsGCJobName).Infof(
   191  				"FQDN garbage collector work deleted %d name entries", len(namesToClean))
   192  			_, err := d.dnsNameManager.ForceGenerateDNS(context.TODO(), namesToClean)
   193  			return err
   194  		},
   195  	})
   196  
   197  	// Prefill the cache with the CLI provided pre-cache data. This allows various bridging arrangements during upgrades, or just ensure critical DNS mappings remain.
   198  	if preCachePath != "" {
   199  		log.WithField(logfields.Path, preCachePath).Info("Reading toFQDNs pre-cache data")
   200  		precache, err := readPreCache(preCachePath)
   201  		if err != nil {
   202  			// FIXME: add a link to the "documented format"
   203  			log.WithError(err).WithField(logfields.Path, preCachePath).Error("Cannot parse toFQDNs pre-cache data. Please ensure the file is JSON and follows the documented format")
   204  			// We do not stop the agent here. It is safer to continue with best effort
   205  			// than to enter crash backoffs when this file is broken.
   206  		} else {
   207  			d.dnsNameManager.GetDNSCache().UpdateFromCache(precache, nil)
   208  		}
   209  	}
   210  
   211  	// Prefill the cache with DNS lookups from restored endpoints. This is needed
   212  	// to maintain continuity of which IPs are allowed.
   213  	// Note: This is TTL aware, and expired data will not be used (e.g. when
   214  	// restoring after a long delay).
   215  	for _, restoredEP := range restoredEndpoints.restored {
   216  		// Upgrades from old ciliums have this nil
   217  		if restoredEP.DNSHistory != nil {
   218  			d.dnsNameManager.GetDNSCache().UpdateFromCache(restoredEP.DNSHistory, []string{})
   219  		}
   220  	}
   221  
   222  	// Do not start the proxy in dry mode or if L7 proxy is disabled.
   223  	// The proxy would not get any traffic in the dry mode anyway, and some of the socket
   224  	// operations require privileges not available in all unit tests.
   225  	if option.Config.DryMode || !option.Config.EnableL7Proxy {
   226  		return nil
   227  	}
   228  
   229  	// Once we stop returning errors from StartDNSProxy this should live in
   230  	// StartProxySupport
   231  	port, listenerName, err := proxy.GetProxyPort(policy.ParserTypeDNS, false)
   232  	if option.Config.ToFQDNsProxyPort != 0 {
   233  		port = uint16(option.Config.ToFQDNsProxyPort)
   234  	}
   235  	if err != nil {
   236  		return err
   237  	}
   238  	proxy.DefaultDNSProxy, err = dnsproxy.StartDNSProxy("", port, d.lookupEPByIP, d.lookupSecIDByIP, d.notifyOnDNSMsg)
   239  	if err == nil {
   240  		// Increase the ProxyPort reference count so that it will never get released.
   241  		err = d.l7Proxy.SetProxyPort(listenerName, proxy.DefaultDNSProxy.BindPort)
   242  
   243  		proxy.DefaultDNSProxy.SetRejectReply(option.Config.FQDNRejectResponse)
   244  	}
   245  	return err // filled by StartDNSProxy
   246  }
   247  
   248  // updateSelectors propagates the mapping of FQDNSelector to identity, as well
   249  // as the set of FQDNSelectors which have no IPs which correspond to them
   250  // (usually due to TTL expiry), down to policy layer managed by this daemon.
   251  func (d *Daemon) updateSelectors(ctx context.Context, selectorWithIPsToUpdate map[policyApi.FQDNSelector][]net.IP, selectorsWithoutIPs []policyApi.FQDNSelector) (wg *sync.WaitGroup, err error) {
   252  	// Convert set of selectors with IPs to update to set of selectors
   253  	// with identities corresponding to said IPs.
   254  	selectorsIdentities, err := identitiesForFQDNSelectorIPs(selectorWithIPsToUpdate)
   255  	if err != nil {
   256  		return &sync.WaitGroup{}, err
   257  	}
   258  
   259  	// Update mapping in selector cache with new identities.
   260  	return d.updateSelectorCacheFQDNs(ctx, selectorsIdentities, selectorsWithoutIPs), nil
   261  }
   262  
   263  // pollerResponseNotify handles update events for updates from the poller. It
   264  // sends these on as monitor events and accesslog entries.
   265  // Note: The poller directly updates d.dnsNameManager with new IP data, separate
   266  // from this callback.
   267  func (d *Daemon) pollerResponseNotify(lookupTime time.Time, qname string, response *fqdn.DNSIPRecords) {
   268  	// Do nothing if this option is off
   269  	if !option.Config.ToFQDNsEnablePollerEvents {
   270  		return
   271  	}
   272  
   273  	// FIXME: Not always true but we don't have the protocol information here
   274  	protocol := accesslog.TransportProtocol(u8proto.ProtoIDs["udp"])
   275  
   276  	record := logger.LogRecord{
   277  		LogRecord: accesslog.LogRecord{
   278  			Type:              accesslog.TypeResponse,
   279  			ObservationPoint:  accesslog.Ingress,
   280  			IPVersion:         accesslog.VersionIPv4,
   281  			TransportProtocol: protocol,
   282  			Timestamp:         time.Now().UTC().Format(time.RFC3339Nano),
   283  			NodeAddressInfo:   accesslog.NodeAddressInfo{},
   284  		},
   285  	}
   286  
   287  	if ip := node.GetExternalIPv4(); ip != nil {
   288  		record.LogRecord.NodeAddressInfo.IPv4 = ip.String()
   289  	}
   290  
   291  	if ip := node.GetIPv6(); ip != nil {
   292  		record.LogRecord.NodeAddressInfo.IPv6 = ip.String()
   293  	}
   294  
   295  	// Construct the list of DNS types for question and answer RRs
   296  	questionTypes := []uint16{dns.TypeA, dns.TypeAAAA}
   297  	answerTypes := []uint16{}
   298  	for _, ip := range response.IPs {
   299  		if ip.To4() == nil {
   300  			answerTypes = append(answerTypes, dns.TypeAAAA)
   301  		} else {
   302  			answerTypes = append(answerTypes, dns.TypeA)
   303  		}
   304  	}
   305  
   306  	// Update DNS specific data in the LogRecord
   307  	logger.LogTags.Verdict(accesslog.VerdictForwarded, "DNSPoller")(&record)
   308  	logger.LogTags.DNS(&accesslog.LogRecordDNS{
   309  		Query:             qname,
   310  		IPs:               response.IPs,
   311  		TTL:               uint32(response.TTL),
   312  		CNAMEs:            nil,
   313  		ObservationSource: accesslog.DNSSourceAgentPoller,
   314  		RCode:             dns.RcodeSuccess,
   315  		QTypes:            questionTypes,
   316  		AnswerTypes:       answerTypes,
   317  	})(&record)
   318  	record.Log()
   319  }
   320  
   321  // lookupEPByIP returns the endpoint that this IP belongs to
   322  func (d *Daemon) lookupEPByIP(endpointIP net.IP) (endpoint *endpoint.Endpoint, err error) {
   323  	e := endpointmanager.LookupIP(endpointIP)
   324  	if e == nil {
   325  		return nil, fmt.Errorf("Cannot find endpoint with IP %s", endpointIP.String())
   326  	}
   327  
   328  	return e, nil
   329  }
   330  
   331  func (d *Daemon) lookupSecIDByIP(ip net.IP) (secID ipcache.Identity, exists bool, err error) {
   332  	ipv6Prefixes, ipv4Prefixes := d.prefixLengths.ToBPFData()
   333  	prefixes := ipv4Prefixes
   334  	if ip.To4() == nil {
   335  		prefixes = ipv6Prefixes
   336  	}
   337  
   338  	for _, prefixLen := range prefixes {
   339  		maskedStr := fmt.Sprintf("%s/%d", ip, prefixLen)
   340  		_, cidr, _ := net.ParseCIDR(maskedStr)
   341  		secID, exists = ipcache.IPIdentityCache.LookupByPrefix(cidr.String())
   342  		if exists == true {
   343  			break
   344  		}
   345  	}
   346  	return secID, exists, nil
   347  }
   348  
   349  // NotifyOnDNSMsg handles DNS data in the daemon by emitting monitor
   350  // events, proxy metrics and storing DNS data in the DNS cache. This may
   351  // result in rule generation.
   352  // It will:
   353  // - Report a monitor error event and proxy metrics when the proxy sees an
   354  //   error, and when it can't process something in this function
   355  // - Report the verdict in a monitor event and emit proxy metrics
   356  // - Insert the DNS data into the cache when msg is a DNS response and we
   357  //   can lookup the endpoint related to it
   358  // epIPPort and serverAddr should match the original request, where epAddr is
   359  // the source for egress (the only case current).
   360  func (d *Daemon) notifyOnDNSMsg(lookupTime time.Time, ep *endpoint.Endpoint, epIPPort string, serverAddr string, msg *dns.Msg, protocol string, allowed bool, stat dnsproxy.ProxyRequestContext) error {
   361  	var protoID = u8proto.ProtoIDs[strings.ToLower(protocol)]
   362  	var verdict accesslog.FlowVerdict
   363  	var reason string
   364  	metricError := metricErrorAllow
   365  	stat.ProcessingTime.Start()
   366  
   367  	endMetric := func() {
   368  		stat.ProcessingTime.End(true)
   369  		metrics.ProxyUpstreamTime.WithLabelValues(metrics.ErrorTimeout, metrics.L7DNS, upstream).Observe(
   370  			stat.UpstreamTime.Total().Seconds())
   371  		metrics.ProxyUpstreamTime.WithLabelValues(metricError, metrics.L7DNS, processingTime).Observe(
   372  			stat.ProcessingTime.Total().Seconds())
   373  	}
   374  
   375  	switch {
   376  	case stat.IsTimeout():
   377  		metricError = metricErrorTimeout
   378  		endMetric()
   379  		return nil
   380  	case stat.Err != nil:
   381  		metricError = metricErrorProxy
   382  		verdict = accesslog.VerdictError
   383  		reason = "Error: " + stat.Err.Error()
   384  	case allowed:
   385  		verdict = accesslog.VerdictForwarded
   386  		reason = "Allowed by policy"
   387  	case !allowed:
   388  		metricError = metricErrorDenied
   389  		verdict = accesslog.VerdictDenied
   390  		reason = "Denied by policy"
   391  	}
   392  
   393  	// We determine the direction based on the DNS packet. The observation
   394  	// point is always Egress, however.
   395  	var flowType accesslog.FlowType
   396  	if msg.Response {
   397  		flowType = accesslog.TypeResponse
   398  	} else {
   399  		flowType = accesslog.TypeRequest
   400  	}
   401  
   402  	var epPort, serverPort int
   403  	_, epPortStr, err := net.SplitHostPort(epIPPort)
   404  	if err != nil {
   405  		log.WithError(err).Error("cannot extract source IP from DNS request")
   406  	} else {
   407  		if epPort, err = strconv.Atoi(epPortStr); err != nil {
   408  			log.WithError(err).WithField(logfields.Port, epPortStr).Error("cannot parse source port")
   409  		}
   410  	}
   411  
   412  	serverIP, serverPortStr, err := net.SplitHostPort(serverAddr)
   413  	if err != nil {
   414  		log.WithError(err).Error("cannot extract destination IP from DNS request")
   415  	} else {
   416  		if serverPort, err = strconv.Atoi(serverPortStr); err != nil {
   417  			log.WithError(err).WithField(logfields.Port, serverPortStr).Error("cannot parse destination port")
   418  		}
   419  	}
   420  	if ep == nil {
   421  		// This is a hard fail. We cannot proceed because record.Log requires a
   422  		// non-nil ep, and we also don't want to insert this data into the
   423  		// cache if we don't know that an endpoint asked for it (this is
   424  		// asserted via ep != nil here and msg.Response && msg.Rcode ==
   425  		// dns.RcodeSuccess below).
   426  		err := errors.New("DNS request cannot be associated with an existing endpoint")
   427  		log.WithError(err).Error("cannot find matching endpoint")
   428  		endMetric()
   429  		return err
   430  	}
   431  	qname, responseIPs, TTL, CNAMEs, rcode, recordTypes, qTypes, err := dnsproxy.ExtractMsgDetails(msg)
   432  	if err != nil {
   433  		// This error is ok because all these values are used for reporting, or filling in the cache.
   434  		log.WithError(err).Error("cannot extract DNS message details")
   435  	}
   436  
   437  	ep.UpdateProxyStatistics(strings.ToUpper(protocol), uint16(serverPort), false, !msg.Response, verdict)
   438  	record := logger.NewLogRecord(proxy.DefaultEndpointInfoRegistry, ep, flowType, false,
   439  		func(lr *logger.LogRecord) { lr.LogRecord.TransportProtocol = accesslog.TransportProtocol(protoID) },
   440  		logger.LogTags.Verdict(verdict, reason),
   441  		logger.LogTags.Addressing(logger.AddressingInfo{
   442  			SrcIPPort:   epIPPort,
   443  			DstIPPort:   serverAddr,
   444  			SrcIdentity: ep.GetIdentity().Uint32(),
   445  		}),
   446  		func(lr *logger.LogRecord) {
   447  			lr.LogRecord.SourceEndpoint = accesslog.EndpointInfo{
   448  				ID:           ep.GetID(),
   449  				IPv4:         ep.GetIPv4Address(),
   450  				IPv6:         ep.GetIPv6Address(),
   451  				Labels:       ep.GetLabels(),
   452  				LabelsSHA256: ep.GetLabelsSHA(),
   453  				Identity:     uint64(ep.GetIdentity()),
   454  				Port:         uint16(epPort),
   455  			}
   456  
   457  			// When the server is an endpoint, get all the data for it.
   458  			// When external, use the ipcache to fill in the SecID
   459  			if serverEP := endpointmanager.LookupIPv4(serverIP); serverEP != nil {
   460  				lr.LogRecord.DestinationEndpoint = accesslog.EndpointInfo{
   461  					ID:           serverEP.GetID(),
   462  					IPv4:         serverEP.GetIPv4Address(),
   463  					IPv6:         serverEP.GetIPv6Address(),
   464  					Labels:       serverEP.GetLabels(),
   465  					LabelsSHA256: serverEP.GetLabelsSHA(),
   466  					Identity:     uint64(serverEP.GetIdentity()),
   467  					Port:         uint16(serverPort),
   468  				}
   469  			} else if serverSecID, exists := ipcache.IPIdentityCache.LookupByIP(serverIP); exists {
   470  				secID := secIDCache.LookupIdentityByID(serverSecID.ID)
   471  				// TODO: handle IPv6
   472  				lr.LogRecord.DestinationEndpoint = accesslog.EndpointInfo{
   473  					IPv4: serverIP,
   474  					// IPv6:         serverEP.GetIPv6Address(),
   475  					Labels:       secID.Labels.GetModel(),
   476  					LabelsSHA256: secID.GetLabelsSHA256(),
   477  					Identity:     uint64(serverSecID.ID.Uint32()),
   478  					Port:         uint16(serverPort),
   479  				}
   480  			}
   481  		},
   482  		logger.LogTags.DNS(&accesslog.LogRecordDNS{
   483  			Query:             qname,
   484  			IPs:               responseIPs,
   485  			TTL:               TTL,
   486  			CNAMEs:            CNAMEs,
   487  			ObservationSource: accesslog.DNSSourceProxy,
   488  			RCode:             rcode,
   489  			QTypes:            qTypes,
   490  			AnswerTypes:       recordTypes,
   491  		}),
   492  	)
   493  	record.Log()
   494  
   495  	if msg.Response && msg.Rcode == dns.RcodeSuccess && len(responseIPs) > 0 {
   496  		// This must happen before the NameManager update below, to ensure that
   497  		// this data is included in the serialized Endpoint object.
   498  		log.WithField(logfields.EndpointID, ep.ID).Debug("Recording DNS lookup in endpoint specific cache")
   499  		if ep.DNSHistory.Update(lookupTime, qname, responseIPs, int(TTL)) {
   500  			ep.SyncEndpointHeaderFile()
   501  		}
   502  
   503  		log.WithFields(logrus.Fields{
   504  			"qname": qname,
   505  			"ips":   responseIPs,
   506  		}).Debug("Updating DNS name in cache from response to to query")
   507  
   508  		updateCtx, updateCancel := context.WithTimeout(context.TODO(), option.Config.FQDNProxyResponseMaxDelay)
   509  		defer updateCancel()
   510  		updateStart := time.Now()
   511  
   512  		wg, err := d.dnsNameManager.UpdateGenerateDNS(updateCtx, lookupTime, map[string]*fqdn.DNSIPRecords{
   513  			qname: {
   514  				IPs: responseIPs,
   515  				TTL: int(TTL),
   516  			}})
   517  		if err != nil {
   518  			log.WithError(err).Error("error updating internal DNS cache for rule generation")
   519  		}
   520  
   521  		updateComplete := make(chan struct{})
   522  		go func(wg *sync.WaitGroup, done chan struct{}) {
   523  			wg.Wait()
   524  			close(updateComplete)
   525  		}(wg, updateComplete)
   526  
   527  		select {
   528  		case <-updateCtx.Done():
   529  			log.Error("Timed out waiting for datapath updates of FQDN IP information; returning response")
   530  		case <-updateComplete:
   531  		}
   532  
   533  		log.WithFields(logrus.Fields{
   534  			logfields.Duration:   time.Since(updateStart),
   535  			logfields.EndpointID: ep.GetID(),
   536  			"qname":              qname,
   537  		}).Debug("Waited for endpoints to regenerate due to a DNS response")
   538  		endMetric()
   539  	}
   540  
   541  	stat.ProcessingTime.End(true)
   542  	return nil
   543  }
   544  
   545  type getFqdnCache struct {
   546  	daemon *Daemon
   547  }
   548  
   549  func NewGetFqdnCacheHandler(d *Daemon) GetFqdnCacheHandler {
   550  	return &getFqdnCache{daemon: d}
   551  }
   552  
   553  func (h *getFqdnCache) Handle(params GetFqdnCacheParams) middleware.Responder {
   554  	// endpoints we want data from
   555  	endpoints := endpointmanager.GetEndpoints()
   556  
   557  	CIDRStr := ""
   558  	if params.Cidr != nil {
   559  		CIDRStr = *params.Cidr
   560  	}
   561  
   562  	matchPatternStr := ""
   563  	if params.Matchpattern != nil {
   564  		matchPatternStr = *params.Matchpattern
   565  	}
   566  
   567  	lookups, err := extractDNSLookups(endpoints, CIDRStr, matchPatternStr)
   568  	switch {
   569  	case err != nil:
   570  		return api.Error(GetFqdnCacheBadRequestCode, err)
   571  	case len(lookups) == 0:
   572  		return NewGetFqdnCacheIDNotFound()
   573  	}
   574  
   575  	return NewGetFqdnCacheOK().WithPayload(lookups)
   576  }
   577  
   578  type deleteFqdnCache struct {
   579  	daemon *Daemon
   580  }
   581  
   582  func NewDeleteFqdnCacheHandler(d *Daemon) DeleteFqdnCacheHandler {
   583  	return &deleteFqdnCache{daemon: d}
   584  }
   585  
   586  func (h *deleteFqdnCache) Handle(params DeleteFqdnCacheParams) middleware.Responder {
   587  	// endpoints we want to modify
   588  	endpoints := endpointmanager.GetEndpoints()
   589  
   590  	matchPatternStr := ""
   591  	if params.Matchpattern != nil {
   592  		matchPatternStr = *params.Matchpattern
   593  	}
   594  
   595  	namesToRegen, err := deleteDNSLookups(
   596  		h.daemon.dnsNameManager.GetDNSCache(),
   597  		h.daemon.dnsPoller.DNSHistory,
   598  		endpoints,
   599  		time.Now(),
   600  		matchPatternStr)
   601  	if err != nil {
   602  		return api.Error(DeleteFqdnCacheBadRequestCode, err)
   603  	}
   604  	h.daemon.dnsNameManager.ForceGenerateDNS(context.TODO(), namesToRegen)
   605  	return NewDeleteFqdnCacheOK()
   606  }
   607  
   608  type getFqdnCacheID struct {
   609  	daemon *Daemon
   610  }
   611  
   612  func NewGetFqdnCacheIDHandler(d *Daemon) GetFqdnCacheIDHandler {
   613  	return &getFqdnCacheID{daemon: d}
   614  }
   615  
   616  func (h *getFqdnCacheID) Handle(params GetFqdnCacheIDParams) middleware.Responder {
   617  	var endpoints []*endpoint.Endpoint
   618  	if params.ID != "" {
   619  		ep, err := endpointmanager.Lookup(params.ID)
   620  		switch {
   621  		case err != nil:
   622  			return api.Error(GetFqdnCacheIDBadRequestCode, err)
   623  		case ep == nil:
   624  			return api.Error(GetFqdnCacheIDNotFoundCode, fmt.Errorf("Cannot find endpoint %s", params.ID))
   625  		default:
   626  			endpoints = []*endpoint.Endpoint{ep}
   627  		}
   628  	}
   629  
   630  	CIDRStr := ""
   631  	if params.Cidr != nil {
   632  		CIDRStr = *params.Cidr
   633  	}
   634  
   635  	matchPatternStr := ""
   636  	if params.Matchpattern != nil {
   637  		matchPatternStr = *params.Matchpattern
   638  	}
   639  
   640  	lookups, err := extractDNSLookups(endpoints, CIDRStr, matchPatternStr)
   641  	switch {
   642  	case err != nil:
   643  		return api.Error(GetFqdnCacheBadRequestCode, err)
   644  	case len(lookups) == 0:
   645  		return NewGetFqdnCacheIDNotFound()
   646  	}
   647  
   648  	return NewGetFqdnCacheIDOK().WithPayload(lookups)
   649  }
   650  
   651  // extractDNSLookups returns API models.DNSLookup copies of DNS data in each
   652  // endpoint's DNSHistory. These are filtered by CIDRStr and matchPatternStr if
   653  // they are non-empty.
   654  func extractDNSLookups(endpoints []*endpoint.Endpoint, CIDRStr, matchPatternStr string) (lookups []*models.DNSLookup, err error) {
   655  	cidrMatcher := func(ip net.IP) bool { return true }
   656  	if CIDRStr != "" {
   657  		_, cidr, err := net.ParseCIDR(CIDRStr)
   658  		if err != nil {
   659  			return nil, err
   660  		}
   661  		cidrMatcher = func(ip net.IP) bool { return cidr.Contains(ip) }
   662  	}
   663  
   664  	nameMatcher := func(name string) bool { return true }
   665  	if matchPatternStr != "" {
   666  		matcher, err := matchpattern.Validate(matchpattern.Sanitize(matchPatternStr))
   667  		if err != nil {
   668  			return nil, err
   669  		}
   670  		nameMatcher = func(name string) bool { return matcher.MatchString(name) }
   671  	}
   672  
   673  	for _, ep := range endpoints {
   674  		for _, lookup := range ep.DNSHistory.Dump() {
   675  			if !nameMatcher(lookup.Name) {
   676  				continue
   677  			}
   678  
   679  			// The API model needs strings
   680  			IPStrings := make([]string, 0, len(lookup.IPs))
   681  
   682  			// only proceed if any IP matches the cidr selector
   683  			anIPMatches := false
   684  			for _, ip := range lookup.IPs {
   685  				anIPMatches = anIPMatches || cidrMatcher(ip)
   686  				IPStrings = append(IPStrings, ip.String())
   687  			}
   688  			if !anIPMatches {
   689  				continue
   690  			}
   691  
   692  			lookups = append(lookups, &models.DNSLookup{
   693  				Fqdn:           lookup.Name,
   694  				Ips:            IPStrings,
   695  				LookupTime:     strfmt.DateTime(lookup.LookupTime),
   696  				TTL:            int64(lookup.TTL),
   697  				ExpirationTime: strfmt.DateTime(lookup.ExpirationTime),
   698  				EndpointID:     int64(ep.ID),
   699  			})
   700  		}
   701  	}
   702  
   703  	return lookups, nil
   704  }
   705  
   706  func deleteDNSLookups(globalCache *fqdn.DNSCache, pollerCache *fqdn.DNSCache, endpoints []*endpoint.Endpoint, expireLookupsBefore time.Time, matchPatternStr string) (namesToRegen []string, err error) {
   707  	var nameMatcher *regexp.Regexp // nil matches all in our implementation
   708  	if matchPatternStr != "" {
   709  		nameMatcher, err = matchpattern.Validate(matchPatternStr)
   710  		if err != nil {
   711  			return nil, err
   712  		}
   713  	}
   714  
   715  	// Clear any to-delete entries globally
   716  	// Clear any to-delete entries from the poller cache.
   717  	// Clear any to-delete entries in each endpoint, then update globally to
   718  	// insert any entries that now should be in the global cache (because they
   719  	// provide an IP at the latest expiration time).
   720  	namesToRegen = append(namesToRegen, globalCache.ForceExpire(expireLookupsBefore, nameMatcher)...)
   721  	namesToRegen = append(namesToRegen, pollerCache.ForceExpire(expireLookupsBefore, nameMatcher)...)
   722  	for _, ep := range endpoints {
   723  		namesToRegen = append(namesToRegen, ep.DNSHistory.ForceExpire(expireLookupsBefore, nameMatcher)...)
   724  		globalCache.UpdateFromCache(ep.DNSHistory, nil)
   725  	}
   726  
   727  	return namesToRegen, nil
   728  }
   729  
   730  // readPreCache returns a fqdn.DNSCache object created from the json data at
   731  // preCachePath
   732  func readPreCache(preCachePath string) (cache *fqdn.DNSCache, err error) {
   733  	data, err := ioutil.ReadFile(preCachePath)
   734  	if err != nil {
   735  		return nil, err
   736  	}
   737  
   738  	cache = fqdn.NewDNSCache(0) // no per-host limit here
   739  	if err = cache.UnmarshalJSON(data); err != nil {
   740  		return nil, err
   741  	}
   742  	return cache, nil
   743  }