mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
[vnet] get upstream nameservers on Windows (#51738)
This commit is contained in:
@@ -218,6 +218,7 @@ require (
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/time v0.9.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
google.golang.org/api v0.219.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287
|
||||
google.golang.org/grpc v1.70.0
|
||||
|
||||
@@ -2853,6 +2853,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2025 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
|
||||
// OSUpstreamNameserverSource provides the list of upstream DNS nameservers
|
||||
// configured in the OS. The VNet DNS resolver will forward unhandles queries to
|
||||
// these nameservers.
|
||||
type OSUpstreamNameserverSource struct {
|
||||
ttlCache *utils.FnCache
|
||||
}
|
||||
|
||||
// NewOSUpstreamNameserverSource returns a new *OSUpstreamNameserverSource.
|
||||
func NewOSUpstreamNameserverSource() (*OSUpstreamNameserverSource, error) {
|
||||
ttlCache, err := utils.NewFnCache(utils.FnCacheConfig{
|
||||
TTL: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &OSUpstreamNameserverSource{
|
||||
ttlCache: ttlCache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpstreamNameservers returns a cached view of the host OS's current default
|
||||
// nameservers.
|
||||
func (s *OSUpstreamNameserverSource) UpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
return utils.FnCacheGet(ctx, s.ttlCache, 0, loadUpstreamNameservers)
|
||||
}
|
||||
|
||||
func loadUpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
return platformLoadUpstreamNameservers(ctx)
|
||||
}
|
||||
|
||||
func withDNSPort(addr netip.Addr) string {
|
||||
return netip.AddrPortFrom(addr, 53).String()
|
||||
}
|
||||
@@ -20,47 +20,24 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
confFilePath = "/etc/resolv.conf"
|
||||
)
|
||||
|
||||
// OSUpstreamNameserverSource provides the list of upstream DNS nameservers
|
||||
// configured in the OS. The VNet DNS resolver will forward unhandles queries to
|
||||
// these nameservers.
|
||||
type OSUpstreamNameserverSource struct {
|
||||
ttlCache *utils.FnCache
|
||||
}
|
||||
|
||||
// NewOSUpstreamNameserverSource returns a new *OSUpstreamNameserverSource.
|
||||
func NewOSUpstreamNameserverSource() (*OSUpstreamNameserverSource, error) {
|
||||
ttlCache, err := utils.NewFnCache(utils.FnCacheConfig{
|
||||
TTL: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return &OSUpstreamNameserverSource{
|
||||
ttlCache: ttlCache,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpstreamNameservers returns a cached view of the host OS's current default
|
||||
// nameservers, as found in /etc/resolv.conf.
|
||||
func (s *OSUpstreamNameserverSource) UpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
return utils.FnCacheGet(ctx, s.ttlCache, 0, s.upstreamNameservers)
|
||||
}
|
||||
|
||||
func (s *OSUpstreamNameserverSource) upstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
// platformLoadUpstreamNameservers reads the OS DNS nameservers found in
|
||||
// /etc/resolv.conf. The comments in that file make it clear it is not actually
|
||||
// consulted for DNS hostname resolution, but MacOS seems to keep it up to date
|
||||
// with the current default nameservers as configured for the OS, and it is the
|
||||
// easiest place to read them. Eventually we should probably use a better
|
||||
// method, but for now this works.
|
||||
func platformLoadUpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
f, err := os.Open(confFilePath)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err, "opening %s", confFilePath)
|
||||
@@ -80,23 +57,13 @@ func (s *OSUpstreamNameserverSource) upstreamNameservers(ctx context.Context) ([
|
||||
}
|
||||
address := fields[1]
|
||||
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
slog.DebugContext(ctx, "Skipping invalid IP", "ip", address)
|
||||
ip, err := netip.ParseAddr(address)
|
||||
if err != nil {
|
||||
slog.DebugContext(ctx, "Skipping invalid IP", "ip", address, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add port 53 suffix, the only port supported on MacOS.
|
||||
var nameserver string
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
nameserver = address + ":53"
|
||||
case ip.To16() != nil:
|
||||
nameserver = "[" + address + "]:53"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
nameservers = append(nameservers, nameserver)
|
||||
nameservers = append(nameservers, withDNSPort(ip))
|
||||
}
|
||||
|
||||
slog.DebugContext(ctx, "Loaded host upstream nameservers.", "nameservers", nameservers, "config_file", confFilePath)
|
||||
|
||||
@@ -29,14 +29,11 @@ import (
|
||||
var (
|
||||
// vnetNotImplemented is an error indicating that VNet is not implemented on the host OS.
|
||||
vnetNotImplemented = &trace.NotImplementedError{Message: "VNet is not implemented on " + runtime.GOOS}
|
||||
|
||||
// Satisfy unused linter.
|
||||
_ = withDNSPort
|
||||
)
|
||||
|
||||
type OSUpstreamNameserverSource struct{}
|
||||
|
||||
func NewOSUpstreamNameserverSource() (*OSUpstreamNameserverSource, error) {
|
||||
return nil, trace.Wrap(vnetNotImplemented)
|
||||
}
|
||||
|
||||
func (s *OSUpstreamNameserverSource) UpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
func platformLoadUpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
return nil, trace.Wrap(vnetNotImplemented)
|
||||
}
|
||||
|
||||
@@ -16,22 +16,51 @@
|
||||
|
||||
package dns
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/netip"
|
||||
"sort"
|
||||
|
||||
// OSUpstreamNameserverSource provides the list of upstream DNS nameservers
|
||||
// configured in the OS. The VNet DNS resolver will forward unhandles queries to
|
||||
// these nameservers.
|
||||
type OSUpstreamNameserverSource struct{}
|
||||
"github.com/gravitational/trace"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
)
|
||||
|
||||
// NewOSUpstreamNameserverSource returns a new *OSUpstreamNameserverSource.
|
||||
func NewOSUpstreamNameserverSource() (*OSUpstreamNameserverSource, error) {
|
||||
return &OSUpstreamNameserverSource{}, nil
|
||||
// platformLoadUpstreamNameservers attempts to find the default DNS nameservers
|
||||
// that VNet should forward unmatched queries to. To do this, it finds the
|
||||
// nameservers configured for each interface and sorts by the interface metric.
|
||||
func platformLoadUpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
interfaces, err := winipcfg.GetIPInterfaceTable(windows.AF_INET)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err, "looking up local network interfaces")
|
||||
}
|
||||
sort.Slice(interfaces, func(i, j int) bool {
|
||||
return interfaces[i].Metric < interfaces[j].Metric
|
||||
})
|
||||
var nameservers []string
|
||||
for _, iface := range interfaces {
|
||||
ifaceNameservers, err := iface.InterfaceLUID.DNS()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err, "looking up DNS nameservers for interface")
|
||||
}
|
||||
for _, ifaceNameserver := range ifaceNameservers {
|
||||
if ignoreUpstreamNameserver(ifaceNameserver) {
|
||||
continue
|
||||
}
|
||||
nameservers = append(nameservers, withDNSPort(ifaceNameserver))
|
||||
}
|
||||
}
|
||||
slog.DebugContext(ctx, "Loaded host upstream nameservers", "nameservers", nameservers)
|
||||
return nameservers, nil
|
||||
}
|
||||
|
||||
// UpstreamNameservers is net yet implemented and currently returns a nil/empty
|
||||
// list of upstream nameservers. It does not return an error so that the
|
||||
// networking stack can actually run without just immediately exiting.
|
||||
func (s *OSUpstreamNameserverSource) UpstreamNameservers(ctx context.Context) ([]string, error) {
|
||||
// TODO(nklaassen): implement UpstreamNameservers on windows.
|
||||
return nil, nil
|
||||
var (
|
||||
// Ignore site-local addresses, which Windows seems to have set on
|
||||
// most interfaces, implementing a deprecated IPv6 spec.
|
||||
siteLocalPrefix = netip.MustParsePrefix("fec0::/10")
|
||||
)
|
||||
|
||||
func ignoreUpstreamNameserver(addr netip.Addr) bool {
|
||||
return siteLocalPrefix.Contains(addr)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user