Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
177 changes: 176 additions & 1 deletion pkg/cloudmetadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package cloudmetadata
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"

apitypes "github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/go-logger"
Expand Down Expand Up @@ -54,8 +58,179 @@ func GetCloudMetadataWithIMDS(ctx context.Context) (*apitypes.CloudMetadata, err

cMetadata, err := cMetadataClient.GetMetadata(ctx)
if err != nil {
return nil, err
// Try DigitalOcean metadata endpoints as a fallback (e.g., droplets) if IMDS didn't work.
if doMeta, derr := fetchDigitalOceanMetadata(ctx); derr == nil && doMeta != nil {
logger.L().Info("retrieved cloud metadata from DigitalOcean metadata service as fallback")
return doMeta, nil
}

// Try GCP metadata endpoints as a fallback
if gcpMeta, gerr := fetchGCPMetadata(ctx); gerr == nil && gcpMeta != nil {
logger.L().Info("retrieved cloud metadata from GCP metadata service as fallback")
return gcpMeta, nil
}

// Try Azure metadata endpoints as a fallback
if azureMeta, aerr := fetchAzureMetadata(ctx); aerr == nil && azureMeta != nil {
logger.L().Info("retrieved cloud metadata from Azure metadata service as fallback")
return azureMeta, nil
}

// Wrap the underlying error with additional context so logs make it clearer why metadata is missing.
// This helps surface issues like IMDS token endpoint failures (e.g. IMDSv2 token 404), unreachable metadata endpoints,
// or provider-specific metadata problems.
return nil, fmt.Errorf("failed to get cloud metadata from IMDS: %w", err)
}

return cMetadata, nil
}

// fetchDigitalOceanMetadata attempts to fetch basic metadata from DigitalOcean's metadata service.
//
// It probes the metadata root and queries a few commonly available endpoints.
// It returns a non-nil error if it does not look like DigitalOcean's metadata service or no useful values were found.
func fetchDigitalOceanMetadata(ctx context.Context) (*apitypes.CloudMetadata, error) {
client := &http.Client{
Timeout: 2 * time.Second,
}
base := "http://169.254.169.254/metadata/v1/"

// Probe root to see whether the metadata endpoint responds and contains expected entries.
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, base, nil)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("digitalocean metadata root returned status: %d", resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
bstr := string(body)
// Basic heuristic: the DO metadata root typically lists resources like 'id', 'hostname', 'region' etc.
if !strings.Contains(bstr, "id") && !strings.Contains(bstr, "region") && !strings.Contains(bstr, "hostname") {
return nil, fmt.Errorf("digitalocean metadata root missing expected entries")
}

// helper to fetch a single textual endpoint and return trimmed result or empty string
get := func(path string) string {
url := base + path
r, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp2, err2 := client.Do(r)
if err2 != nil || resp2.StatusCode != 200 {
if resp2 != nil {
resp2.Body.Close()
}
return ""
}
defer resp2.Body.Close()
b, _ := io.ReadAll(resp2.Body)
return strings.TrimSpace(string(b))
}

id := get("id")
if id == "" {
id = get("droplet_id")
}
hostname := get("hostname")
region := get("region")
instanceType := get("size")
if instanceType == "" {
instanceType = get("type")
}
privateIP := get("interfaces/private/0/ipv4/address")
publicIP := get("interfaces/public/0/ipv4/address")

// if nothing useful was obtained, return an error so callers can continue trying other fallbacks
if id == "" && hostname == "" && region == "" && privateIP == "" && publicIP == "" && instanceType == "" {
return nil, fmt.Errorf("digitalocean metadata endpoints returned no data")
}

return &apitypes.CloudMetadata{
Provider: "digitalocean",
InstanceID: id,
InstanceType: instanceType,
Region: region,
PrivateIP: privateIP,
PublicIP: publicIP,
Hostname: hostname,
}, nil
}

// fetchGCPMetadata attempts to fetch basic metadata from GCP's metadata service.
func fetchGCPMetadata(ctx context.Context) (*apitypes.CloudMetadata, error) {
client := &http.Client{
Timeout: 2 * time.Second,
}
base := "http://metadata.google.internal/computeMetadata/v1/instance/"

get := func(path string) string {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, base+path, nil)
req.Header.Set("Metadata-Flavor", "Google")
resp, err := client.Do(req)
if err != nil || resp.StatusCode != 200 {
if resp != nil {
resp.Body.Close()
}
return ""
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
return strings.TrimSpace(string(b))
}

machineType := get("machine-type")
if machineType == "" {
return nil, fmt.Errorf("not a GCP instance")
}

// GCP returns full path like "projects/12345/machineTypes/n1-standard-1"
parts := strings.Split(machineType, "/")
instanceType := parts[len(parts)-1]

return &apitypes.CloudMetadata{
Provider: "gcp",
InstanceID: get("id"),
InstanceType: instanceType,
Zone: get("zone"),
Hostname: get("hostname"),
}, nil
}

// fetchAzureMetadata attempts to fetch basic metadata from Azure's metadata service.
func fetchAzureMetadata(ctx context.Context) (*apitypes.CloudMetadata, error) {
client := &http.Client{
Timeout: 2 * time.Second,
}
base := "http://169.254.169.254/metadata/instance/compute/"

get := func(path string) string {
url := base + path + "?api-version=2021-02-01&format=text"
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
req.Header.Set("Metadata", "true")
resp, err := client.Do(req)
if err != nil || resp.StatusCode != 200 {
if resp != nil {
resp.Body.Close()
}
return ""
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
return strings.TrimSpace(string(b))
}

vmSize := get("vmSize")
if vmSize == "" {
return nil, fmt.Errorf("not an Azure instance")
}

return &apitypes.CloudMetadata{
Provider: "azure",
InstanceID: get("vmId"),
InstanceType: vmSize,
Region: get("location"),
Zone: get("zone"),
Hostname: get("name"),
}, nil
}
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ func (bt *BpfTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(bt.ociStore),
)
go func() {
err := bt.runtime.RunGadget(bt.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := bt.runtime.RunGadget(bt.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", bt.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
1 change: 1 addition & 0 deletions pkg/containerwatcher/v2/tracers/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func (ct *CapabilitiesTracer) Start(ctx context.Context) error {
params := map[string]string{
"operator.oci.ebpf.collect-kstack": "false",
"operator.oci.ebpf.unique": "true",
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := ct.runtime.RunGadget(ct.gadgetCtx, nil, params)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/containerwatcher/v2/tracers/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func (dt *DNSTracer) Start(ctx context.Context) error {
)
go func() {
params := map[string]string{
"operator.oci.ebpf.paths": "true", // CWD paths in events
"operator.oci.ebpf.paths": "true", // CWD paths in events
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := dt.runtime.RunGadget(dt.gadgetCtx, nil, params)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/containerwatcher/v2/tracers/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func (et *ExecTracer) Start(ctx context.Context) error {
)
go func() {
params := map[string]string{
"operator.oci.ebpf.paths": "true", // CWD paths in events
"operator.oci.ebpf.paths": "true", // CWD paths in events
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := et.runtime.RunGadget(et.gadgetCtx, nil, params)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ func (et *ExitTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(et.ociStore),
)
go func() {
err := et.runtime.RunGadget(et.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := et.runtime.RunGadget(et.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", et.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ func (ft *ForkTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(ft.ociStore),
)
go func() {
err := ft.runtime.RunGadget(ft.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := ft.runtime.RunGadget(ft.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", ft.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/hardlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ func (ht *HardlinkTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(ht.ociStore),
)
go func() {
err := ht.runtime.RunGadget(ht.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := ht.runtime.RunGadget(ht.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", ht.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ func (ht *HTTPTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(ht.ociStore),
)
go func() {
err := ht.runtime.RunGadget(ht.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := ht.runtime.RunGadget(ht.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", ht.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/iouring.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ func (it *IoUringTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(it.ociStore),
)
go func() {
err := it.runtime.RunGadget(it.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := it.runtime.RunGadget(it.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", it.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/kmod.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ func (kt *KmodTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(kt.ociStore),
)
go func() {
err := kt.runtime.RunGadget(kt.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := kt.runtime.RunGadget(kt.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", kt.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/containerwatcher/v2/tracers/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ func (nt *NetworkTracer) Start(ctx context.Context) error {
)
go func() {
params := map[string]string{
"operator.oci.annotate": "network:kubenameresolver.enable=true",
"operator.oci.annotate": "network:kubenameresolver.enable=true",
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := nt.runtime.RunGadget(nt.gadgetCtx, nil, params)
if err != nil {
Expand Down
7 changes: 2 additions & 5 deletions pkg/containerwatcher/v2/tracers/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func (ot *OpenTracer) Start(ctx context.Context) error {
)
go func() {
params := map[string]string{
"operator.oci.ebpf.paths": strconv.FormatBool(ot.cfg.EnableFullPathTracing),
"operator.oci.ebpf.paths": strconv.FormatBool(ot.cfg.EnableFullPathTracing),
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := ot.runtime.RunGadget(ot.gadgetCtx, nil, params)
if err != nil {
Expand Down Expand Up @@ -128,10 +129,6 @@ func (ot *OpenTracer) eventOperator() operators.DataOperator {

// callback handles open events from the tracer
func (ot *OpenTracer) callback(event utils.OpenEvent) {
if event.GetContainer() == "" {
return
}

errorRaw := event.GetError()
if errorRaw > -1 {
// Handle the event with syscall enrichment
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/ptrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ func (pt *PtraceTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(pt.ociStore),
)
go func() {
err := pt.runtime.RunGadget(pt.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := pt.runtime.RunGadget(pt.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", pt.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/randomx.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ func (rt *RandomXTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(rt.ociStore),
)
go func() {
err := rt.runtime.RunGadget(rt.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := rt.runtime.RunGadget(rt.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", rt.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ func (st *SSHTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(st.ociStore),
)
go func() {
err := st.runtime.RunGadget(st.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := st.runtime.RunGadget(st.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", st.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/containerwatcher/v2/tracers/symlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ func (st *SymlinkTracer) Start(ctx context.Context) error {
gadgetcontext.WithOrasReadonlyTarget(st.ociStore),
)
go func() {
err := st.runtime.RunGadget(st.gadgetCtx, nil, nil)
params := map[string]string{
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := st.runtime.RunGadget(st.gadgetCtx, nil, params)
if err != nil {
logger.L().Error("Error running gadget", helpers.String("gadget", st.gadgetCtx.Name()), helpers.Error(err))
}
Expand Down
1 change: 1 addition & 0 deletions pkg/containerwatcher/v2/tracers/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (st *SyscallTracer) Start(ctx context.Context) error {
params := map[string]string{
"operator.oci.ebpf.map-fetch-count": "0",
"operator.oci.ebpf.map-fetch-interval": "30s",
"operator.LocalManager.host": "true", // don't error if container-collection is nil when using local manager
}
err := st.runtime.RunGadget(st.gadgetCtx, nil, params)
if err != nil {
Expand Down
Loading
Loading