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

Add support for EKS Pod Identity #416

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

tongshen-stephanie
Copy link

@tongshen-stephanie tongshen-stephanie commented Jan 8, 2025

Issue #, if available: Closes #300

Description of changes:
Introduce an optional parameter usePodIdentity to support EKS Pod Identity

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@tongshen-stephanie tongshen-stephanie requested a review from a team as a code owner January 8, 2025 17:53
@tongshen-stephanie tongshen-stephanie changed the title Add support for Pod Idenity Add support for EKS Pod Identity Jan 8, 2025
@codecov-commenter
Copy link

codecov-commenter commented Jan 8, 2025

Codecov Report

Attention: Patch coverage is 68.42105% with 72 lines in your changes missing coverage. Please review.

Project coverage is 52.62%. Comparing base (be94dd0) to head (4dd8774).

Files with missing lines Patch % Lines
...ntial_provider/pod_identity_credential_provider.go 70.43% 28 Missing and 6 partials ⚠️
auth/auth.go 31.42% 23 Missing and 1 partial ⚠️
credential_provider/irsa_credential_provider.go 83.33% 10 Missing ⚠️
provider/parameter_store_provider.go 0.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #416      +/-   ##
==========================================
+ Coverage   49.67%   52.62%   +2.94%     
==========================================
  Files           9       11       +2     
  Lines         781      952     +171     
==========================================
+ Hits          388      501     +113     
- Misses        383      436      +53     
- Partials       10       15       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

README.md Outdated Show resolved Hide resolved
auth/auth.go Outdated Show resolved Hide resolved
auth/auth.go Show resolved Hide resolved
auth/auth.go Outdated
return nil, err
}
credProvider := NewIRSACredentialProvider(p.stsClient, *roleArn, p.region)
config, err = credProvider.GetAWSConfig(fetcher)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

		config, err = credProvider.GetAWSConfig(fetcher)
		if err != nil {
			return nil, err
		}

is common to both blocks of the if statement, it can be extracted out.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made change in commit c3ca813

auth/auth.go Outdated
}
}

func (p *PodIdentityCredentialProvider) GetAWSConfig(fetcher *authTokenFetcher) (*aws.Config, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move the two credential providers to their own files in a subfolder?

Copy link
Author

@tongshen-stephanie tongshen-stephanie Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean creating a separate folder/package similar to auth? I didn't do it because moving credential providers to a new folder would also require moving authTokenFetcher at the same time (otherwise it causes circular dependency since credential provider and auth try to import each other). auth is pretty much empty after moving out those 2 big pieces.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made the change in commit 703570b

auth/auth.go Outdated
return podIdentityAgentEndpointIPv4
}()

var podIdentityAgentEndpoint = defaultPodIdentityAgentEndpoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is defaultPodIdentityAgentEndpoint used anywhere else? If not you can remove the extra assignment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultPodIdentityAgentEndpoint is used in tests. The endpoint is overridden in test, so assign it back to default state after test completion.

auth/auth.go Outdated
return nil, roleErr
}
credProvider := NewIRSACredentialProvider(p.stsClient, *roleArn, p.region)
config, err = credProvider.GetAWSConfig(fetcher)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you missed config, err = credProvider.GetAWSConfig(fetcher)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for missing this. corrected in commit 703570b

auth/auth.go Outdated
config, err = credProvider.GetAWSConfig(fetcher)

} else {
roleArn, roleErr := p.getRoleARN()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be the responsibility of the IRSA credential provider, let's move it to that provider's implementation in its constructor.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made the move in commit 703570b

@@ -0,0 +1,279 @@
package credential_provider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also split this test file into two test files for both implementations of the credential providers?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made the change in commit 539f715

server/server.go Outdated
}

if usePodIdentity {
klog.Infof("Using Pod Identity for authentication in namespace: %s, service account: %s", nameSpace, svcAcct)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be logged from here for better encapsulation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made the change in commit 539f715

@@ -53,6 +53,10 @@ spec:
env:
- name: AWS_USE_FIPS_ENDPOINT
value: {{ .Values.useFipsEndpoint | quote }}
- name: POD_IP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for good measure, let's leave a comment above this explaining that this is needed for the pod identity agent to choose between IPV4/IPV6

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in commit 539f715

spec:
selector:
app: nginx
app: nginx-irsas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like an extra trailing 's'.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in commit 4d4df72. Thank you for catching!

func NewAuth(
ctx context.Context,
region, nameSpace, svcAcc string,
region, nameSpace, svcAcc, podName string,
usePodIdentity bool,
k8sClient k8sv1.CoreV1Interface,
) (auth *Auth, e error) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not use sess nor stsClient when usePodIdentity is true. Can move the sts.new(sess) call up here and condition it all on !usePodIdentity (setting stsClient to nil otherwise)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change made in commit 4d4df72

README.md Outdated
@@ -8,14 +8,14 @@ AWS offers two services to manage secrets and parameters conveniently in your co
## Installation

### Requirements
* Amazon Elastic Kubernetes Service (EKS) 1.17+ running an EC2 node group (Fargate node groups are not supported **[^1]**)
* Amazon Elastic Kubernetes Service (EKS) 1.24+ running an EC2 node group (Fargate node groups are not supported **[^1]**)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing this is being driven by Pod Identity only being supported on 1.24. I know 1.17 is no longer supported but this does not match what is required by the helm chart and if we change that it could break users running ancient installations out there who need to reload this driver for some reason.

Instead, we could just leave this at 1.17 and mention somewhere below that using Pod Identity requires EKS 1.24.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change made in commit 4d4df72

Comment on lines 56 to 59
- name: POD_IP # This is needed to choose between IPv4 and IPv6 Pod Identity Agent endpoint
valueFrom:
fieldRef:
fieldPath: status.podIP
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all inside an {{- if .Values.useFipsEndpoint }} condition so when using the helm chart install option it will never be set unless the administrator turns on FIPS. We could move the "if FIPS" condition to be just around the FIP env variable, but it would be better to just not need to pass in the address in configuration and dynamically determine the address. See comments below.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed setting POD_IP env variable in daemonset template and installers in commit 4d4df72

Comment on lines 29 to 42
var defaultPodIdentityAgentEndpoint = func() string {
isIPv6, err := isIPv6()
if err != nil {
klog.Warningf("Error determining IP version: %v. Defaulting to IPv4 endpoint", err)
return podIdentityAgentEndpointIPv4
}

if isIPv6 {
klog.Infof("Using Pod Identity Agent IPv6 endpoint")
return podIdentityAgentEndpointIPv6
}
klog.Infof("Using Pod Identity Agent IPv4 endpoint")
return podIdentityAgentEndpointIPv4
}()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking up the address type is cumbersome and even if we get it right, there are a lot of ways the network could get mis-configured. A more reliable approach is to just try both addresses in quick succession. Try IPV4 with a 100ms timeout, and if that fails log the error as a warning and then try IPV6 with the same timeout and only fail if both tries fail.

This adds a slight penalty for IPV6 only configurations so we would also want to give the option to turn off IPV4. To make this general we would probably let the user turn off either (maybe something like preferedAddressType = IPV4/IPV6).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

introduce a new optional parameter preferredAddressType in commit 4d4df72.

Implemented behavior:

  • if preferredAddressType is set to ipv4, only try ipv4
  • if preferredAddressType is set to ipv6, only try ipv6
  • if preferredAddressType is not set or is set with value other than ipv4/ipv6, try ipv4 first with 100ms timeout and then try ipv6. if both tries fail, error out.

},
}

// Use the K8s API to fetch the token from the OIDC provider.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case there likely will not be an OIDC provider.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in commit 4d4df72

Comment on lines 78 to 80
if preferredAddressType != "" {
klog.Warningf("Unknown preferred address type: %s, falling back to auto selection", preferredAddressType)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly failing on bad configuration is the best option. It makes problems easier to find and it is difficult to add in later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made change in commit c88e0f6

Comment on lines 133 to 159
switch p.preferredEndpoint {
case preferenceIPv4:
klog.Infof("Using preferred Pod Identity Agent IPv4 endpoint")
config, err := p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv4)
if err != nil {
return nil, fmt.Errorf("failed to get AWS config from pod identity agent IPv4 endpoint: %+v", err)
}
return config, nil
case preferenceIPv6:
klog.Infof("Using preferred Pod Identity Agent IPv6 endpoint")
config, err := p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv6)
if err != nil {
return nil, fmt.Errorf("failed to get AWS config from pod identity agent IPv6 endpoint: %+v", err)
}
return config, nil
default:
klog.Infof("Using auto Pod Identity Agent endpoint selection")
config, err := p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv4)
if err != nil {
klog.Warningf("IPv4 endpoint attempt failed: %+v. Trying IPv6 endpoint", err)
config, err = p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv6)
if err != nil {
return nil, fmt.Errorf("failed to get AWS config from pod identity agent: %+v", err)
}
}
return config, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a little more natural to solve things in general by avoiding handling each case separately. For example:

if( p.preferredEndpoint == preferenceIPv4 || p.preferredEndpoint == preferenceAuto ) {
    config, err := p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv4)
    if err != nil {
        klog.Warningf("IPv4 endpoint attempt failed: %+v.", err)
    } else {
        return config, nil;
    }
}
if(  p.preferredEndpoint == preferenceIPv6 || p.preferredEndpoint == preferenceAuto ) {
    config, err := p.getAWSConfigFromPodIdentityAgent(token, podIdentityAgentEndpointIPv6)
    if err != nil {
        klog.Warningf("IPv6 endpoint attempt failed: %+v.", err)
    }
}
if err != nil {
    return nil, fmt.Errorf("failed to get AWS config from pod identity agent: %+v", err);
}

return config, nil;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made change in commit c88e0f6. This does look a lot cleaner. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pod Identity Association support
4 participants