Skip to content

Commit

Permalink
Disable DNS name resolver by default (#1032)
Browse files Browse the repository at this point in the history
* Disable name resolver by default

* Fine-grained DNS selection

* defaulting to k8s name resolution only
  • Loading branch information
mariomac authored Jul 19, 2024
1 parent 37f2e0c commit c8e6a58
Show file tree
Hide file tree
Showing 24 changed files with 179 additions and 51 deletions.
1 change: 1 addition & 0 deletions pkg/beyla/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var DefaultConfig = Config{
},
},
NameResolver: &transform.NameResolverConfig{
Sources: []string{"k8s"},
CacheLen: 1024,
CacheTTL: 5 * time.Minute,
},
Expand Down
2 changes: 2 additions & 0 deletions pkg/beyla/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ network:
require.NoError(t, os.Setenv("BEYLA_INTERNAL_METRICS_PROMETHEUS_PORT", "3210"))
require.NoError(t, os.Setenv("GRAFANA_CLOUD_SUBMIT", "metrics,traces"))
require.NoError(t, os.Setenv("KUBECONFIG", "/foo/bar"))
require.NoError(t, os.Setenv("BEYLA_NAME_RESOLVER_SOURCES", "k8s,dns"))
defer unsetEnv(t, map[string]string{
"KUBECONFIG": "",
"BEYLA_OPEN_PORT": "", "BEYLA_EXECUTABLE_NAME": "", "OTEL_SERVICE_NAME": "", "BEYLA_NOOP_TRACES": "",
Expand Down Expand Up @@ -176,6 +177,7 @@ network:
Unmatch: transform.UnmatchHeuristic,
},
NameResolver: &transform.NameResolverConfig{
Sources: []string{"k8s", "dns"},
CacheLen: 1024,
CacheTTL: 5 * time.Minute,
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/export/attributes/attr_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"slices"

attr "github.com/grafana/beyla/pkg/export/attributes/names"
"github.com/grafana/beyla/pkg/internal/helpers"
maps2 "github.com/grafana/beyla/pkg/internal/helpers/maps"
)

// Default is true if an attribute must be reported by default,
Expand Down Expand Up @@ -52,7 +52,7 @@ func (p *AttrSelector) For(metricName Name) []attr.Name {
allInclusionLists := p.selector.Matching(metricName)
if len(allInclusionLists) == 0 {
// if the user did not provide any selector, return the default attributes for that metric
attrs := helpers.SetToSlice(attributeNames.Default())
attrs := maps2.SetToSlice(attributeNames.Default())
slices.Sort(attrs)
return attrs
}
Expand All @@ -68,7 +68,7 @@ func (p *AttrSelector) For(metricName Name) []attr.Name {
p.rmExcludedAttributes(matchingAttrs, il)
}

sas := helpers.SetToSlice(matchingAttrs)
sas := maps2.SetToSlice(matchingAttrs)
slices.Sort(sas)
return sas
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/internal/discover/attacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/grafana/beyla/pkg/beyla"
"github.com/grafana/beyla/pkg/internal/ebpf"
"github.com/grafana/beyla/pkg/internal/goexec"
"github.com/grafana/beyla/pkg/internal/helpers"
"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/internal/imetrics"
"github.com/grafana/beyla/pkg/internal/svc"
)
Expand All @@ -31,7 +31,7 @@ type TraceAttacher struct {
// processInstances keeps track of the instances of each process. This will help making sure
// that we don't remove the BPF resources of an executable until all their instances are removed
// are stopped
processInstances helpers.MultiCounter[uint64]
processInstances maps.MultiCounter[uint64]

// keeps a copy of all the tracers for a given executable path
existingTracers map[uint64]*ebpf.ProcessTracer
Expand All @@ -45,7 +45,7 @@ func TraceAttacherProvider(ta *TraceAttacher) pipe.FinalProvider[[]Event[Instrum
func (ta *TraceAttacher) attacherLoop() (pipe.FinalFunc[[]Event[Instrumentable]], error) {
ta.log = slog.With("component", "discover.TraceAttacher")
ta.existingTracers = map[uint64]*ebpf.ProcessTracer{}
ta.processInstances = helpers.MultiCounter[uint64]{}
ta.processInstances = maps.MultiCounter[uint64]{}
ta.pinPath = BuildPinPath(ta.Cfg)

if err := ta.init(); err != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/internal/discover/watcher_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/mariomac/pipes/pipe"
"k8s.io/client-go/tools/cache"

"github.com/grafana/beyla/pkg/internal/helpers"
"github.com/grafana/beyla/pkg/internal/helpers/container"
"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/internal/kube"
"github.com/grafana/beyla/pkg/services"
)
Expand Down Expand Up @@ -41,7 +41,7 @@ type watcherKubeEnricher struct {
// we use our own indexer instead an informer indexer because we need a 1:N relation while
// the other indices provide N:1 relation
// level-1 key: replicaset ns/name. Level-2 key: Pod name
podsByOwner helpers.Map2[nsName, string, *kube.PodInfo]
podsByOwner maps.Map2[nsName, string, *kube.PodInfo]

podsInfoCh chan Event[*kube.PodInfo]
rsInfoCh chan Event[*kube.ReplicaSetInfo]
Expand Down Expand Up @@ -84,7 +84,7 @@ func (wk *watcherKubeEnricher) init() error {
wk.log = slog.With("component", "discover.watcherKubeEnricher")
wk.containerByPID = map[PID]container.Info{}
wk.processByContainer = map[string]processAttrs{}
wk.podsByOwner = helpers.Map2[nsName, string, *kube.PodInfo]{}
wk.podsByOwner = maps.Map2[nsName, string, *kube.PodInfo]{}

// the podsInfoCh channel will receive any update about pods being created or deleted
wk.podsInfoCh = make(chan Event[*kube.PodInfo], 10)
Expand Down
45 changes: 45 additions & 0 deletions pkg/internal/helpers/maps/bits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package maps

// Bits wraps an unsigned integer that can be used as a bit map
type Bits uint

type builderOpts[T any] struct {
transform []func(T) T
}

// BuilderOpt allows defining option for building Bits map in the MappedBits method
type BuilderOpt[T any] func(*builderOpts[T])

// WithTransform will apply the provided transformer function to the passed key values
// in the MappedBits constructor function
func WithTransform[T any](transformFunc func(T) T) BuilderOpt[T] {
return func(o *builderOpts[T]) {
o.transform = append(o.transform, transformFunc)
}
}

// MappedBits builds a Bits map from a set of values (e.g. strings) that are mapped in the form
// value --> corresponding Bits value
// in the "maps" constructor argument
func MappedBits[T comparable](values []T, maps map[T]Bits, opts ...BuilderOpt[T]) Bits {
bo := builderOpts[T]{}
for _, opt := range opts {
opt(&bo)
}

b := Bits(0)
for _, value := range values {
for _, t := range bo.transform {
value = t(value)
}
if val, ok := maps[value]; ok {
b |= val
}
}
return b
}

// Has returns true if the map contains all the value Bits passed as argument
func (i Bits) Has(value Bits) bool {
return i&value == value
}
37 changes: 37 additions & 0 deletions pkg/internal/helpers/maps/bits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package maps

import (
"testing"

"github.com/stretchr/testify/assert"
)

type key int

var mapper = map[key]Bits{1: 0b0001, 2: 0b0010, 3: 0b0100, 4: 0b1000}

func TestBits_Full(t *testing.T) {
bits := MappedBits([]key{1, 2, 3, 4}, mapper)
assert.True(t, bits.Has(0b0001))
assert.True(t, bits.Has(0b0010))
assert.True(t, bits.Has(0b0100))
assert.True(t, bits.Has(0b1000))
}

func TestBits_Empty(t *testing.T) {
bits := MappedBits(nil, mapper)
assert.False(t, bits.Has(0b0001))
assert.False(t, bits.Has(0b0010))
assert.False(t, bits.Has(0b0100))
assert.False(t, bits.Has(0b1000))
}

func TestBits_Transform(t *testing.T) {
bits := MappedBits([]key{10, 30, 8910}, mapper,
WithTransform(func(k key) key { return k / 10 }))
assert.True(t, bits.Has(0b0001))
assert.False(t, bits.Has(0b0010))
assert.True(t, bits.Has(0b0100))
assert.False(t, bits.Has(0b1000))
assert.False(t, bits.Has(0xb10000)) // key non-existing i the mappers
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package helpers
package maps

// MultiCounter maps a counter to a given key
type MultiCounter[K comparable] map[K]*int
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package helpers
package maps

import (
"slices"
Expand Down
4 changes: 3 additions & 1 deletion pkg/internal/kube/informer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"

"github.com/grafana/beyla/pkg/internal/helpers/maps"
)

const (
Expand Down Expand Up @@ -52,7 +54,7 @@ type Metadata struct {

containerEventHandlers []ContainerEventHandler

disabledInformers informerType
disabledInformers maps.Bits
}

// PodInfo contains precollected metadata for Pods.
Expand Down
3 changes: 2 additions & 1 deletion pkg/internal/kube/informer_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"k8s.io/client-go/kubernetes"

"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/kubeflags"
)

Expand All @@ -21,7 +22,7 @@ type MetadataProvider struct {
syncTimeout time.Duration

enable atomic.Value
disabledInformers informerType
disabledInformers maps.Bits
}

func NewMetadataProvider(
Expand Down
38 changes: 18 additions & 20 deletions pkg/internal/kube/informer_type.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
package kube

import "strings"
import (
"strings"

type informerType int
"github.com/grafana/beyla/pkg/internal/helpers/maps"
)

const (
InformerService = informerType(1 << iota)
InformerService = maps.Bits(1 << iota)
InformerReplicaSet
InformerNode
)

func informerTypes(str []string) informerType {
it := informerType(0)
for _, s := range str {
switch strings.ToLower(s) {
case "service", "services":
it |= InformerService
case "replicaset", "replicasets":
it |= InformerReplicaSet
case "node", "nodes":
it |= InformerNode
}
}
return it
}

func (i informerType) Has(it informerType) bool {
return i&it != 0
func informerTypes(str []string) maps.Bits {
return maps.MappedBits(
str,
map[string]maps.Bits{
"service": InformerService,
"services": InformerService,
"replicaset": InformerReplicaSet,
"replicasets": InformerReplicaSet,
"node": InformerNode,
"nodes": InformerNode,
},
maps.WithTransform(strings.ToLower),
)
}
43 changes: 32 additions & 11 deletions pkg/transform/name_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,30 @@ import (
"github.com/mariomac/pipes/pipe"

attr "github.com/grafana/beyla/pkg/export/attributes/names"
"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/internal/pipe/global"
"github.com/grafana/beyla/pkg/internal/request"
"github.com/grafana/beyla/pkg/internal/svc"
kube2 "github.com/grafana/beyla/pkg/internal/transform/kube"
)

const (
ResolverDNS = maps.Bits(1 << iota)
ResolverK8s
)

func resolverSources(str []string) maps.Bits {
return maps.MappedBits(str, map[string]maps.Bits{
"dns": ResolverDNS,
"k8s": ResolverK8s,
"kube": ResolverK8s,
"kubernetes": ResolverK8s,
}, maps.WithTransform(strings.ToLower))
}

type NameResolverConfig struct {
// Sources for name resolving. Accepted values: dns, k8s
Sources []string `yaml:"sources" env:"BEYLA_NAME_RESOLVER_SOURCES" envSeparator:"," envDefault:"k8s"`
// CacheLen specifies the max size of the LRU cache that is checked before
// performing the name lookup. Default: 256
CacheLen int `yaml:"cache_len" env:"BEYLA_NAME_RESOLVER_CACHE_LEN"`
Expand All @@ -30,11 +47,13 @@ type NameResolver struct {
cache *expirable.LRU[string, string]
cfg *NameResolverConfig
db *kube2.Database

sources maps.Bits
}

func NameResolutionProvider(ctxInfo *global.ContextInfo, cfg *NameResolverConfig) pipe.MiddleProvider[[]request.Span, []request.Span] {
return func() (pipe.MiddleFunc[[]request.Span, []request.Span], error) {
if cfg == nil {
if cfg == nil || len(cfg.Sources) == 0 {
return pipe.Bypass[[]request.Span](), nil
}
return nameResolver(ctxInfo, cfg)
Expand All @@ -43,9 +62,10 @@ func NameResolutionProvider(ctxInfo *global.ContextInfo, cfg *NameResolverConfig

func nameResolver(ctxInfo *global.ContextInfo, cfg *NameResolverConfig) (pipe.MiddleFunc[[]request.Span, []request.Span], error) {
nr := NameResolver{
cfg: cfg,
db: ctxInfo.AppO11y.K8sDatabase,
cache: expirable.NewLRU[string, string](cfg.CacheLen, nil, cfg.CacheTTL),
cfg: cfg,
db: ctxInfo.AppO11y.K8sDatabase,
cache: expirable.NewLRU[string, string](cfg.CacheLen, nil, cfg.CacheTTL),
sources: resolverSources(cfg.Sources),
}

return func(in <-chan []request.Span, out chan<- []request.Span) {
Expand Down Expand Up @@ -129,7 +149,7 @@ func (nr *NameResolver) dnsResolve(svc *svc.ID, ip string) (string, string) {
return "", ""
}

if nr.db != nil {
if nr.sources.Has(ResolverK8s) && nr.db != nil {
ipAddr := net.ParseIP(ip)

if ipAddr != nil && !ipAddr.IsLoopback() {
Expand All @@ -141,14 +161,15 @@ func (nr *NameResolver) dnsResolve(svc *svc.ID, ip string) (string, string) {
}
}

n := nr.resolveIP(ip)
if n == ip {
if nr.sources.Has(ResolverDNS) {
n := nr.resolveIP(ip)
if n == ip {
return n, svc.Namespace
}
n = nr.cleanName(svc, ip, n)
return n, svc.Namespace
}

n = nr.cleanName(svc, ip, n)

return n, svc.Namespace
return "", ""
}

func (nr *NameResolver) resolveFromK8s(ip string) (string, string) {
Expand Down
Loading

0 comments on commit c8e6a58

Please sign in to comment.