Skip to content

Commit

Permalink
Merge pull request #7421 from cPu1/access-entries
Browse files Browse the repository at this point in the history
Support EKS Access entries
  • Loading branch information
cPu1 authored Dec 20, 2023
2 parents 7b80bb6 + ed79c3d commit b1dfa06
Show file tree
Hide file tree
Showing 98 changed files with 5,455 additions and 700 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ require (
github.com/tj/assert v0.0.3
github.com/vburenin/ifacemaker v1.2.1
github.com/vektra/mockery/v2 v2.38.0
github.com/weaveworks/goformation/v4 v4.10.2-0.20230526082129-5f5eaa9609b8
github.com/weaveworks/goformation/v4 v4.10.2-0.20231113122203-bf1ae633f95c
github.com/weaveworks/schemer v0.0.0-20230525114451-47139fe25848
github.com/xgfone/netaddr v0.5.1
golang.org/x/crypto v0.16.0
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnw
github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/awslabs/goformation/v4 v4.15.5/go.mod h1:wB5lKZf1J0MYH1Lt4B9w3opqz0uIjP7MMCAcib3QkwA=
github.com/awslabs/goformation/v4 v4.19.5 h1:Y+Tzh01tWg8gf//AgGKUamaja7Wx9NPiJf1FpZu4/iU=
github.com/awslabs/goformation/v4 v4.19.5/go.mod h1:JoNpnVCBOUtEz9bFxc9sjy8uBUCLF5c4D1L7RhRTVM8=
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
Expand Down Expand Up @@ -1325,7 +1324,6 @@ github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
Expand All @@ -1336,7 +1334,6 @@ github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.12.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
Expand Down Expand Up @@ -1643,8 +1640,8 @@ github.com/vektra/mockery/v2 v2.38.0 h1:I0LBuUzZHqAU4d1DknW0DTFBPO6n8TaD38WL2KJf
github.com/vektra/mockery/v2 v2.38.0/go.mod h1:diB13hxXG6QrTR0ol2Rk8s2dRMftzvExSvPDKr+IYKk=
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 h1:txplJASvd6b/hrE0s/Ixfpp2cuwH9IO9oZBAN9iYa4A=
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2/go.mod h1:DGCIhurYgnLz8J9ga1fMV/fbLDyUvTyrWXVWUIyJon4=
github.com/weaveworks/goformation/v4 v4.10.2-0.20230526082129-5f5eaa9609b8 h1:qjATkrajxxCgWIQaVgOmG8RiRwGGn1mOm6szwVty7zA=
github.com/weaveworks/goformation/v4 v4.10.2-0.20230526082129-5f5eaa9609b8/go.mod h1:x92o12+Azh6DQ4yoXT5oEuE7dhQHR5V2vy/fmZ6pO7k=
github.com/weaveworks/goformation/v4 v4.10.2-0.20231113122203-bf1ae633f95c h1:iejfxgm8iQ6Jr3yT7Javgk40drlL6w9B6+zgACs0fMw=
github.com/weaveworks/goformation/v4 v4.10.2-0.20231113122203-bf1ae633f95c/go.mod h1:3c2tyJmoge5qTS4PXS0niVJxR0YzroIBsts3dQI3EdI=
github.com/weaveworks/schemer v0.0.0-20230525114451-47139fe25848 h1:I7S+IHZIU49skVgTNArf9bIdy07mCn1Z0zv1r07ROws=
github.com/weaveworks/schemer v0.0.0-20230525114451-47139fe25848/go.mod h1:y8Luzq6JDsYVoIV0QAlnvIiq8bSaap0myMjWKyzVFTY=
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I=
Expand Down Expand Up @@ -1862,7 +1859,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
Expand Down
122 changes: 117 additions & 5 deletions integration/tests/crud/creategetdelete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import (
"github.com/weaveworks/eksctl/integration/tests"
clusterutils "github.com/weaveworks/eksctl/integration/utilities/cluster"
"github.com/weaveworks/eksctl/integration/utilities/kube"
"github.com/weaveworks/eksctl/pkg/actions/nodegroup"
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/authconfigmap"
"github.com/weaveworks/eksctl/pkg/eks"
"github.com/weaveworks/eksctl/pkg/iam"
iamoidc "github.com/weaveworks/eksctl/pkg/iam/oidc"
Expand Down Expand Up @@ -163,6 +165,14 @@ var _ = SynchronizedBeforeSuite(func() []byte {

var _ = Describe("(Integration) Create, Get, Scale & Delete", func() {

makeClientset := func() *kubernetes.Clientset {
config, err := clientcmd.BuildConfigFromFlags("", params.KubeconfigPath)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
clientset, err := kubernetes.NewForConfig(config)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
return clientset
}

Context("validating cluster setup", func() {
It("should have created an EKS cluster and 6 CloudFormation stacks", func() {
awsConfig := NewConfig(params.Region)
Expand Down Expand Up @@ -846,11 +856,7 @@ var _ = Describe("(Integration) Create, Get, Scale & Delete", func() {
WithStdin(clusterutils.ReaderFromFile(params.ClusterName, params.Region, "testdata/taints-max-pods.yaml"))).To(RunSuccessfully())

By("asserting that both formats for taints are supported")
config, err := clientcmd.BuildConfigFromFlags("", params.KubeconfigPath)
ExpectWithOffset(1, err).NotTo(HaveOccurred())
clientset, err := kubernetes.NewForConfig(config)
ExpectWithOffset(1, err).NotTo(HaveOccurred())

clientset := makeClientset()
nodeListN1 := tests.ListNodes(clientset, taintsNg1)
nodeListN2 := tests.ListNodes(clientset, taintsNg2)

Expand Down Expand Up @@ -1057,6 +1063,112 @@ var _ = Describe("(Integration) Create, Get, Scale & Delete", func() {
})
})

Context("access entries", Serial, func() {
hasAuthIdentity := func(nodeRoleARN string) bool {
auth, err := authconfigmap.NewFromClientSet(makeClientset())
Expect(err).NotTo(HaveOccurred())
identities, err := auth.GetIdentities()
Expect(err).NotTo(HaveOccurred())
for _, id := range identities {
if roleID, ok := id.(iam.RoleIdentity); ok && roleID.RoleARN == nodeRoleARN {
return true
}
}
return false
}

type authMode int
const (
authModeAccessEntry authMode = iota + 1
authModeAWSAuthConfigMap
)

type ngAuthTest struct {
ngName string
createNgArgs []string

expectedAuthMode authMode
expectedCreateNgOutput string
}

DescribeTable("authorising self-managed nodegroups", Serial, func(nt ngAuthTest) {
cmd := params.EksctlCreateNodegroupCmd.
WithArgs(
"--cluster", params.ClusterName,
"--name", nt.ngName,
"--nodes", "1",
"--managed=false",
).WithArgs(nt.createNgArgs...)
session := cmd.Run()
Expect(session.ExitCode()).To(BeZero())
if nt.expectedCreateNgOutput != "" {
Expect(session.Out.Contents()).To(ContainSubstring(nt.expectedCreateNgOutput))
}

DeferCleanup(func() {
cmd := params.EksctlDeleteCmd.WithArgs(
"nodegroup",
"--cluster", params.ClusterName,
"--name", nt.ngName,
)
Expect(cmd).To(RunSuccessfully())
})

cmd = params.EksctlGetCmd.WithArgs(
"nodegroup",
"--cluster", params.ClusterName,
"--name", nt.ngName,
"-o", "json",
)
session = cmd.Run()
Expect(session.ExitCode()).To(BeZero())
var ngSummaries []nodegroup.Summary
Expect(json.Unmarshal(session.Out.Contents(), &ngSummaries)).To(Succeed())
Expect(ngSummaries).To(HaveLen(1))
ngSummary := ngSummaries[0]
Expect(ngSummary.NodeInstanceRoleARN).NotTo(BeEmpty())

By("checking access entries")
cmd = params.EksctlGetCmd.
WithArgs(
"accessentry",
"--cluster", params.ClusterName,
)
session = cmd.Run()
Expect(session.ExitCode()).To(BeZero())
if nt.expectedAuthMode == authModeAccessEntry {
Expect(session.Out.Contents()).To(ContainSubstring(ngSummary.NodeInstanceRoleARN), "failed to find access entry for nodegroup")
} else {
Expect(session.Out.Contents()).NotTo(ContainSubstring(ngSummary.NodeInstanceRoleARN), "found access entry for nodegroup")
}

By("checking aws-auth ConfigMap")
Expect(hasAuthIdentity(ngSummary.NodeInstanceRoleARN)).To(Equal(nt.expectedAuthMode == authModeAWSAuthConfigMap))
},
Entry("with access entry", ngAuthTest{
ngName: "ng-access-entry",
expectedAuthMode: authModeAccessEntry,
}),

Entry("with --update-auth-configmap", ngAuthTest{
ngName: "ng-update-auth",
createNgArgs: []string{"--update-auth-configmap"},

expectedCreateNgOutput: "--update-auth-configmap is deprecated and will be removed soon; the recommended way " +
"to authorize nodes is by creating EKS access entries",
expectedAuthMode: authModeAWSAuthConfigMap,
}),

Entry("with --update-auth-configmap=false", ngAuthTest{
ngName: "ng-update-auth-false",
createNgArgs: []string{"--update-auth-configmap=false"},

expectedCreateNgOutput: "--update-auth-configmap is deprecated and will be removed soon; eksctl now uses " +
"EKS Access Entries to authorize nodes if it is enabled on the cluster",
}),
)
})

Context("draining nodegroup(s)", func() {
It("should be able to drain a nodegroup", func() {
Expect(params.EksctlDrainNodeGroupCmd.WithArgs(
Expand Down
69 changes: 69 additions & 0 deletions pkg/accessentry/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package accessentry

import (
"fmt"

"github.com/kris-nova/logger"

ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
)

// Service is a service for access entries.
type Service struct {
// ClusterStateGetter returns the cluster state.
ClusterStateGetter
}

// ClusterStateGetter returns the cluster state.
type ClusterStateGetter interface {
GetClusterState() *ekstypes.Cluster
}

// IsEnabled reports whether the cluster has access entries enabled.
func (s *Service) IsEnabled() bool {
cluster := s.GetClusterState()
return cluster.AccessConfig != nil && IsEnabled(cluster.AccessConfig.AuthenticationMode)
}

// IsAWSAuthDisabled reports whether the cluster has authentication mode set to API.
func (s *Service) IsAWSAuthDisabled() bool {
accessConfig := s.GetClusterState().AccessConfig
return accessConfig == nil || accessConfig.AuthenticationMode == ekstypes.AuthenticationModeApi
}

// IsEnabled reports whether the authenticationMode indicates that the cluster has access entries enabled.
func IsEnabled(authenticationMode ekstypes.AuthenticationMode) bool {
return authenticationMode != ekstypes.AuthenticationModeConfigMap
}

// ValidateAPIServerAccess validates whether the API server is accessible for clusterConfig, and logs warning messages
// for operations that might fail later.
func ValidateAPIServerAccess(clusterConfig *api.ClusterConfig) error {
if !api.IsDisabled(clusterConfig.AccessConfig.BootstrapClusterCreatorAdminPermissions) {
return nil
}

const (
apiServerConnectivityMsg = "eksctl features that require connectivity to the Kubernetes API server will fail"
bootstrapFalseMsg = "bootstrapClusterCreatorAdminPermissions is false"
)
switch clusterConfig.AccessConfig.AuthenticationMode {
case ekstypes.AuthenticationModeConfigMap:
if len(clusterConfig.NodeGroups) > 0 {
return fmt.Errorf("cannot create self-managed nodegroups when authenticationMode is %s and %s", ekstypes.AuthenticationModeConfigMap, bootstrapFalseMsg)
}
logger.Warning("%s; %s", bootstrapFalseMsg, apiServerConnectivityMsg)
default:
if len(clusterConfig.AccessConfig.AccessEntries) == 0 {
if len(clusterConfig.NodeGroups) > 0 {
return fmt.Errorf("cannot create self-managed nodegroups when %s and no access entries are configured", bootstrapFalseMsg)
}
logger.Warning("%s and no access entries are configured; %s", bootstrapFalseMsg, apiServerConnectivityMsg)
return nil
}
logger.Warning("%s; if no configured access entries allow access to the Kubernetes API server, %s", bootstrapFalseMsg, apiServerConnectivityMsg)
}
return nil
}
13 changes: 13 additions & 0 deletions pkg/actions/accessentry/access_entry_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package accessentry_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestAccessEntry(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Access Entry Suite")
}
66 changes: 66 additions & 0 deletions pkg/actions/accessentry/creator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package accessentry

import (
"context"
"crypto/sha1"
"encoding/base32"
"errors"
"fmt"
"strings"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/utils/tasks"
)

// CreatorInterface creates access entries.
//
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
//counterfeiter:generate -o fakes/fake_creator.go . CreatorInterface
type CreatorInterface interface {
// Create creates access entries.
Create(ctx context.Context, accessEntries []api.AccessEntry) error
// CreateTasks creates a TaskTree for creating access entries.
CreateTasks(ctx context.Context, accessEntries []api.AccessEntry) *tasks.TaskTree
}

// A Creator creates access entries.
type Creator struct {
ClusterName string
StackCreator StackCreator
}

// Create creates the specified access entries.
func (m *Creator) Create(ctx context.Context, accessEntries []api.AccessEntry) error {
taskTree := m.CreateTasks(ctx, accessEntries)
if errs := taskTree.DoAllSync(); len(errs) > 0 {
var allErrs []string
for _, err := range errs {
allErrs = append(allErrs, err.Error())
}
return errors.New(strings.Join(allErrs, "\n"))
}
return nil
}

// CreateTasks creates a TaskTree for creating access entries.
func (m *Creator) CreateTasks(ctx context.Context, accessEntries []api.AccessEntry) *tasks.TaskTree {
taskTree := &tasks.TaskTree{
Parallel: true,
}
for _, ae := range accessEntries {
taskTree.Append(&accessEntryTask{
ctx: ctx,
info: fmt.Sprintf("create access entry for principal ARN %s", ae.PrincipalARN),
clusterName: m.ClusterName,
accessEntry: ae,
stackCreator: m.StackCreator,
})
}
return taskTree
}

// MakeStackName creates a stack name for the specified access entry.
func MakeStackName(clusterName string, accessEntry api.AccessEntry) string {
s := sha1.Sum([]byte(accessEntry.PrincipalARN.String()))
return fmt.Sprintf("eksctl-%s-accessentry-%s", clusterName, base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(s[:]))
}
Loading

0 comments on commit b1dfa06

Please sign in to comment.