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

Fix EKS cluster detection #1532

Merged
merged 11 commits into from
Feb 11, 2025
Merged
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
47 changes: 46 additions & 1 deletion translator/util/eksdetector/eksdetector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ package eksdetector

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"sync"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -15,6 +18,7 @@ import (

type Detector interface {
getConfigMap(namespace string, name string) (map[string]string, error)
getIssuer() (string, error)
}

type EksDetector struct {
Expand Down Expand Up @@ -55,7 +59,8 @@ var (
}

// IsEKS checks if the agent is running on EKS. This is done by using the kubernetes API to determine if the aws-auth
// configmap exists in the kube-system namespace
// configmap exists in the kube-system namespace or by extracting the "iss" field from the service account token and
// checking if it contains "eks" as a fall-back
IsEKS = func() IsEKSCache {
once.Do(func() {
var errors error
Expand All @@ -71,6 +76,11 @@ var (
awsAuth, err := eksDetector.getConfigMap(authConfigNamespace, authConfigConfigMap)
if err == nil {
value = awsAuth != nil
} else {
issuer, err := eksDetector.getIssuer()
if err == nil {
value = strings.Contains(strings.ToLower(issuer), "eks")
}
}
}
isEKSCacheSingleton = IsEKSCache{Value: value, Err: errors}
Expand All @@ -90,6 +100,41 @@ func (d *EksDetector) getConfigMap(namespace string, name string) (map[string]st
return configMap.Data, nil
}

// getIssuer retrieves the issuer ("iss") from the service account token
func (d *EksDetector) getIssuer() (string, error) {
conf, err := getInClusterConfig()
if err != nil {
return "", fmt.Errorf("failed to get in-cluster config: %w", err)
}

token := conf.BearerToken
if token == "" {
return "", fmt.Errorf("empty token in config")
}

parts := strings.Split(token, ".")
if len(parts) < 2 {
return "", fmt.Errorf("missing payload")
}

decoded, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return "", fmt.Errorf("failed to decode token payload: %w", err)
}

var claims map[string]interface{}
if err = json.Unmarshal(decoded, &claims); err != nil {
return "", fmt.Errorf("failed to unmarshal token payload: %w", err)
}

iss, ok := claims["iss"].(string)
if !ok {
return "", fmt.Errorf("issuer field not found in token")
}

return iss, nil
}

func getClient() (kubernetes.Interface, error) {
//Get cluster config
confs, err := getInClusterConfig()
Expand Down
26 changes: 26 additions & 0 deletions translator/util/eksdetector/eksdetector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package eksdetector

import (
"encoding/base64"
"fmt"
"testing"

Expand Down Expand Up @@ -54,9 +55,17 @@ func TestEKS(t *testing.T) {
}

testDetector.On("getConfigMap", authConfigNamespace, authConfigConfigMap).Return(map[string]string{conventions.AttributeK8SClusterName: "my-cluster"}, nil)

isEks := IsEKS()
assert.True(t, isEks.Value)
assert.NoError(t, isEks.Err)

testDetector.On("getConfigMap", authConfigNamespace, authConfigConfigMap).Return(nil, fmt.Errorf("configmap not found"))
testDetector.On("getIssuer").Return("https://oidc.eks.us-west-2.amazonaws.com/id/someid", nil)

isEks = IsEKS()
assert.True(t, isEks.Value)
assert.NoError(t, isEks.Err)
}

func Test_getConfigMap(t *testing.T) {
Expand All @@ -82,6 +91,23 @@ func Test_getConfigMap(t *testing.T) {
assert.NotNil(t, res)
}

func Test_getIssuer(t *testing.T) {
client := fake.NewSimpleClientset()
testDetector := &EksDetector{Clientset: client}

payload := `{"iss":"https://oidc.eks.us-west-2.amazonaws.com/id/someid"}`
encodedPayload := base64.RawURLEncoding.EncodeToString([]byte(payload))
dummyToken := "header." + encodedPayload + ".signature"

getInClusterConfig = func() (*rest.Config, error) {
return &rest.Config{BearerToken: dummyToken}, nil
}

issuer, err := testDetector.getIssuer()
assert.NoError(t, err)
assert.Equal(t, "https://oidc.eks.us-west-2.amazonaws.com/id/someid", issuer)
}

func Test_getClientError(t *testing.T) {
//InClusterConfig error
getInClusterConfig = func() (*rest.Config, error) {
Expand Down
7 changes: 6 additions & 1 deletion translator/util/eksdetector/eksdetectortestutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var (
return &EksDetector{Clientset: fake.NewSimpleClientset()}, nil
}

// TestIsEKSCacheEKS os used for unit testing EKS route
// TestIsEKSCacheEKS is used for unit testing EKS route
TestIsEKSCacheEKS = func() IsEKSCache {
return IsEKSCache{Value: true, Err: nil}
}
Expand All @@ -44,3 +44,8 @@ func (detector *MockDetector) getConfigMap(namespace string, name string) (map[s
args := detector.Called(namespace, name)
return args.Get(0).(map[string]string), args.Error(1)
}

func (detector *MockDetector) getIssuer() (string, error) {
args := detector.Called()
return args.Get(0).(string), args.Error(1)
}