diff --git a/cmd/containerboot/egressservices.go b/cmd/containerboot/egressservices.go index 6b7c085ab..3e902b1cc 100644 --- a/cmd/containerboot/egressservices.go +++ b/cmd/containerboot/egressservices.go @@ -202,7 +202,6 @@ func (ep *egressProxy) syncEgressConfigs(cfgs *egressservices.Configs, status *e // Delete unnecessary services. if err := ep.deleteUnnecessaryServices(cfgs, status); err != nil { return nil, fmt.Errorf("error deleting services: %w", err) - } newStatus := &egressservices.Status{} if !wantsServicesConfigured(cfgs) { @@ -476,7 +475,7 @@ func (ep *egressProxy) tailnetTargetIPsForSvc(svc egressservices.Config, n ipn.N log.Printf("netmap is not available, unable to determine backend addresses for %s", svc.TailnetTarget.FQDN) return addrs, nil } - egressAddrs, err := resolveTailnetFQDN(n.NetMap, svc.TailnetTarget.FQDN) + egressAddrs, err := resolveTailnetFQDN(context.Background(), ep.tsClient, svc.TailnetTarget.FQDN) if err != nil || len(egressAddrs) == 0 { log.Printf("tailnet target %q does not have any backend addresses, skipping", svc.TailnetTarget.FQDN) return addrs, nil @@ -529,7 +528,6 @@ func (ep *egressProxy) shouldResync(n ipn.Notify) bool { // ensureServiceDeleted ensures that any rules for an egress service are removed // from the firewall configuration. func ensureServiceDeleted(svcName string, svc *egressservices.ServiceStatus, nfr linuxfw.NetfilterRunner) error { - // Note that the portmap is needed for iptables based firewall only. // Nftables group rules for a service in a chain, so there is no need to // specify individual portmapping based rules. diff --git a/cmd/containerboot/main.go b/cmd/containerboot/main.go index 133dc6e60..2c611591b 100644 --- a/cmd/containerboot/main.go +++ b/cmd/containerboot/main.go @@ -117,7 +117,9 @@ import ( "syscall" "time" + "golang.org/x/net/dns/dnsmessage" "golang.org/x/sys/unix" + "tailscale.com/client/local" "tailscale.com/client/tailscale" "tailscale.com/ipn" kubeutils "tailscale.com/k8s-operator" @@ -528,7 +530,7 @@ runLoop: } } if cfg.TailnetTargetFQDN != "" { - egressAddrs, err := resolveTailnetFQDN(n.NetMap, cfg.TailnetTargetFQDN) + egressAddrs, err := resolveTailnetFQDN(ctx, client, cfg.TailnetTargetFQDN) if err != nil { log.Print(err.Error()) break @@ -884,23 +886,51 @@ func runHTTPServer(mux *http.ServeMux, addr string) (close func() error) { // resolveTailnetFQDN resolves a tailnet FQDN to a list of IP prefixes, which // can be either a peer device or a Tailscale Service. -func resolveTailnetFQDN(nm *netmap.NetworkMap, fqdn string) ([]netip.Prefix, error) { +func resolveTailnetFQDN(ctx context.Context, c *local.Client, fqdn string) ([]netip.Prefix, error) { dnsFQDN, err := dnsname.ToFQDN(fqdn) if err != nil { return nil, fmt.Errorf("error parsing %q as FQDN: %w", fqdn, err) } - // Check all peer devices first. - for _, p := range nm.Peers { - if strings.EqualFold(p.Name(), dnsFQDN.WithTrailingDot()) { - return p.Addresses().AsSlice(), nil + bytes, _, err := c.QueryDNS(ctx, dnsFQDN.WithTrailingDot(), "A") + if err != nil { + return nil, fmt.Errorf("error querying tailscale DNS: %w", err) + } + + var p dnsmessage.Parser + header, err := p.Start(bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse DNS response: %w", err) + } + p.SkipAllQuestions() + if header.RCode != dnsmessage.RCodeSuccess { + return nil, fmt.Errorf("no answers in DNS query response for FQDN %q", dnsFQDN.WithTrailingDot()) + } + + answers, err := p.AllAnswers() + if err != nil { + return nil, fmt.Errorf("failed to parse DNS answers: %w", err) + } + + addrs := make([]netip.Prefix, len(answers)) + for _, a := range answers { + if a.Header.Type == dnsmessage.TypeA { + ar, ok := a.Body.(*dnsmessage.AResource) + if !ok { + log.Printf("error: failed to cast body to *dnsmessage.AResource") + continue + } + + addr := netip.AddrFrom4(ar.A) + prefix := netip.PrefixFrom(addr, 32) + + log.Printf("adding prefix %q\n", prefix.String()) + addrs = append(addrs, prefix) } } - // If not found yet, check for a matching Tailscale Service. - if svcIPs := serviceIPsFromNetMap(nm, dnsFQDN); len(svcIPs) != 0 { - log.Printf("[warning] MagicDNS name %s is a Tailscale Service. If accept-routes is not enabled, routing to this destination will not work", fqdn) - return svcIPs, nil + if len(addrs) > 0 { + return addrs, nil } return nil, fmt.Errorf("could not find Tailscale node or service %q; it either does not exist, or not reachable because of ACLs", fqdn)