Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS improvements #3489

Merged
merged 20 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ab5c424
Improve DNS-sanity check and ensure that it never reaches the cluster.
thallgren Jan 12, 2024
c4e3af5
Remove search-path handling from the overriding DNS-resolver.
thallgren Jan 12, 2024
3b38ebe
Fix correct DNS response when A exists but AAAA doesn't, or vice-versa.
thallgren Jan 12, 2024
4ec6379
Use the name "routes" instead of "namespaces" for DNS routes.
thallgren Jan 17, 2024
b48a4ef
Simplify the flow of calls during DNS resolution.
thallgren Jan 17, 2024
187a6d2
Use an exported constant for DNS DefaultExcludeSuffixes.
thallgren Jan 17, 2024
49a0878
Let clusters LookupIP return NXNAME when it finds zero IPs or errors
thallgren Jan 17, 2024
ca31614
An intercept no longer changes DNS, so there's no point to wait for it.
thallgren Jan 17, 2024
ed464d7
Unify handling of search paths for all OSes.
thallgren Jan 17, 2024
9a03204
Ensure that iptables-legacy is imported to the telepresence client image
thallgren Jan 17, 2024
bc7cb29
Improve DNS-servers handling of recursion and the tel2-search domain.
thallgren Jan 17, 2024
0e22e24
Add integration tests for the DNS suffix logic.
thallgren Jan 17, 2024
038942a
Removing intercept from exclude test. It's not supposed to work.
thallgren Jan 16, 2024
c9d6f9b
Restore DNS dropSuffixes and use that for the tel2-search too.
thallgren Jan 17, 2024
a682c7b
Ensure that the DNS resolver uses lowercase names everywhere.
thallgren Jan 17, 2024
1a35c0a
Fix glitch in telepresence config view command.
thallgren Jan 17, 2024
29f64df
Disable traffic-manager's metriton reporting during integration testing
thallgren Jan 17, 2024
1af82d4
Disable root-daemon's metriton reporting when SCOUT_DISABLE=1
thallgren Jan 17, 2024
f251f3f
Fix correct generation of files under /etc/resolver for darwin systems.
thallgren Jan 17, 2024
82b6446
Skip the DNS suffix-rule test on linux-arm64.
thallgren Jan 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build-aux/docker/images/Dockerfile.client
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ RUN setcap 'cap_net_bind_service+ep' /usr/local/bin/telepresence
# The telepresence target is the one that gets published. It aims to be a small as possible.
FROM alpine as telepresence

RUN apk add --no-cache ca-certificates iptables bash
RUN apk add --no-cache ca-certificates iptables iptables-legacy bash
RUN rm /sbin/iptables && ln -s /sbin/iptables-legacy /sbin/iptables
RUN rm /sbin/ip6tables && ln -s /sbin/ip6tables-legacy /sbin/ip6tables

# the telepresence binary
COPY --from=telepresence-build /usr/local/bin/telepresence /usr/local/bin
Expand Down
4 changes: 4 additions & 0 deletions charts/telepresence/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ spec:
value: "{{ join " " . }}"
{{- end }}
{{- end }}
{{- if not .metritonEnabled }} # 0 is false
- name: SCOUT_DISABLE
value: "1"
{{- end }}
{{- /*
Client configuration
*/}}
Expand Down
3 changes: 3 additions & 0 deletions charts/telepresence/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ grpc:
# podCIDRs is the verbatim list of CIDRs used when the podCIDRStrategy is set to environment
podCIDRs: []

# Enable/disable metriton reporting to ambassador
metritonEnabled: true

# podCIDRStrategy controls what strategy the traffic-manager will use for finding out what
# CIDRs the cluster is using for its pods. Valid values are:
#
Expand Down
29 changes: 16 additions & 13 deletions integration_test/itest/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type Cluster interface {
CompatVersion() string
Executable() string
GeneralError() error
GlobalEnv() dos.MapEnv
GlobalEnv(context.Context) dos.MapEnv
AgentVersion(context.Context) string
Initialize(context.Context) context.Context
InstallTrafficManager(ctx context.Context, values map[string]string) error
Expand Down Expand Up @@ -372,12 +372,13 @@ func (s *cluster) withBasicConfig(c context.Context, t *testing.T) context.Conte
return c
}

func (s *cluster) GlobalEnv() dos.MapEnv {
func (s *cluster) GlobalEnv(ctx context.Context) dos.MapEnv {
globalEnv := dos.MapEnv{
"KUBECONFIG": s.kubeConfig,
}
yes := struct{}{}
includeEnv := map[string]struct{}{
"SCOUT_DISABLE": yes,
"HOME": yes,
"PATH": yes,
"LOGNAME": yes,
Expand All @@ -403,7 +404,7 @@ func (s *cluster) GlobalEnv() dos.MapEnv {
includeEnv["USERNAME"] = yes
includeEnv["windir"] = yes
}
for _, env := range os.Environ() {
for _, env := range dos.Environ(ctx) {
if eqIdx := strings.IndexByte(env, '='); eqIdx > 0 {
key := env[:eqIdx]
if _, ok := includeEnv[key]; ok {
Expand Down Expand Up @@ -633,16 +634,18 @@ func (s *cluster) TelepresenceHelmInstall(ctx context.Context, upgrade bool, set
}
nsl := nss.UniqueList()
vx := struct {
LogLevel string `json:"logLevel"`
Image *Image `json:"image,omitempty"`
Agent *xAgent `json:"agent,omitempty"`
ClientRbac xRbac `json:"clientRbac"`
ManagerRbac xRbac `json:"managerRbac"`
Client xClient `json:"client"`
LogLevel string `json:"logLevel"`
MetritonEnabled bool `json:"metritonEnabled"`
Image *Image `json:"image,omitempty"`
Agent *xAgent `json:"agent,omitempty"`
ClientRbac xRbac `json:"clientRbac"`
ManagerRbac xRbac `json:"managerRbac"`
Client xClient `json:"client"`
}{
LogLevel: "debug",
Image: GetImage(ctx),
Agent: agent,
LogLevel: "debug",
MetritonEnabled: false,
Image: GetImage(ctx),
Agent: agent,
ClientRbac: xRbac{
Create: true,
Namespaced: len(nss.ManagedNamespaces) > 0,
Expand Down Expand Up @@ -775,7 +778,7 @@ func Command(ctx context.Context, executable string, args ...string) *dexec.Cmd
}

func EnvironMap(ctx context.Context) dos.MapEnv {
env := GetGlobalHarness(ctx).GlobalEnv()
env := GetGlobalHarness(ctx).GlobalEnv(ctx)
maps.Merge(env, getEnv(ctx))
return env
}
Expand Down
2 changes: 1 addition & 1 deletion integration_test/itest/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func GetUser(ctx context.Context) string {

func LookupEnv(ctx context.Context, key string) (value string, ok bool) {
if value, ok = getEnv(ctx)[key]; !ok {
value, ok = GetGlobalHarness(ctx).GlobalEnv()[key]
value, ok = GetGlobalHarness(ctx).GlobalEnv(ctx)[key]
}
return
}
Expand Down
205 changes: 172 additions & 33 deletions integration_test/kubeconfig_extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package integration_test
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/url"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
Expand All @@ -18,6 +21,8 @@ import (
"github.com/datawire/dlib/dlog"
"github.com/datawire/dlib/dtime"
"github.com/telepresenceio/telepresence/v2/integration_test/itest"
"github.com/telepresenceio/telepresence/v2/pkg/client"
"github.com/telepresenceio/telepresence/v2/pkg/client/rootd/dns"
"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
"github.com/telepresenceio/telepresence/v2/pkg/iputil"
"github.com/telepresenceio/telepresence/v2/pkg/routing"
Expand Down Expand Up @@ -210,45 +215,179 @@ func (s *notConnectedSuite) Test_ConflictingProxies() {
}
}

func (s *notConnectedSuite) Test_DNSIncludes() {
ctx := s.Context()
func (s *notConnectedSuite) Test_DNSSuffixRules() {
if s.IsCI() && runtime.GOOS == "linux" && runtime.GOARCH == "arm64" {
s.T().Skip("The DNS on the linux-arm64 GitHub runner is not configured correctly")
}

ctx = itest.WithKubeConfigExtension(ctx, func(cluster *api.Cluster) map[string]any {
return map[string]any{"dns": map[string][]string{"include-suffixes": {".org"}}}
})
require := s.Require()
logFile := filepath.Join(filelocation.AppUserLogDir(ctx), "daemon.log")
const randomName = "zwslkjsdf"
const randomDomain = ".xnrqj"
tests := []struct {
name string
domainName string
includeSuffixes []string
excludeSuffixes []string
wantedLogEntry []string
mustHaveWanted bool
unwantedLogEntry []string
}{
{
"default-exclude-com",
randomName + ".com",
nil,
nil,
[]string{
`Cluster DNS excluded by exclude-suffix ".com" for name "` + randomName + `.com"`,
},
false,
[]string{
`Lookup A "` + randomName + `.com`,
},
},
{
"default-exclude-random-domain",
randomName + randomDomain,
nil,
nil,
[]string{
`Cluster DNS excluded for name "` + randomName + randomDomain + `". No inclusion rule was matched`,
},
false,
[]string{
`Lookup A "` + randomName + randomDomain,
},
},
{
"include-random-domain",
randomName + randomDomain,
[]string{randomDomain},
dns.DefaultExcludeSuffixes,
[]string{
`Cluster DNS included by include-suffix "` + randomDomain + `" for name "` + randomName + randomDomain,
`Lookup A "` + randomName + randomDomain,
},
true,
nil,
},
{
"equally specific include overrides exclude",
randomName + ".org",
[]string{".org"},
dns.DefaultExcludeSuffixes,
[]string{
`Cluster DNS included by include-suffix ".org" (overriding exclude-suffix ".org") for name "` + randomName + `.org"`,
`Lookup A "` + randomName + `.org."`,
},
true,
nil,
},
{
"more specific include overrides exclude",
randomName + ".my-domain.org",
[]string{".my-domain.org"},
dns.DefaultExcludeSuffixes,
[]string{
`Cluster DNS included by include-suffix ".my-domain.org" (overriding exclude-suffix ".org") for name "` + randomName + `.my-domain.org"`,
`Lookup A "` + randomName + `.my-domain.org."`,
},
true,
nil,
},
{
"more specific exclude overrides include",
randomName + ".my-domain.org",
[]string{".org"},
[]string{".com", ".my-domain.org"},
[]string{
`Cluster DNS excluded by exclude-suffix ".my-domain.org" for name "` + randomName + `.my-domain.org"`,
},
true,
[]string{
`Lookup A "` + randomName + `.my-domain.org."`,
},
},
}
logFile := filepath.Join(filelocation.AppUserLogDir(s.Context()), "daemon.log")

s.TelepresenceConnect(ctx, "--context", "extra")
defer itest.TelepresenceDisconnectOk(ctx)
for _, tt := range tests {
tt := tt
s.Run(tt.name, func() {
ctx := itest.WithKubeConfigExtension(s.Context(), func(cluster *api.Cluster) map[string]any {
return map[string]any{
"dns": map[string][]string{
"exclude-suffixes": tt.excludeSuffixes,
"include-suffixes": tt.includeSuffixes,
},
}
})
require := s.Require()

// Check that config view -c includes the includeSuffixes
stdout := itest.TelepresenceOk(ctx, "config", "view", "--client-only")
require.Contains(stdout, " includeSuffixes:\n - .org")
s.TelepresenceConnect(ctx, "--context", "extra")
defer itest.TelepresenceDisconnectOk(ctx)

retryCount := 0
s.Eventually(func() bool {
// Test with ".org" suffix that was added as an include-suffix
host := fmt.Sprintf("zwslkjsdf-%d.org", retryCount)
short, cancel := context.WithTimeout(ctx, 20*time.Millisecond)
defer cancel()
_, _ = net.DefaultResolver.LookupIPAddr(short, host)
// Check that config view -c includes the includeSuffixes
var cfg client.SessionConfig
stdout := itest.TelepresenceOk(ctx, "config", "view", "--client-only", "--output", "json")
require.NoError(json.Unmarshal([]byte(stdout), &cfg))
require.Equal(cfg.DNS.ExcludeSuffixes, tt.excludeSuffixes)
require.Equal(cfg.DNS.IncludeSuffixes, tt.includeSuffixes)

// Give query time to reach telepresence and produce a log entry
dtime.SleepWithContext(ctx, 100*time.Millisecond)
rootLog, err := os.Open(logFile)
require.NoError(err)
defer rootLog.Close()

rootLog, err := os.Open(logFile)
require.NoError(err)
defer rootLog.Close()
// Figure out where the current end of the logfile is. This must be done before any
// of the tests run because the queries that the DNS resolver receives are dependent
// on how the system's DNS resolver handle search paths and caching.
st, err := rootLog.Stat()
s.Require().NoError(err)
pos := st.Size()

scanFor := fmt.Sprintf(`Lookup A "%s."`, host)
scn := bufio.NewScanner(rootLog)
for scn.Scan() {
if strings.Contains(scn.Text(), scanFor) {
return true
short, cancel := context.WithTimeout(ctx, 20*time.Millisecond)
defer cancel()
_, _ = net.DefaultResolver.LookupIPAddr(short, tt.domainName)

// Give query time to reach telepresence and produce a log entry
dtime.SleepWithContext(ctx, 100*time.Millisecond)

for _, wl := range tt.wantedLogEntry {
_, err = rootLog.Seek(pos, io.SeekStart)
require.NoError(err)
scn := bufio.NewScanner(rootLog)
found := false

// mustHaveWanted caters for cases where the default behavior from the system's resolver
// is to not send unwanted queries to our resolver at all (based on search and routes).
// It is forced to true for inclusion tests.
mustHaveWanted := tt.mustHaveWanted
for scn.Scan() {
txt := scn.Text()
if strings.Contains(txt, wl) {
found = true
break
}
if !mustHaveWanted {
if strings.Contains(txt, " ServeDNS ") && strings.Contains(txt, tt.domainName) {
mustHaveWanted = true
}
}
}
s.Truef(found || !mustHaveWanted, "Unable to find %q", wl)
}
}
retryCount++
return false
}, 30*time.Second, time.Second, "daemon.log does not contain expected LookupHost entry")

for _, wl := range tt.unwantedLogEntry {
_, err = rootLog.Seek(pos, io.SeekStart)
require.NoError(err)
scn := bufio.NewScanner(rootLog)
found := false
for scn.Scan() {
if strings.Contains(scn.Text(), wl) {
found = true
break
}
}
s.Falsef(found, "Found unwanted %q", wl)
}
})
}
}
2 changes: 0 additions & 2 deletions integration_test/uhn_dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package integration_test
import (
"fmt"
"net"
"strconv"
"time"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -56,7 +55,6 @@ func (s *unqualifiedHostNameDNSSuite) Test_UHNExcludes() {

// when
s.TelepresenceConnect(ctx, "--context", "extra")
itest.TelepresenceOk(ctx, "intercept", serviceName, "--port", strconv.Itoa(port))

// then
for _, excluded := range excludes {
Expand Down
14 changes: 12 additions & 2 deletions pkg/client/cli/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,19 @@ func configView() *cobra.Command {
func runConfigView(cmd *cobra.Command, _ []string) error {
var cfg client.SessionConfig
clientOnly, _ := cmd.Flags().GetBool("client-only")
if !clientOnly {
cmd.Annotations = map[string]string{
ann.Session: ann.Required,
}
if err := connect.InitCommand(cmd); err != nil {
// Unable to establish a session, so try to convey the local config instead. It
// may be helpful in diagnosing the problem.
cmd.Annotations = map[string]string{}
clientOnly = true
}
}

if clientOnly {
// Unable to establish a session, so try to convey the local config instead. It
// may be helpful in diagnosing the problem.
if err := connect.InitCommand(cmd); err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/client/cli/connect/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func launchDaemon(ctx context.Context, cr *daemon.Request) error {
if cr != nil && cr.RootDaemonProfilingPort > 0 {
args = append(args, "--pprof", strconv.Itoa(int(cr.RootDaemonProfilingPort)))
}
if os.Getenv("SCOUT_DISABLE") == "1" {
args = append(args, "--disable-metriton")
}
args = append(args, logDir, filelocation.AppUserConfigDir(ctx))
return proc.StartInBackgroundAsRoot(ctx, args...)
}
Expand Down
Loading
Loading