Skip to content

Commit

Permalink
Discover EKS: handle invalid Auth Mode and Private only endpoint (#48303
Browse files Browse the repository at this point in the history
)

During the EKS Enrollment in the Discover wizard, when listing clusters,
check for pre-reqs when showing available clusters.

1. When in Cloud, only allow enrollment if the cluster has a Public
   Endpoint for configuration.

1. Only show clusters which have the API authentication mode enabled
   (either API only or API_AND_CONFIG_MAP).
  • Loading branch information
marcoandredinis authored Nov 4, 2024
1 parent 2f74062 commit 3f96801
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 30 deletions.
28 changes: 21 additions & 7 deletions lib/integrations/awsoidc/eks_list_clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ type EKSCluster struct {

// Status is a current status of an EKS cluster in AWS.
Status string

// AuthenticationMode contains the authentication mode of the cluster.
// Expected values are: API, API_AND_CONFIG_MAP, CONFIG_MAP.
// Only API and API_AND_CONFIG_MAP are supported when installing the Teleport Helm chart.
// https://aws.amazon.com/blogs/containers/a-deep-dive-into-simplified-amazon-eks-access-management-controls/
AuthenticationMode string

// EndpointPublicAddress indicates whether the Cluster's VPC Config has its endpoint as a public address.
// For Teleport Cloud, this is required to access the cluster and proceed with the installation.
EndpointPublicAddress bool
}

// ListEKSClustersResponse contains a page of AWS EKS Clusters.
Expand Down Expand Up @@ -152,19 +162,23 @@ func ListEKSClusters(ctx context.Context, clt ListEKSClustersClient, req ListEKS
return nil
}

extraLabels, err := getExtraEKSLabels(eksClusterInfo.Cluster)
cluster := eksClusterInfo.Cluster

extraLabels, err := getExtraEKSLabels(cluster)
if err != nil {
ret.ClusterFetchingErrors[clusterName] = err
return nil
}

ret.Clusters = append(ret.Clusters, EKSCluster{
Name: aws.ToString(eksClusterInfo.Cluster.Name),
Region: req.Region,
Arn: aws.ToString(eksClusterInfo.Cluster.Arn),
Labels: eksClusterInfo.Cluster.Tags,
JoinLabels: extraLabels,
Status: strings.ToLower(string(eksClusterInfo.Cluster.Status)),
Name: aws.ToString(cluster.Name),
Region: req.Region,
Arn: aws.ToString(cluster.Arn),
Labels: cluster.Tags,
JoinLabels: extraLabels,
Status: strings.ToLower(string(cluster.Status)),
AuthenticationMode: string(cluster.AccessConfig.AuthenticationMode),
EndpointPublicAddress: cluster.ResourcesVpcConfig.EndpointPublicAccess,
})
return nil
})
Expand Down
66 changes: 61 additions & 5 deletions lib/integrations/awsoidc/eks_list_clusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ func TestListEKSClusters(t *testing.T) {
Arn: aws.String(fmt.Sprintf("%s_%d", baseArn, c)),
Tags: map[string]string{"label": "value"},
Status: "active",
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
})
}

Expand Down Expand Up @@ -153,6 +159,12 @@ func TestListEKSClusters(t *testing.T) {
Name: aws.String("EKS"),
Status: "active",
Tags: map[string]string{"label": "value"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
},
expectedClusters: []EKSCluster{
Expand All @@ -166,7 +178,9 @@ func TestListEKSClusters(t *testing.T) {
"region": "us-east-1",
"teleport.dev/cloud": "AWS",
},
Status: "active",
Status: "active",
AuthenticationMode: "API",
EndpointPublicAddress: true,
},
},
expectedFetchingErrors: map[string]error{},
Expand All @@ -179,12 +193,24 @@ func TestListEKSClusters(t *testing.T) {
Name: aws.String("EKS"),
Status: "active",
Tags: map[string]string{"label": "value"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
{
Arn: aws.String(baseArn + "2"),
Name: aws.String("EKS2"),
Status: "active",
Tags: map[string]string{"label2": "value2"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
},
expectedClusters: []EKSCluster{
Expand All @@ -198,7 +224,9 @@ func TestListEKSClusters(t *testing.T) {
"region": "us-east-1",
"teleport.dev/cloud": "AWS",
},
Status: "active",
Status: "active",
AuthenticationMode: "API",
EndpointPublicAddress: true,
},
{
Name: "EKS2",
Expand All @@ -210,25 +238,51 @@ func TestListEKSClusters(t *testing.T) {
"region": "us-east-1",
"teleport.dev/cloud": "AWS",
},
Status: "active",
Status: "active",
AuthenticationMode: "API",
EndpointPublicAddress: true,
},
},
expectedFetchingErrors: map[string]error{},
},
{
name: "two clusters, one success, one error",
name: "three clusters, one success, two error",
inputEKSClusters: []eksTypes.Cluster{
{
Arn: aws.String(baseArn),
Name: aws.String("EKS"),
Status: "active",
Tags: map[string]string{"label": "value"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
{
Arn: aws.String(baseArn),
Name: aws.String("erroredCluster"),
Status: "active",
Tags: map[string]string{"label2": "value2"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeApi,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
{
Arn: aws.String(baseArn),
Name: aws.String("erroredCluster"),
Status: "active",
Tags: map[string]string{"label2": "value2"},
AccessConfig: &eksTypes.AccessConfigResponse{
AuthenticationMode: eksTypes.AuthenticationModeConfigMap,
},
ResourcesVpcConfig: &eksTypes.VpcConfigResponse{
EndpointPublicAccess: true,
},
},
},
expectedClusters: []EKSCluster{
Expand All @@ -242,7 +296,9 @@ func TestListEKSClusters(t *testing.T) {
"region": "us-east-1",
"teleport.dev/cloud": "AWS",
},
Status: "active",
Status: "active",
AuthenticationMode: "API",
EndpointPublicAddress: true,
},
},
expectedFetchingErrors: map[string]error{"erroredCluster": errors.New("erroredCluster")},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
labelMatcher,
} from 'teleport/Discover/Shared';

import cfg from 'teleport/config';

import { CheckedEksCluster } from './EnrollEksCluster';

type Props = {
Expand Down Expand Up @@ -121,26 +123,54 @@ function getStatus(item: CheckedEksCluster) {
}
}

function disabledStates(item: CheckedEksCluster, autoDiscovery: boolean) {
const disabled =
getStatus(item) !== ItemStatus.Success ||
item.kubeServerExists ||
autoDiscovery;
function disabledStates(
item: CheckedEksCluster,
autoDiscovery: boolean
): { disabled: boolean; disabledText: string } {
if (autoDiscovery) {
return {
disabled: true,
disabledText: 'All eligible EKS clusters will be enrolled automatically',
};
}

let disabledText = `This EKS cluster is already enrolled and is a part of this cluster`;
switch (item.status) {
case 'failed':
case 'pending':
case 'creating':
case 'updating':
disabledText = 'Not available, try refreshing the list';
break;
case 'deleting':
disabledText = 'Not available';
if (item.kubeServerExists) {
return {
disabled: true,
disabledText:
'This EKS cluster is already enrolled and is a part of this cluster',
};
}
if (autoDiscovery) {
disabledText = 'All eligible EKS clusters will be enrolled automatically';

if (cfg.isCloud && !item.endpointPublicAddress) {
return {
disabled: true,
disabledText:
'Please enable endpoint public access in your EKS cluster and try again.',
};
}

if (
item.authenticationMode !== 'API' &&
item.authenticationMode !== 'API_AND_CONFIG_MAP'
) {
return {
disabled: true,
disabledText:
'Only API and API_AND_CONFIG_MAP authentication modes are supported.',
};
}

if (['pending', 'creating', 'updating'].includes(item.status)) {
return {
disabled: true,
disabledText: 'Not available, try refreshing the list',
};
}

if (['failed', 'deleting'].includes(item.status)) {
return { disabled: true, disabledText: 'Not available' };
}

return { disabled, disabledText };
return { disabled: false, disabledText: '' };
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ const mockEKSClusters: AwsEksCluster[] = [
status: 'active',
labels: [],
joinLabels: [],
authenticationMode: 'API',
endpointPublicAddress: true,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ const eksClusters: AwsEksCluster[] = [
{ name: 'region', value: 'us-east-1' },
{ name: 'account-id', value: '1234567789012' },
],
authenticationMode: 'API',
endpointPublicAddress: true,
},
{
name: 'EKS2',
Expand All @@ -315,6 +317,8 @@ const eksClusters: AwsEksCluster[] = [
{ name: 'region', value: 'us-east1' },
{ name: 'account-id', value: '1234567789012' },
],
authenticationMode: 'API',
endpointPublicAddress: true,
},
{
name: 'EKS3',
Expand All @@ -327,5 +331,35 @@ const eksClusters: AwsEksCluster[] = [
{ name: 'region', value: 'us-east-1' },
{ name: 'account-id', value: '1234567789012' },
],
authenticationMode: 'API',
endpointPublicAddress: true,
},
{
name: 'EKS4',
region: 'us-east-1',
accountId: '123456789012',
status: 'active',
labels: [{ name: 'env', value: 'prod' }],
joinLabels: [
{ name: 'teleport.dev/cloud', value: 'AWS' },
{ name: 'region', value: 'us-east-1' },
{ name: 'account-id', value: '1234567789012' },
],
authenticationMode: 'CONFIG_MAP',
endpointPublicAddress: true,
},
{
name: 'EKS5',
region: 'us-east-1',
accountId: '123456789012',
status: 'active',
labels: [{ name: 'env', value: 'prod' }],
joinLabels: [
{ name: 'teleport.dev/cloud', value: 'AWS' },
{ name: 'region', value: 'us-east-1' },
{ name: 'account-id', value: '1234567789012' },
],
authenticationMode: 'API_AND_CONFIG_MAP',
endpointPublicAddress: false,
},
];
13 changes: 13 additions & 0 deletions web/packages/teleport/src/services/integrations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,19 @@ export type AwsEksCluster = {
* joinLabels contains labels that should be injected into teleport kube agent, if EKS cluster is being enrolled.
*/
joinLabels: Label[];

/**
* AuthenticationMode is the cluster's configured authentication mode.
* You can read more about the Authentication Modes here: https://aws.amazon.com/blogs/containers/a-deep-dive-into-simplified-amazon-eks-access-management-controls/
*/
authenticationMode: 'API' | 'API_AND_CONFIG_MAP' | 'CONFIG_MAP';

/**
* EndpointPublicAddress indicates whether this cluster is publicly accessible.
* This is a requirement for Teleport Cloud tenants because the control plane must be able to access the EKS Cluster
* in order to deploy the helm chart.
*/
endpointPublicAddress: boolean;
};

export type EnrollEksClustersRequest = {
Expand Down

0 comments on commit 3f96801

Please sign in to comment.