github.com/fafucoder/cilium@v1.6.11/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 }