From 06c735490b59ebbf9b3969f3add29fc84e4716e7 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 28 May 2024 13:13:47 +0100 Subject: [PATCH] Bootstrap kubernetes RBACs when running EKS auto discovery (#41693) * Bootstrap kubernetes RBACs when running EKS auto discovery This PR extends the ability of `discovery_service` to self-bootstrap the required permissions for the `kubernetes_service` to dial and forward requests to the Kubernetes API on behalf of the users. When EKS auto-discovery was initially developed, it wasn't possible to bootstrap the required permissions without having prior access to the cluster itself. Recently, AWS releases a new API to configure access to IAM identities. By default, there are just a few predifined permissions that either don't have the required permissions for `kubernetes_service` to be operational or are the equivalent of `cluster-admin` RBAC role. To bypass it, `discovery_service` temporarily escalates itself to `cluster-admin` by creating an EKS `AccessEntry` and associates the policy `arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy`. Once access is granted, it creates a Kubernetes RBAC `ClusterRole` and `ClusterRoleBinding` with the minimal required permissions for `kubernetes_service` to be operational. These permissions are binded to `teleport:kube-service:eks`. Finally, it deletes the previously created `AccessEntry` and creates another for the target ARN that Kubernetes Service uses. Fixes #39021 Signed-off-by: Tiago Silva * handle review comments * do not exit on failed GetCallerIdentity call --------- Signed-off-by: Tiago Silva --- api/proto/teleport/legacy/types/types.proto | 4 + api/types/discoveryconfig/derived.gen.go | 3 +- api/types/matchers_aws.go | 9 + api/types/types.pb.go | 345 ++++++++------ lib/cloud/azure/kubernetes.go | 55 +-- lib/config/configuration.go | 17 +- lib/config/fileconf.go | 2 + lib/fixtures/kube.go | 66 +++ lib/kube/proxy/cluster_details.go | 38 +- lib/kube/utils/eks_token_signed.go | 62 +++ lib/srv/discovery/discovery_test.go | 2 +- lib/srv/discovery/fetchers/eks.go | 450 +++++++++++++++++- lib/srv/discovery/fetchers/eks_test.go | 25 +- .../kube_integration_watcher_test.go | 22 +- 14 files changed, 819 insertions(+), 281 deletions(-) create mode 100644 lib/fixtures/kube.go create mode 100644 lib/kube/utils/eks_token_signed.go diff --git a/api/proto/teleport/legacy/types/types.proto b/api/proto/teleport/legacy/types/types.proto index eec214cdce4..318b3c866ac 100644 --- a/api/proto/teleport/legacy/types/types.proto +++ b/api/proto/teleport/legacy/types/types.proto @@ -6780,6 +6780,10 @@ message AWSMatcher { // KubeAppDiscovery controls whether Kubernetes App Discovery will be enabled for agents running on // discovered clusters, currently only affects AWS EKS discovery in integration mode. bool KubeAppDiscovery = 8 [(gogoproto.jsontag) = "kube_app_discovery,omitempty"]; + // SetupAccessForARN is the role that the discovery service should create EKS Access Entries for. + // This value should match the IAM identity that Teleport Kubernetes Service uses. + // If this value is empty, the discovery service will attempt to set up access for its own identity (self). + string SetupAccessForARN = 9 [(gogoproto.jsontag) = "setup_access_for_arn,omitempty"]; } // AssumeRole provides a role ARN and ExternalID to assume an AWS role diff --git a/api/types/discoveryconfig/derived.gen.go b/api/types/discoveryconfig/derived.gen.go index 42dac4ec1d8..f92f2f40ba9 100644 --- a/api/types/discoveryconfig/derived.gen.go +++ b/api/types/discoveryconfig/derived.gen.go @@ -150,7 +150,8 @@ func deriveTeleportEqual_8(this, that *types.AWSMatcher) bool { deriveTeleportEqual_16(this.Params, that.Params) && deriveTeleportEqual_17(this.SSM, that.SSM) && this.Integration == that.Integration && - this.KubeAppDiscovery == that.KubeAppDiscovery + this.KubeAppDiscovery == that.KubeAppDiscovery && + this.SetupAccessForARN == that.SetupAccessForARN } // deriveTeleportEqual_9 returns whether this and that are equal. diff --git a/api/types/matchers_aws.go b/api/types/matchers_aws.go index 98d2d9a2ca5..8138aa39c16 100644 --- a/api/types/matchers_aws.go +++ b/api/types/matchers_aws.go @@ -143,6 +143,15 @@ func (m *AWSMatcher) CheckAndSetDefaults() error { } } + if m.SetupAccessForARN != "" { + if !slices.Contains(m.Types, AWSMatcherEKS) { + return trace.BadParameter("discovery service AWS matcher setup_access_for_arn is only supported for eks") + } + if err := awsapiutils.CheckRoleARN(m.SetupAccessForARN); err != nil { + return trace.BadParameter("invalid setup access for ARN: %v", err) + } + } + if m.Tags == nil || len(m.Tags) == 0 { m.Tags = map[string]apiutils.Strings{Wildcard: {Wildcard}} } diff --git a/api/types/types.pb.go b/api/types/types.pb.go index b9c3167ccc8..5840c643150 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -18143,7 +18143,11 @@ type AWSMatcher struct { Integration string `protobuf:"bytes,7,opt,name=Integration,proto3" json:"integration,omitempty"` // KubeAppDiscovery controls whether Kubernetes App Discovery will be enabled for agents running on // discovered clusters, currently only affects AWS EKS discovery in integration mode. - KubeAppDiscovery bool `protobuf:"varint,8,opt,name=KubeAppDiscovery,proto3" json:"kube_app_discovery,omitempty"` + KubeAppDiscovery bool `protobuf:"varint,8,opt,name=KubeAppDiscovery,proto3" json:"kube_app_discovery,omitempty"` + // SetupAccessForARN is the role that the discovery service should create EKS Access Entries for. + // This value should match the IAM identity that Teleport Kubernetes Service uses. + // If this value is empty, the discovery service will attempt to set up access for its own identity (self). + SetupAccessForARN string `protobuf:"bytes,9,opt,name=SetupAccessForARN,proto3" json:"setup_access_for_arn,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -19052,7 +19056,7 @@ func init() { func init() { proto.RegisterFile("teleport/legacy/types/types.proto", fileDescriptor_9198ee693835762e) } var fileDescriptor_9198ee693835762e = []byte{ - // 25970 bytes of a gzipped FileDescriptorProto + // 26003 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x70, 0x1c, 0x49, 0x7a, 0x20, 0x36, 0xfd, 0x00, 0xd0, 0xf8, 0xf0, 0x6a, 0x24, 0x40, 0x12, 0xc4, 0x90, 0xd3, 0x9c, 0xe2, 0x0c, 0x87, 0x9c, 0x07, 0xb9, 0x04, 0x77, 0xb8, 0x3b, 0x3b, 0xaf, 0x6d, 0x74, 0x83, 0x44, @@ -20531,152 +20535,154 @@ var fileDescriptor_9198ee693835762e = []byte{ 0x1f, 0x4f, 0xc1, 0x42, 0xbf, 0xb6, 0xb2, 0x8b, 0x90, 0x6e, 0x56, 0x7d, 0x3a, 0x88, 0xdb, 0xa6, 0xdb, 0x53, 0x4b, 0x34, 0xf2, 0x2e, 0x4c, 0xf0, 0x9c, 0x7f, 0xb5, 0x1b, 0x5b, 0x66, 0x45, 0x2c, 0x10, 0x74, 0xbc, 0x14, 0xe9, 0xc9, 0xbd, 0x1b, 0x91, 0x5c, 0xd5, 0x2a, 0xbe, 0xf1, 0xa3, 0x29, - 0x58, 0xec, 0xdf, 0x45, 0x76, 0xc6, 0xfc, 0xbf, 0xd4, 0x5d, 0x5f, 0x6f, 0x1b, 0xc7, 0x11, 0xd7, - 0xf1, 0x5f, 0xa4, 0x91, 0x64, 0x9d, 0xd6, 0x8e, 0xac, 0xca, 0xb2, 0xe2, 0x12, 0xa9, 0x51, 0x5f, - 0x1b, 0xa7, 0x49, 0x1b, 0x24, 0x4a, 0x91, 0x06, 0x27, 0x72, 0x29, 0xd2, 0xba, 0x3f, 0xf4, 0xdd, - 0xd1, 0x8a, 0xdd, 0x3f, 0x07, 0x5a, 0x3a, 0x4b, 0x6c, 0xa9, 0x23, 0x23, 0x52, 0x71, 0xdd, 0xb7, - 0xbe, 0x14, 0x28, 0x8a, 0x02, 0x45, 0x5f, 0xfb, 0xd0, 0x97, 0xbc, 0xf4, 0x33, 0xf4, 0x0b, 0x04, - 0x28, 0x0a, 0xe4, 0x13, 0x14, 0xad, 0x81, 0x7e, 0x84, 0xbe, 0xe4, 0xa9, 0xd8, 0xd9, 0xdd, 0xbb, - 0xbd, 0x23, 0xa9, 0x48, 0x8e, 0xd1, 0x22, 0x6f, 0xe2, 0xec, 0xec, 0x6a, 0x6f, 0xff, 0xcc, 0xee, - 0xcc, 0xce, 0xfc, 0x26, 0x60, 0x37, 0xf3, 0xd4, 0x2f, 0x15, 0xcf, 0x18, 0xbc, 0xad, 0xe7, 0x1c, - 0x53, 0x13, 0x46, 0x56, 0x29, 0x97, 0x01, 0x5d, 0xcd, 0x7c, 0x91, 0xad, 0x24, 0x19, 0xab, 0x7f, - 0x2a, 0xc0, 0x1a, 0x5b, 0x3e, 0xfd, 0x68, 0x34, 0x32, 0xcf, 0xc6, 0xc7, 0x51, 0x3c, 0x16, 0x17, - 0x2a, 0xf2, 0x2e, 0x54, 0x8e, 0x2f, 0x67, 0x0b, 0xe4, 0xec, 0x84, 0x00, 0x8a, 0x64, 0xe9, 0x18, - 0xc0, 0xfe, 0x26, 0x37, 0x41, 0xc9, 0x34, 0x82, 0x12, 0x75, 0xc9, 0x5b, 0x18, 0x26, 0xf9, 0x46, - 0xde, 0x83, 0x32, 0xea, 0xfe, 0x42, 0x30, 0xca, 0x0b, 0xed, 0xf4, 0x9e, 0xa1, 0x65, 0xc0, 0xe3, - 0x15, 0xc8, 0x9b, 0x00, 0x29, 0x3a, 0x98, 0x90, 0x7c, 0x52, 0x89, 0x4e, 0x00, 0xc2, 0xbc, 0x85, - 0x93, 0x27, 0x5d, 0x01, 0xb9, 0x65, 0xc0, 0xaa, 0x1c, 0x92, 0xa1, 0x8c, 0x7f, 0xe6, 0x96, 0x08, - 0x6f, 0x85, 0x17, 0xb4, 0x86, 0x22, 0x06, 0xba, 0xfa, 0xef, 0x02, 0x2c, 0xec, 0xb3, 0x6b, 0x02, - 0x2a, 0xbf, 0xe7, 0x2b, 0xd3, 0x6f, 0xc3, 0xa2, 0x35, 0xe8, 0x0a, 0xcb, 0xfd, 0x48, 0x40, 0x30, - 0xa0, 0x73, 0x66, 0x7f, 0xd0, 0x95, 0x8f, 0x00, 0x23, 0x4f, 0x65, 0xfa, 0x12, 0xc7, 0xd2, 0x7b, - 0x50, 0xe1, 0x2f, 0xdc, 0xc2, 0x4c, 0x23, 0x2f, 0x8a, 0x49, 0x8f, 0xee, 0xf2, 0x62, 0xc5, 0xd8, - 0xcc, 0xdf, 0xc8, 0xd5, 0x5b, 0x8b, 0x40, 0x60, 0x50, 0x54, 0xfd, 0xf2, 0xc5, 0x54, 0x7d, 0x25, - 0xd2, 0xb4, 0x72, 0x91, 0x48, 0xd3, 0x8d, 0x6d, 0x58, 0x54, 0xfa, 0x73, 0xa9, 0x7b, 0xe3, 0xaf, - 0x0b, 0xb0, 0x8c, 0x5f, 0x95, 0xbc, 0x12, 0x7d, 0x3d, 0x0d, 0x17, 0xef, 0x67, 0x0c, 0x17, 0xeb, - 0xea, 0x7c, 0xf1, 0x2f, 0x3b, 0xc7, 0x62, 0x71, 0x0f, 0x56, 0x27, 0x18, 0xc9, 0x3b, 0x50, 0x66, - 0xdd, 0x97, 0x8a, 0x9e, 0x9e, 0x5f, 0x01, 0x29, 0x2a, 0x09, 0xfb, 0xf0, 0x91, 0xc7, 0xb9, 0xab, - 0xff, 0xd1, 0x60, 0x49, 0xc0, 0xc5, 0xc5, 0x4f, 0x06, 0x5f, 0x3a, 0x9c, 0xb7, 0xf3, 0xc3, 0xc9, - 0xc3, 0x24, 0xc4, 0x70, 0xfe, 0xaf, 0x07, 0x71, 0x3b, 0x33, 0x88, 0xd7, 0x93, 0x18, 0x65, 0xf9, - 0x39, 0xe7, 0x8c, 0xe1, 0x5f, 0x11, 0xb5, 0x23, 0xcb, 0x48, 0x7e, 0x06, 0x0b, 0x4e, 0xf4, 0x34, - 0xa3, 0x2f, 0xdd, 0x9e, 0xd1, 0xe8, 0xdd, 0x84, 0x91, 0xef, 0x29, 0x3c, 0x6a, 0xe2, 0xe8, 0x69, - 0x38, 0xf1, 0x88, 0x93, 0x36, 0xc9, 0x54, 0xa6, 0x6c, 0xb5, 0xcb, 0x2c, 0x7d, 0xe1, 0x8c, 0x87, - 0x91, 0x3f, 0x7f, 0x29, 0x02, 0xa4, 0x7e, 0x4c, 0x6c, 0x03, 0x46, 0x19, 0x50, 0x55, 0x61, 0x39, - 0x46, 0x92, 0xba, 0xc6, 0x05, 0x89, 0xdc, 0x16, 0x26, 0xd1, 0xc2, 0xec, 0x18, 0x72, 0x34, 0x8e, - 0xd6, 0x84, 0x9f, 0xd0, 0x61, 0xd4, 0xef, 0x72, 0x59, 0x5c, 0xc4, 0xbc, 0xfe, 0xd7, 0x52, 0xea, - 0x8c, 0xbc, 0x1f, 0xe8, 0x4d, 0x54, 0x67, 0x0c, 0x13, 0xbe, 0x81, 0xa5, 0x17, 0xf7, 0x0d, 0x2c, - 0xbf, 0x80, 0x6f, 0x60, 0xe5, 0x82, 0xbe, 0x81, 0x6d, 0x58, 0xe8, 0xc5, 0x9f, 0x44, 0xf1, 0x78, - 0x70, 0xfa, 0x0c, 0x3d, 0x87, 0x52, 0x43, 0x16, 0x1b, 0xea, 0x96, 0x2c, 0xe3, 0xf3, 0x8d, 0x07, - 0x66, 0xc2, 0xaf, 0x4e, 0x77, 0x42, 0x14, 0x2e, 0x0c, 0x5f, 0x14, 0x80, 0x4c, 0x36, 0x40, 0xde, - 0x87, 0x45, 0x2e, 0x82, 0xc3, 0xd3, 0xd1, 0xc7, 0xc2, 0xb1, 0x8c, 0x47, 0x58, 0x29, 0x64, 0x35, - 0xc2, 0x8a, 0x93, 0xbd, 0xd1, 0xc7, 0x7d, 0xf2, 0x53, 0xb8, 0x8a, 0x13, 0x30, 0x8c, 0x4e, 0x7b, - 0x83, 0xc3, 0x10, 0x31, 0x2e, 0xba, 0x7d, 0x81, 0xe2, 0xfd, 0x06, 0xa6, 0x9b, 0x98, 0x2c, 0x9e, - 0x31, 0x51, 0xe8, 0xbf, 0xd5, 0x46, 0xce, 0x36, 0x67, 0x24, 0x01, 0xe8, 0x6a, 0xfd, 0x27, 0x67, - 0xfd, 0xbe, 0x98, 0x7b, 0x03, 0x73, 0xf1, 0xe6, 0xca, 0x66, 0x34, 0x7c, 0x25, 0x6d, 0xb8, 0x71, - 0xd6, 0xef, 0x93, 0x77, 0x01, 0x06, 0x71, 0x78, 0xd2, 0x1b, 0x8d, 0xf8, 0x73, 0x42, 0xe2, 0x7b, - 0x99, 0x52, 0xd5, 0x61, 0x1c, 0xc4, 0x36, 0x27, 0x92, 0x1f, 0x00, 0x3a, 0x66, 0x63, 0xc4, 0x02, - 0xae, 0x80, 0xb2, 0xc0, 0xe5, 0x93, 0xc4, 0xec, 0x74, 0x1e, 0x45, 0x7e, 0xef, 0x57, 0xd2, 0x7f, - 0xe4, 0x11, 0xac, 0x8a, 0x88, 0xac, 0xfd, 0xde, 0xf8, 0x58, 0xdc, 0x7e, 0xbf, 0xca, 0xd5, 0x59, - 0xb9, 0xfe, 0xfe, 0xb6, 0x04, 0x60, 0xee, 0xfb, 0x32, 0x18, 0xf0, 0x0e, 0x94, 0xd9, 0x9d, 0x5e, - 0xda, 0x06, 0xd0, 0xb2, 0x8a, 0xed, 0xaa, 0x96, 0x55, 0xe4, 0x60, 0xfb, 0xd5, 0x8b, 0x8e, 0xd0, - 0x3c, 0x55, 0x48, 0x0d, 0x09, 0xa7, 0x9c, 0x94, 0xb9, 0x43, 0x72, 0x12, 0xb1, 0x00, 0xd2, 0xf0, - 0x3c, 0xa1, 0x65, 0xae, 0xa6, 0x71, 0x2e, 0xa2, 0x40, 0x00, 0xae, 0xa5, 0x21, 0x7e, 0xea, 0xf2, - 0x49, 0xd9, 0xc8, 0x1e, 0x94, 0x82, 0x6e, 0xe2, 0x59, 0x38, 0x23, 0x68, 0xf1, 0x96, 0x40, 0x59, - 0x4f, 0x03, 0x17, 0xaf, 0x8c, 0xbb, 0x99, 0x64, 0x14, 0xd8, 0x08, 0xa1, 0x50, 0x11, 0x19, 0x74, - 0x66, 0x44, 0xb0, 0x8b, 0x04, 0x3a, 0x02, 0xb7, 0x06, 0x89, 0xea, 0xad, 0x43, 0xe4, 0xca, 0x79, - 0x1b, 0x8a, 0xbe, 0x6f, 0x0b, 0x57, 0xfd, 0xe5, 0x54, 0x63, 0xf0, 0x7d, 0x5b, 0x66, 0x09, 0x3b, - 0x51, 0xaa, 0x31, 0x66, 0xf2, 0x43, 0x58, 0x54, 0x2e, 0xc4, 0x22, 0xc8, 0x05, 0xc7, 0xa0, 0x97, - 0x92, 0x33, 0xf7, 0xea, 0x94, 0x4c, 0x2c, 0xd0, 0xf7, 0xce, 0x1e, 0x47, 0xe6, 0x70, 0x88, 0x5e, - 0x71, 0x9f, 0x44, 0xa7, 0x1c, 0x0c, 0x6e, 0x3e, 0x85, 0x7c, 0x09, 0xbb, 0xc3, 0x61, 0x78, 0x28, - 0x4b, 0x55, 0xfb, 0x48, 0xbe, 0x66, 0x35, 0x52, 0x27, 0x88, 0x9d, 0x92, 0x59, 0x25, 0x01, 0x4f, - 0x49, 0xa9, 0x24, 0xa4, 0xaa, 0xc1, 0x9b, 0x53, 0x82, 0x2c, 0xf1, 0xe9, 0x49, 0x09, 0xb2, 0xcc, - 0x84, 0x56, 0x7e, 0x5a, 0x52, 0x82, 0xf7, 0xc5, 0xc8, 0x7d, 0x00, 0x70, 0x6f, 0xd0, 0x8b, 0xed, - 0x68, 0x7c, 0x3c, 0x38, 0x54, 0x62, 0x3d, 0x17, 0x7f, 0x3e, 0xe8, 0xc5, 0xe1, 0x09, 0x92, 0xbf, - 0xf8, 0xc7, 0x6b, 0x0a, 0x93, 0xa7, 0xfc, 0x4d, 0xbe, 0x0b, 0x0b, 0xec, 0x57, 0x90, 0x3a, 0x60, - 0x70, 0xa3, 0x1f, 0xd6, 0x16, 0xb9, 0x01, 0x13, 0x06, 0xb2, 0x8d, 0xf8, 0x8a, 0xbd, 0xe1, 0x58, - 0xb9, 0x8c, 0x4a, 0x30, 0xc5, 0xde, 0x70, 0x9c, 0xc7, 0x63, 0x51, 0x98, 0x49, 0x33, 0xe9, 0xba, - 0x44, 0xe8, 0x14, 0x30, 0x8e, 0x68, 0xd9, 0x12, 0x2b, 0x23, 0x94, 0x40, 0x10, 0x6a, 0x2e, 0x85, - 0x5c, 0x35, 0xec, 0x84, 0xdf, 0xac, 0xf3, 0xa7, 0x08, 0x71, 0x26, 0xf0, 0x4e, 0x8c, 0x8e, 0x0f, - 0xc3, 0x03, 0x24, 0x67, 0x3a, 0x91, 0x30, 0x93, 0x1d, 0x58, 0xe1, 0x11, 0x49, 0x09, 0xd2, 0xb7, - 0x38, 0x1f, 0x50, 0x12, 0xa5, 0x50, 0xe0, 0xea, 0xbf, 0xcf, 0x55, 0x20, 0x0d, 0x28, 0xa3, 0x42, - 0x26, 0xc2, 0x42, 0x6e, 0xa8, 0x7a, 0x68, 0x7e, 0xd5, 0xa3, 0x14, 0x40, 0x0d, 0x54, 0x95, 0x02, - 0xc8, 0x4a, 0x3e, 0x02, 0xa0, 0xf1, 0xe9, 0xa0, 0xdf, 0x47, 0xa8, 0x92, 0x79, 0x54, 0x67, 0x6e, - 0x66, 0x77, 0x0f, 0xb6, 0x92, 0x32, 0xc9, 0xac, 0xee, 0xec, 0x77, 0x98, 0x03, 0x34, 0x51, 0xda, - 0xaa, 0xb6, 0xa0, 0xc2, 0xb7, 0x0e, 0xc2, 0xfe, 0x08, 0x4c, 0x41, 0x05, 0x34, 0x86, 0xc3, 0xfe, - 0x08, 0xfa, 0x24, 0xec, 0x8f, 0x52, 0xa1, 0xba, 0x07, 0xd7, 0xa6, 0x7d, 0x58, 0x46, 0x85, 0xd4, - 0x2e, 0xaa, 0x42, 0xfe, 0xb9, 0x08, 0x4b, 0xd8, 0x9a, 0x94, 0x99, 0x26, 0x2c, 0xfb, 0x67, 0x8f, - 0x93, 0xf0, 0x39, 0x29, 0x3b, 0x79, 0x42, 0x7d, 0xb5, 0x40, 0x7d, 0x24, 0xca, 0xd4, 0x20, 0x14, - 0xae, 0x48, 0xb9, 0x2d, 0x72, 0x1d, 0x14, 0x52, 0xc4, 0x1d, 0x19, 0xd8, 0x3d, 0x99, 0xe9, 0x20, - 0x57, 0x29, 0x95, 0xde, 0xc5, 0xcb, 0x48, 0xef, 0xd2, 0x85, 0xa4, 0xf7, 0x8f, 0x61, 0x49, 0xfe, - 0x37, 0x94, 0xbb, 0xe5, 0xaf, 0x26, 0x77, 0x33, 0x8d, 0x11, 0x2b, 0x91, 0xbf, 0x95, 0x73, 0xe5, - 0x2f, 0xbe, 0xbc, 0xc9, 0x5d, 0x36, 0x91, 0xbc, 0x4c, 0xb4, 0x81, 0x50, 0xe0, 0xbb, 0xb5, 0xf6, - 0x0b, 0x9c, 0x69, 0xef, 0xc0, 0x82, 0x35, 0x90, 0x8f, 0x2e, 0x8a, 0xb5, 0xbb, 0x2f, 0x89, 0xea, - 0xe1, 0x9e, 0x70, 0x26, 0x67, 0x51, 0xf1, 0x65, 0x9c, 0x45, 0xdb, 0x00, 0x6d, 0x1e, 0xf4, 0x90, - 0x42, 0xf8, 0xe2, 0x96, 0x91, 0xd1, 0x11, 0x59, 0xa3, 0xbb, 0xc2, 0xcc, 0xa4, 0x93, 0x70, 0xcf, - 0x30, 0x0f, 0x0e, 0x06, 0x67, 0xf1, 0x38, 0x93, 0xf3, 0x42, 0x04, 0x4a, 0x85, 0x5d, 0x51, 0xa6, - 0x8a, 0x87, 0x5c, 0xb5, 0x97, 0x3b, 0x21, 0xe4, 0x7e, 0xe2, 0x57, 0x76, 0x6e, 0x0a, 0xc0, 0xea, - 0xc4, 0x08, 0xcd, 0xf4, 0x26, 0xab, 0xfe, 0x4d, 0x53, 0xe1, 0xce, 0x5e, 0x60, 0xaa, 0xdf, 0x03, - 0x48, 0x5e, 0xbd, 0xe5, 0x5c, 0x73, 0xfd, 0x27, 0xa1, 0xaa, 0xa3, 0x9c, 0xf2, 0x2a, 0x5f, 0x53, - 0x7c, 0x59, 0x5f, 0x13, 0xc0, 0xa2, 0xfb, 0x8b, 0x71, 0x37, 0x75, 0x93, 0x00, 0x3f, 0xb9, 0x77, - 0xa2, 0x64, 0x92, 0xa9, 0x0a, 0xd3, 0x5b, 0xeb, 0xcc, 0x54, 0x85, 0x49, 0xc5, 0xea, 0x7d, 0x58, - 0x51, 0x5d, 0xb8, 0x9f, 0xc5, 0x07, 0xe4, 0x47, 0x1c, 0xa7, 0x41, 0xcb, 0x68, 0x06, 0x0a, 0x13, - 0x93, 0xb8, 0xcf, 0xe2, 0x03, 0x7e, 0x5b, 0xe9, 0x3e, 0x55, 0xfb, 0x8a, 0x3a, 0xdb, 0xe7, 0x1a, - 0x90, 0x49, 0x76, 0x55, 0x9a, 0x68, 0xff, 0x87, 0xbb, 0x60, 0xee, 0x0e, 0x55, 0xba, 0xcc, 0x1d, - 0xca, 0xf8, 0x83, 0x06, 0x2b, 0x2d, 0xd3, 0x16, 0xd8, 0x64, 0xdc, 0x7a, 0xff, 0x4d, 0xb8, 0xd9, - 0x32, 0xed, 0xb0, 0xed, 0x5a, 0xad, 0xda, 0xc3, 0x70, 0x2a, 0xe4, 0xc8, 0x4d, 0xf8, 0xc6, 0x24, - 0x4b, 0x6a, 0xe5, 0xdf, 0x84, 0xf5, 0xc9, 0x62, 0x09, 0x4b, 0x32, 0xbd, 0xb2, 0x44, 0x30, 0x29, - 0x1a, 0x1f, 0xc2, 0x8a, 0x44, 0xeb, 0x08, 0x2c, 0x1f, 0x41, 0xbe, 0x56, 0x60, 0xf1, 0x01, 0xf5, - 0x5a, 0x8d, 0x87, 0x61, 0xa3, 0x63, 0x59, 0xfa, 0x1c, 0x59, 0x86, 0x05, 0x41, 0xa8, 0x99, 0xba, - 0x46, 0x96, 0x60, 0xbe, 0xe5, 0xf8, 0xb4, 0xd6, 0xf1, 0xa8, 0x5e, 0x30, 0x3e, 0x84, 0x2b, 0x69, - 0x3e, 0x77, 0x34, 0xd2, 0xbf, 0x02, 0x45, 0xcf, 0xdc, 0xd7, 0xe7, 0x08, 0x40, 0xa5, 0xbd, 0x57, - 0xf3, 0xdf, 0x7a, 0x4b, 0xd7, 0xc8, 0x22, 0xbc, 0xb2, 0x5b, 0x6b, 0x87, 0x7b, 0xb6, 0xaf, 0x17, - 0xd8, 0x0f, 0x73, 0xdf, 0xc7, 0x1f, 0x45, 0xe3, 0x7b, 0xb0, 0x8a, 0x77, 0x05, 0xab, 0x37, 0x1a, - 0x47, 0x71, 0x74, 0x8a, 0x7d, 0x58, 0x82, 0x79, 0x3f, 0x62, 0x9b, 0x7c, 0x1c, 0xf1, 0x0e, 0xd8, - 0x67, 0xfd, 0x71, 0x6f, 0xd8, 0x8f, 0x7e, 0xa9, 0x6b, 0xc6, 0x36, 0xac, 0x78, 0x83, 0xb3, 0x71, - 0x2f, 0x3e, 0xf2, 0xc7, 0x8c, 0xe3, 0xe8, 0x19, 0x79, 0x15, 0x56, 0x3b, 0x8e, 0x69, 0xef, 0xb4, - 0x76, 0x3b, 0x6e, 0xc7, 0x0f, 0x6d, 0x33, 0xa8, 0x35, 0xf9, 0x13, 0x81, 0xed, 0xfa, 0x41, 0xe8, - 0xd1, 0x1a, 0x75, 0x02, 0x5d, 0x33, 0x7e, 0xa7, 0xc1, 0x95, 0xce, 0x48, 0xb8, 0xb5, 0x76, 0x10, - 0x79, 0xe2, 0x16, 0x6c, 0x76, 0x7c, 0xea, 0x85, 0x81, 0xbb, 0x47, 0x9d, 0xb0, 0xe3, 0x9b, 0xbb, - 0x79, 0xbc, 0x9b, 0xd7, 0xe0, 0x86, 0xc2, 0xe1, 0xd1, 0x9a, 0xfb, 0x80, 0x7a, 0x61, 0xdb, 0xf4, - 0xfd, 0x7d, 0xd7, 0xab, 0xeb, 0x1a, 0xd9, 0x80, 0xb5, 0x29, 0x0c, 0x76, 0xc3, 0xd4, 0x0b, 0x13, - 0x65, 0x0e, 0xdd, 0x37, 0xad, 0x70, 0xc7, 0x0d, 0xf4, 0xa2, 0x61, 0xb3, 0x83, 0x0e, 0x21, 0x21, - 0x38, 0xa0, 0xe7, 0x3c, 0x94, 0x1c, 0xd7, 0xa1, 0xf9, 0x67, 0x9c, 0x25, 0x98, 0x37, 0xdb, 0x6d, - 0xcf, 0x7d, 0x80, 0x13, 0x0a, 0x50, 0xa9, 0x53, 0x87, 0xf5, 0xac, 0xc8, 0x4a, 0xda, 0x9e, 0x6b, - 0xbb, 0x01, 0xad, 0xeb, 0x25, 0xc3, 0x93, 0x1b, 0x46, 0x36, 0x7a, 0x30, 0xe0, 0x6f, 0x26, 0x75, - 0xda, 0x30, 0x3b, 0x56, 0x20, 0x06, 0xe4, 0x61, 0xe8, 0xd1, 0xfb, 0x1d, 0xea, 0x07, 0xbe, 0xae, - 0x11, 0x1d, 0x96, 0x1c, 0x4a, 0xeb, 0x7e, 0xe8, 0xd1, 0x07, 0x2d, 0xba, 0xaf, 0x17, 0x58, 0x9b, - 0xfc, 0x6f, 0xf6, 0x1f, 0x8c, 0x4f, 0x35, 0x20, 0x1c, 0x4e, 0x43, 0x02, 0x2f, 0xe2, 0xfc, 0x6c, - 0xc1, 0x46, 0x93, 0x0d, 0x2c, 0x7e, 0x9a, 0xed, 0xd6, 0xf3, 0x43, 0xb6, 0x06, 0x24, 0x57, 0xee, - 0x36, 0x1a, 0xba, 0x46, 0x6e, 0xc0, 0xd5, 0x1c, 0xbd, 0xee, 0xb9, 0x6d, 0xbd, 0xb0, 0x51, 0x98, - 0xd7, 0xc8, 0xf5, 0x89, 0xc2, 0x3d, 0x4a, 0xdb, 0x7a, 0x91, 0x4d, 0x51, 0xae, 0x40, 0x2e, 0x40, - 0x5e, 0xbd, 0x64, 0xfc, 0x46, 0x83, 0x35, 0xde, 0x4d, 0xb9, 0x9a, 0x93, 0xae, 0x6e, 0xc2, 0xba, - 0x40, 0xfe, 0x99, 0xd6, 0xd1, 0x6b, 0xa0, 0x67, 0x4a, 0x79, 0x37, 0x5f, 0x85, 0xd5, 0x0c, 0x15, - 0xfb, 0x51, 0x60, 0x7b, 0x35, 0x43, 0xde, 0xa1, 0x7e, 0x10, 0xd2, 0x46, 0xc3, 0xf5, 0x02, 0xde, - 0x91, 0xa2, 0x51, 0x85, 0xd5, 0x5a, 0x74, 0x3a, 0x66, 0x3a, 0x48, 0x3c, 0xea, 0x0d, 0x62, 0xec, - 0xc2, 0x32, 0x2c, 0xd0, 0x8f, 0x02, 0xea, 0xf8, 0x2d, 0xd7, 0xd1, 0xe7, 0x8c, 0xcd, 0x1c, 0x8f, - 0xdc, 0x35, 0xbe, 0xdf, 0xd4, 0xe7, 0x8c, 0x2e, 0x2c, 0x4b, 0x37, 0x52, 0xbe, 0x2a, 0xb6, 0x60, - 0x43, 0xae, 0x35, 0xdc, 0xbf, 0xf9, 0x4f, 0x58, 0x87, 0x6b, 0x93, 0xe5, 0x34, 0xd0, 0x35, 0x36, - 0x0b, 0xb9, 0x12, 0x46, 0x2f, 0x18, 0x3f, 0x81, 0xa5, 0x0c, 0x42, 0xf1, 0x75, 0xb8, 0xaa, 0xfe, - 0x6e, 0x47, 0xf1, 0x61, 0x2f, 0x3e, 0xd2, 0xe7, 0xf2, 0x05, 0xde, 0x59, 0x1c, 0xb3, 0x02, 0x5c, - 0xf1, 0x6a, 0x41, 0x10, 0x9d, 0x9e, 0xf4, 0xe2, 0xee, 0x38, 0x3a, 0xd4, 0x0b, 0xc6, 0x5d, 0x58, - 0xce, 0x40, 0xa8, 0xb0, 0x4f, 0xb3, 0x5c, 0x21, 0x10, 0x6c, 0x5a, 0x6f, 0x75, 0x6c, 0xbd, 0xcc, - 0xd6, 0x7a, 0xb3, 0xb5, 0xdb, 0xd4, 0xc1, 0xf8, 0xa3, 0xc6, 0xae, 0xa4, 0x88, 0x76, 0x68, 0x37, - 0x4c, 0x39, 0x18, 0x6c, 0x22, 0x38, 0xda, 0x12, 0xf5, 0x7d, 0xfe, 0xbe, 0xb7, 0x09, 0xeb, 0xe2, - 0x47, 0x68, 0x3a, 0xf5, 0xb0, 0x69, 0x7a, 0xf5, 0x7d, 0xd3, 0x63, 0xb3, 0xf3, 0x50, 0x2f, 0xe0, - 0x92, 0x53, 0x28, 0x61, 0xe0, 0x76, 0x6a, 0x4d, 0xbd, 0xc8, 0x66, 0x38, 0x43, 0x6f, 0xb7, 0x1c, - 0xbd, 0x84, 0x0b, 0x78, 0x82, 0x1b, 0x9b, 0x65, 0xe5, 0x65, 0xa3, 0x07, 0x7a, 0x3e, 0x70, 0x62, - 0xe2, 0xa1, 0xd5, 0xeb, 0x38, 0x0e, 0xdf, 0xa1, 0x2b, 0xb0, 0xe8, 0x06, 0x4d, 0xea, 0x09, 0x3c, - 0x2c, 0x04, 0xc0, 0xea, 0x38, 0x66, 0x27, 0x68, 0xba, 0x5e, 0xeb, 0x11, 0x6e, 0xd5, 0x75, 0xb8, - 0xe6, 0x5b, 0x66, 0x6d, 0x2f, 0x74, 0xdc, 0x20, 0x6c, 0x39, 0x61, 0xad, 0x69, 0x3a, 0x0e, 0xb5, - 0x74, 0x30, 0xfe, 0xae, 0xc1, 0x8d, 0x73, 0x9e, 0x63, 0xc8, 0x1b, 0x70, 0xa7, 0x49, 0xcd, 0xba, - 0x45, 0x7d, 0x3f, 0x64, 0x4d, 0x52, 0x27, 0x10, 0x6f, 0x9e, 0x53, 0x97, 0xc3, 0x1d, 0xf8, 0xd6, - 0xf9, 0xec, 0xa9, 0x60, 0xf9, 0x36, 0xbc, 0x7e, 0x3e, 0xab, 0x10, 0x34, 0x05, 0x62, 0xc0, 0xed, - 0xf3, 0x39, 0x13, 0x01, 0x55, 0x34, 0x7e, 0xaf, 0xc1, 0xda, 0x74, 0x7d, 0x8c, 0xf5, 0xad, 0xe5, - 0xf8, 0x81, 0x69, 0x59, 0x61, 0xdb, 0xf4, 0x4c, 0x3b, 0xa4, 0x8e, 0xe7, 0x5a, 0xd6, 0xb4, 0x8d, - 0xf9, 0x3a, 0xdc, 0x9a, 0xcd, 0xea, 0xd7, 0xbc, 0x56, 0x9b, 0xad, 0xf0, 0x2a, 0x6c, 0xcd, 0xe6, - 0xa2, 0xad, 0x1a, 0xd5, 0x0b, 0x3b, 0x1f, 0x7c, 0xf6, 0xaf, 0xad, 0xb9, 0xcf, 0x9e, 0x6f, 0x69, - 0x9f, 0x3f, 0xdf, 0xd2, 0xfe, 0xf9, 0x7c, 0x4b, 0x7b, 0xf4, 0x9d, 0x4b, 0x64, 0xc2, 0x7b, 0x5c, - 0xc1, 0x47, 0xfe, 0xef, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0x25, 0xee, 0x1a, 0x11, 0x1b, 0x80, - 0x01, 0x00, + 0x58, 0xec, 0xdf, 0x45, 0x76, 0xc6, 0xfc, 0xbf, 0xd4, 0x5d, 0xcd, 0x6f, 0x1b, 0xc7, 0x15, 0xd7, + 0xf2, 0x2b, 0xd2, 0x93, 0x64, 0xad, 0xc6, 0x8e, 0xac, 0xca, 0xb2, 0xe2, 0x12, 0xa9, 0x51, 0x6f, + 0x1b, 0xa7, 0x49, 0x1b, 0x24, 0x4a, 0x91, 0x06, 0x2b, 0x72, 0x28, 0xd2, 0x5a, 0xee, 0xd2, 0xbb, + 0x4b, 0x2b, 0x76, 0x3f, 0x16, 0xb4, 0xb4, 0x96, 0xd8, 0x52, 0x4b, 0x86, 0x1f, 0x71, 0xdd, 0x5b, + 0x2f, 0xbd, 0x14, 0x05, 0x8a, 0x5e, 0x7b, 0xe8, 0x25, 0x97, 0xfe, 0x0d, 0xfd, 0x07, 0x02, 0x14, + 0x05, 0x72, 0x2f, 0x50, 0xb4, 0x06, 0xfa, 0x27, 0xf4, 0x92, 0x53, 0x31, 0x6f, 0x66, 0x76, 0x67, + 0x97, 0xa4, 0x22, 0x39, 0x46, 0x8b, 0xdc, 0xc4, 0x37, 0x6f, 0x46, 0xb3, 0xf3, 0xf9, 0xde, 0x9b, + 0xf7, 0x7e, 0xcf, 0x67, 0x92, 0x79, 0xe2, 0x97, 0x8a, 0x77, 0x0c, 0x4a, 0xeb, 0x19, 0xc7, 0xd4, + 0x98, 0x91, 0x55, 0xca, 0x64, 0x40, 0x57, 0x33, 0x5f, 0xa4, 0x2b, 0x49, 0xc6, 0xf2, 0x1f, 0x73, + 0xb0, 0xc1, 0x96, 0x4f, 0x2f, 0x1c, 0x8d, 0xcc, 0xc9, 0xf8, 0x34, 0x8c, 0xc6, 0x42, 0xa0, 0x22, + 0xef, 0x42, 0xe9, 0xf4, 0x72, 0xb6, 0x40, 0xce, 0x4e, 0x08, 0xe0, 0x91, 0x2c, 0x1d, 0x03, 0xd8, + 0xdf, 0xe4, 0x26, 0x28, 0x99, 0x46, 0xf0, 0x44, 0x5d, 0x71, 0x97, 0x06, 0x71, 0xbe, 0x91, 0xf7, + 0xa0, 0x88, 0xba, 0xbf, 0x38, 0x18, 0xa5, 0x40, 0x3b, 0xbb, 0x67, 0x68, 0x19, 0x70, 0x79, 0x05, + 0xf2, 0x26, 0x40, 0x82, 0x0e, 0x26, 0x4e, 0x3e, 0xa9, 0x44, 0xc7, 0x00, 0x61, 0xee, 0xd2, 0xd9, + 0x93, 0x8e, 0x80, 0xdc, 0x32, 0x60, 0x5d, 0x0e, 0xc9, 0x40, 0xc6, 0x3f, 0x73, 0x4b, 0x84, 0xbb, + 0xc6, 0x0b, 0x1a, 0x03, 0x11, 0x03, 0x5d, 0xfe, 0x77, 0x0e, 0x96, 0x0e, 0x99, 0x98, 0x80, 0xca, + 0xef, 0xf9, 0xca, 0xf4, 0xdb, 0xb0, 0x6c, 0xf5, 0x3b, 0xc2, 0x72, 0x3f, 0x12, 0x10, 0x0c, 0xe8, + 0x9c, 0xd9, 0xeb, 0x77, 0xe4, 0x23, 0xc0, 0xc8, 0x55, 0x99, 0xbe, 0xc4, 0xb1, 0xf4, 0x1e, 0x94, + 0xf8, 0x0b, 0xb7, 0x30, 0xd3, 0x48, 0x41, 0x31, 0xee, 0xd1, 0x5d, 0x5e, 0xac, 0x18, 0x9b, 0xf9, + 0x1b, 0xb9, 0x2a, 0xb5, 0x08, 0x04, 0x06, 0x45, 0xd5, 0x2f, 0x5e, 0x4c, 0xd5, 0x57, 0x22, 0x4d, + 0x4b, 0x17, 0x89, 0x34, 0xdd, 0xda, 0x85, 0x65, 0xa5, 0x3f, 0x97, 0x92, 0x1b, 0x7f, 0x9d, 0x83, + 0x55, 0xfc, 0xaa, 0xf8, 0x95, 0xe8, 0xeb, 0x69, 0xb8, 0x78, 0x3f, 0x65, 0xb8, 0xd8, 0x54, 0xe7, + 0x8b, 0x7f, 0xd9, 0x39, 0x16, 0x8b, 0x7b, 0xb0, 0x3e, 0xc5, 0x48, 0xde, 0x81, 0x22, 0xeb, 0xbe, + 0x54, 0xf4, 0xf4, 0xec, 0x0a, 0x48, 0x50, 0x49, 0xd8, 0x87, 0x8f, 0x5c, 0xce, 0x5d, 0xfe, 0x8f, + 0x06, 0x2b, 0x02, 0x2e, 0x2e, 0x7a, 0xd2, 0xff, 0xd2, 0xe1, 0xbc, 0x9d, 0x1d, 0x4e, 0x1e, 0x26, + 0x21, 0x86, 0xf3, 0x7f, 0x3d, 0x88, 0xbb, 0xa9, 0x41, 0xbc, 0x1e, 0xc7, 0x28, 0xcb, 0xcf, 0x39, + 0x67, 0x0c, 0xff, 0x82, 0xa8, 0x1d, 0x69, 0x46, 0xf2, 0x33, 0x58, 0xb2, 0xc3, 0xa7, 0x29, 0x7d, + 0xe9, 0xf6, 0x9c, 0x46, 0xef, 0xc6, 0x8c, 0x7c, 0x4f, 0xe1, 0x55, 0x13, 0x85, 0x4f, 0x83, 0xa9, + 0x47, 0x9c, 0xa4, 0x49, 0xa6, 0x32, 0xa5, 0xab, 0x5d, 0x66, 0xe9, 0x0b, 0x67, 0x3c, 0x8c, 0xfc, + 0xf9, 0x73, 0x1e, 0x20, 0xf1, 0x63, 0x62, 0x1b, 0x30, 0x4c, 0x81, 0xaa, 0x0a, 0xcb, 0x31, 0x92, + 0xd4, 0x35, 0x2e, 0x48, 0xe4, 0xb6, 0x30, 0x89, 0xe6, 0xe6, 0xc7, 0x90, 0xa3, 0x71, 0xb4, 0x22, + 0xfc, 0x84, 0x8e, 0xc3, 0x5e, 0x87, 0x9f, 0xc5, 0x79, 0xcc, 0xeb, 0x7f, 0x2d, 0xa1, 0xce, 0xc9, + 0xfb, 0x81, 0xde, 0x44, 0x55, 0xc6, 0x30, 0xe5, 0x1b, 0x58, 0x78, 0x71, 0xdf, 0xc0, 0xe2, 0x0b, + 0xf8, 0x06, 0x96, 0x2e, 0xe8, 0x1b, 0xd8, 0x82, 0xa5, 0x6e, 0xf4, 0x49, 0x18, 0x8d, 0xfb, 0xc3, + 0x67, 0xe8, 0x39, 0x94, 0x18, 0xb2, 0xd8, 0x50, 0x37, 0x64, 0x19, 0x9f, 0x6f, 0xbc, 0x30, 0x63, + 0x7e, 0x75, 0xba, 0x63, 0xa2, 0x70, 0x61, 0xf8, 0x22, 0x07, 0x64, 0xba, 0x01, 0xf2, 0x3e, 0x2c, + 0xf3, 0x23, 0x38, 0x18, 0x8e, 0x3e, 0x16, 0x8e, 0x65, 0x3c, 0xc2, 0x4a, 0x21, 0xab, 0x11, 0x56, + 0x9c, 0xec, 0x8e, 0x3e, 0xee, 0x91, 0x9f, 0xc2, 0x55, 0x9c, 0x80, 0x41, 0x38, 0xec, 0xf6, 0x8f, + 0x03, 0xc4, 0xb8, 0xe8, 0xf4, 0x04, 0x8a, 0xf7, 0x1b, 0x98, 0x6e, 0x62, 0xba, 0x78, 0xce, 0x44, + 0xa1, 0xff, 0x56, 0x0b, 0x39, 0x5b, 0x9c, 0x91, 0xf8, 0xa0, 0xab, 0xf5, 0x9f, 0x4c, 0x7a, 0x3d, + 0x31, 0xf7, 0x06, 0xe6, 0xe2, 0xcd, 0x94, 0xcd, 0x69, 0xf8, 0x4a, 0xd2, 0x70, 0x6d, 0xd2, 0xeb, + 0x91, 0x77, 0x01, 0xfa, 0x51, 0x70, 0xd6, 0x1d, 0x8d, 0xf8, 0x73, 0x42, 0xec, 0x7b, 0x99, 0x50, + 0xd5, 0x61, 0xec, 0x47, 0x4d, 0x4e, 0x24, 0x3f, 0x00, 0x74, 0xcc, 0xc6, 0x88, 0x05, 0x5c, 0x01, + 0x45, 0x81, 0xcb, 0x27, 0x89, 0xe9, 0xe9, 0x3c, 0x09, 0xbd, 0xee, 0xaf, 0xa4, 0xff, 0xc8, 0x23, + 0x58, 0x17, 0x11, 0x59, 0x87, 0xdd, 0xf1, 0xa9, 0x90, 0x7e, 0xbf, 0x8a, 0xe8, 0xac, 0x88, 0xbf, + 0x7f, 0x2f, 0x00, 0x98, 0x87, 0x9e, 0x0c, 0x06, 0xbc, 0x03, 0x45, 0x26, 0xd3, 0x4b, 0xdb, 0x00, + 0x5a, 0x56, 0xb1, 0x5d, 0xd5, 0xb2, 0x8a, 0x1c, 0x6c, 0xbf, 0xba, 0xe1, 0x09, 0x9a, 0xa7, 0x72, + 0x89, 0x21, 0x61, 0xc8, 0x49, 0x29, 0x19, 0x92, 0x93, 0x88, 0x05, 0x90, 0x84, 0xe7, 0x09, 0x2d, + 0x73, 0x3d, 0x89, 0x73, 0x11, 0x05, 0x02, 0x70, 0x2d, 0x09, 0xf1, 0x53, 0x97, 0x4f, 0xc2, 0x46, + 0x0e, 0xa0, 0xe0, 0x77, 0x62, 0xcf, 0xc2, 0x39, 0x41, 0x8b, 0xb7, 0x04, 0xca, 0x7a, 0x12, 0xb8, + 0x78, 0x65, 0xdc, 0x49, 0x25, 0xa3, 0xc0, 0x46, 0x08, 0x85, 0x92, 0xc8, 0xa0, 0x33, 0x27, 0x82, + 0x5d, 0x24, 0xd0, 0x11, 0xb8, 0x35, 0x48, 0x54, 0xa5, 0x0e, 0x91, 0x2b, 0xe7, 0x6d, 0xc8, 0x7b, + 0x5e, 0x53, 0xb8, 0xea, 0xaf, 0x26, 0x1a, 0x83, 0xe7, 0x35, 0x65, 0x96, 0xb0, 0x33, 0xa5, 0x1a, + 0x63, 0x26, 0x3f, 0x84, 0x65, 0x45, 0x20, 0x16, 0x41, 0x2e, 0x38, 0x06, 0xdd, 0x84, 0x9c, 0x92, + 0xab, 0x13, 0x32, 0xb1, 0x40, 0x3f, 0x98, 0x3c, 0x0e, 0xcd, 0xc1, 0x00, 0xbd, 0xe2, 0x3e, 0x09, + 0x87, 0x1c, 0x0c, 0x6e, 0x31, 0x81, 0x7c, 0x09, 0x3a, 0x83, 0x41, 0x70, 0x2c, 0x4b, 0x55, 0xfb, + 0x48, 0xb6, 0x26, 0x69, 0xc1, 0xba, 0x17, 0x8e, 0x27, 0x03, 0xee, 0x0c, 0x51, 0xeb, 0x0f, 0x99, + 0x82, 0xc0, 0x43, 0x62, 0x10, 0x1d, 0x63, 0xc4, 0x0a, 0xa5, 0x07, 0xca, 0x93, 0xfe, 0x30, 0xa3, + 0x2c, 0x4c, 0x57, 0x2e, 0x87, 0xea, 0x94, 0xb3, 0x7b, 0x37, 0xad, 0x76, 0xe0, 0xbd, 0x2b, 0xd5, + 0x8e, 0x44, 0xd9, 0x78, 0x73, 0x46, 0xd8, 0x26, 0x3e, 0x66, 0x29, 0x61, 0x9b, 0xa9, 0x60, 0xcd, + 0x4f, 0x0b, 0x0a, 0x1c, 0x80, 0x98, 0x8b, 0x0f, 0x00, 0xee, 0xf5, 0xbb, 0x51, 0x33, 0x1c, 0x9f, + 0xf6, 0x8f, 0x95, 0xe8, 0xd1, 0xe5, 0x9f, 0xf7, 0xbb, 0x51, 0x70, 0x86, 0xe4, 0x2f, 0xfe, 0xf1, + 0x9a, 0xc2, 0xe4, 0x2a, 0x7f, 0x93, 0xef, 0xc2, 0x12, 0xfb, 0xe5, 0x27, 0x2e, 0x1d, 0xdc, 0x8c, + 0x88, 0xb5, 0x45, 0xb6, 0xc1, 0x98, 0x81, 0xec, 0x22, 0x62, 0x63, 0x77, 0x30, 0x56, 0xc4, 0x5b, + 0x09, 0xcf, 0xd8, 0x1d, 0x8c, 0xb3, 0x08, 0x2f, 0x0a, 0x33, 0xa9, 0xc7, 0x5d, 0x97, 0x98, 0x9f, + 0x02, 0x18, 0x12, 0x6d, 0x65, 0x62, 0xad, 0x05, 0x12, 0x5a, 0x42, 0xcd, 0xce, 0x90, 0xa9, 0x86, + 0x9d, 0xf0, 0xea, 0x55, 0xfe, 0xb8, 0x21, 0x6e, 0x19, 0xde, 0x89, 0xd1, 0xe9, 0x71, 0x70, 0x84, + 0xe4, 0x54, 0x27, 0x62, 0x66, 0xb2, 0x07, 0x6b, 0x3c, 0xc6, 0x29, 0xc6, 0x0e, 0x17, 0x37, 0x0e, + 0x9e, 0x6d, 0x09, 0xb8, 0xb8, 0xfa, 0xef, 0x33, 0x15, 0x48, 0x0d, 0x8a, 0xa8, 0xe2, 0x89, 0x40, + 0x93, 0x1b, 0xaa, 0x66, 0x9b, 0xdd, 0x47, 0x78, 0xae, 0xa0, 0x4e, 0xab, 0x9e, 0x2b, 0xc8, 0x4a, + 0x3e, 0x02, 0xa0, 0xd1, 0xb0, 0xdf, 0xeb, 0x21, 0xf8, 0xc9, 0x22, 0x2a, 0x48, 0x37, 0xd3, 0xfb, + 0x11, 0x5b, 0x49, 0x98, 0x64, 0x9e, 0x78, 0xf6, 0x3b, 0xc8, 0x40, 0xa4, 0x28, 0x6d, 0x95, 0x1b, + 0x50, 0xe2, 0x9b, 0x11, 0x81, 0x84, 0x04, 0x4a, 0xa1, 0x02, 0x43, 0xc3, 0x81, 0x84, 0x04, 0x7d, + 0x1a, 0x48, 0x48, 0xa9, 0x50, 0x3e, 0x80, 0x6b, 0xb3, 0x3e, 0x2c, 0xa5, 0x94, 0x6a, 0x17, 0x55, + 0x4a, 0xff, 0x94, 0x87, 0x15, 0x6c, 0x4d, 0x9e, 0xc2, 0x26, 0xac, 0x7a, 0x93, 0xc7, 0x71, 0x40, + 0x9e, 0x3c, 0x8d, 0x79, 0x8a, 0x7e, 0xb5, 0x40, 0x7d, 0x76, 0x4a, 0xd5, 0x20, 0x14, 0xae, 0xc8, + 0x9b, 0x40, 0x64, 0x4f, 0xc8, 0x25, 0x18, 0x3e, 0x32, 0x54, 0x7c, 0x3a, 0x77, 0x42, 0xa6, 0x52, + 0x72, 0x1f, 0xe4, 0x2f, 0x73, 0x1f, 0x14, 0x2e, 0x74, 0x1f, 0xfc, 0x18, 0x56, 0xe4, 0x7f, 0xc3, + 0x93, 0xbc, 0xf8, 0xd5, 0x4e, 0xf2, 0x54, 0x63, 0xc4, 0x8a, 0x4f, 0xf4, 0xd2, 0xb9, 0x27, 0x3a, + 0xbe, 0xe5, 0xc9, 0x5d, 0x36, 0x95, 0x0e, 0x4d, 0xb4, 0x81, 0xe0, 0xe2, 0xfb, 0x95, 0xd6, 0x0b, + 0xdc, 0x92, 0xef, 0xc0, 0x92, 0xd5, 0x97, 0xcf, 0x38, 0x8a, 0xfd, 0xbc, 0x27, 0x89, 0xaa, 0xb8, + 0x10, 0x73, 0xc6, 0xb7, 0x5b, 0xfe, 0x65, 0xdc, 0x6e, 0xbb, 0x00, 0x2d, 0x1e, 0x46, 0x91, 0x80, + 0x02, 0xe3, 0x96, 0x91, 0xf1, 0x16, 0x69, 0x33, 0xbe, 0xc2, 0xcc, 0x4e, 0x27, 0xe1, 0xf0, 0x61, + 0x1e, 0x1d, 0xf5, 0x27, 0xd1, 0x38, 0x95, 0x45, 0x43, 0x84, 0x5e, 0xb1, 0x2b, 0x01, 0xcb, 0xd4, + 0xe3, 0x21, 0x53, 0xed, 0xe5, 0x4e, 0x08, 0xb9, 0x1f, 0x7b, 0xaa, 0x9d, 0x9b, 0x54, 0xb0, 0x3c, + 0x35, 0x42, 0x73, 0xfd, 0xd3, 0xca, 0x7f, 0xd5, 0x54, 0x00, 0xb5, 0x17, 0x98, 0xea, 0xf7, 0x00, + 0xe2, 0x77, 0x74, 0x39, 0xd7, 0x5c, 0xa3, 0x8a, 0xa9, 0xea, 0x28, 0x27, 0xbc, 0xca, 0xd7, 0xe4, + 0x5f, 0xd6, 0xd7, 0xf8, 0xb0, 0xec, 0xfc, 0x62, 0xdc, 0x49, 0x1c, 0x2f, 0xc0, 0x8b, 0x25, 0x59, + 0x3c, 0x99, 0x64, 0xf2, 0xc3, 0x44, 0x0e, 0x9e, 0x9b, 0xfc, 0x30, 0xae, 0x58, 0xbe, 0x0f, 0x6b, + 0xaa, 0x53, 0xf8, 0xb3, 0xe8, 0x88, 0xfc, 0x88, 0x23, 0x3f, 0x68, 0x29, 0x5d, 0x43, 0x61, 0x62, + 0x27, 0xee, 0xb3, 0xe8, 0x88, 0xcb, 0x3f, 0x9d, 0xa7, 0x6a, 0x5f, 0x51, 0x0b, 0xfc, 0x5c, 0x03, + 0x32, 0xcd, 0xae, 0x9e, 0x26, 0xda, 0xff, 0x41, 0xba, 0xcc, 0x48, 0x65, 0x85, 0xcb, 0x48, 0x65, + 0xc6, 0xef, 0x35, 0x58, 0x6b, 0x98, 0x4d, 0x81, 0x76, 0xc6, 0xdf, 0x03, 0xbe, 0x09, 0x37, 0x1b, + 0x66, 0x33, 0x68, 0x39, 0x56, 0xa3, 0xf2, 0x30, 0x98, 0x09, 0x62, 0x72, 0x13, 0xbe, 0x31, 0xcd, + 0x92, 0xbc, 0x1b, 0x6c, 0xc3, 0xe6, 0x74, 0xb1, 0x04, 0x3a, 0x99, 0x5d, 0x59, 0x62, 0xa2, 0xe4, + 0x8d, 0x0f, 0x61, 0x4d, 0xe2, 0x7f, 0xf8, 0x96, 0x87, 0xb0, 0x61, 0x6b, 0xb0, 0xfc, 0x80, 0xba, + 0x8d, 0xda, 0xc3, 0xa0, 0xd6, 0xb6, 0x2c, 0x7d, 0x81, 0xac, 0xc2, 0x92, 0x20, 0x54, 0x4c, 0x5d, + 0x23, 0x2b, 0xb0, 0xd8, 0xb0, 0x3d, 0x5a, 0x69, 0xbb, 0x54, 0xcf, 0x19, 0x1f, 0xc2, 0x95, 0x24, + 0x43, 0x3c, 0x9a, 0xfd, 0x5f, 0x81, 0xbc, 0x6b, 0x1e, 0xea, 0x0b, 0x04, 0xa0, 0xd4, 0x3a, 0xa8, + 0x78, 0x6f, 0xbd, 0xa5, 0x6b, 0x64, 0x19, 0x5e, 0xd9, 0xaf, 0xb4, 0x82, 0x83, 0xa6, 0xa7, 0xe7, + 0xd8, 0x0f, 0xf3, 0xd0, 0xc3, 0x1f, 0x79, 0xe3, 0x7b, 0xb0, 0x8e, 0xb2, 0x82, 0xd5, 0x1d, 0x8d, + 0xc3, 0x28, 0x1c, 0x62, 0x1f, 0x56, 0x60, 0xd1, 0x0b, 0xd9, 0x26, 0x1f, 0x87, 0xbc, 0x03, 0xcd, + 0x49, 0x6f, 0xdc, 0x1d, 0xf4, 0xc2, 0x5f, 0xea, 0x9a, 0xb1, 0x0b, 0x6b, 0x6e, 0x7f, 0x32, 0xee, + 0x46, 0x27, 0xde, 0x98, 0x71, 0x9c, 0x3c, 0x23, 0xaf, 0xc2, 0x7a, 0xdb, 0x36, 0x9b, 0x7b, 0x8d, + 0xfd, 0xb6, 0xd3, 0xf6, 0x82, 0xa6, 0xe9, 0x57, 0xea, 0xfc, 0xd1, 0xa1, 0xe9, 0x78, 0x7e, 0xe0, + 0xd2, 0x0a, 0xb5, 0x7d, 0x5d, 0x33, 0x7e, 0xab, 0xc1, 0x95, 0xf6, 0x48, 0x38, 0xca, 0xb6, 0x11, + 0xcb, 0xe2, 0x16, 0x6c, 0xb7, 0x3d, 0xea, 0x06, 0xbe, 0x73, 0x40, 0xed, 0xa0, 0xed, 0x99, 0xfb, + 0x59, 0x04, 0x9d, 0xd7, 0xe0, 0x86, 0xc2, 0xe1, 0xd2, 0x8a, 0xf3, 0x80, 0xba, 0x41, 0xcb, 0xf4, + 0xbc, 0x43, 0xc7, 0xad, 0xea, 0x1a, 0xd9, 0x82, 0x8d, 0x19, 0x0c, 0xcd, 0x9a, 0xa9, 0xe7, 0xa6, + 0xca, 0x6c, 0x7a, 0x68, 0x5a, 0xc1, 0x9e, 0xe3, 0xeb, 0x79, 0xa3, 0xc9, 0x2e, 0x3a, 0x04, 0x99, + 0xe0, 0x10, 0xa1, 0x8b, 0x50, 0xb0, 0x1d, 0x9b, 0x66, 0x1f, 0x86, 0x56, 0x60, 0xd1, 0x6c, 0xb5, + 0x5c, 0xe7, 0x01, 0x4e, 0x28, 0x40, 0xa9, 0x4a, 0x6d, 0xd6, 0xb3, 0x3c, 0x2b, 0x69, 0xb9, 0x4e, + 0xd3, 0xf1, 0x69, 0x55, 0x2f, 0x18, 0xae, 0xdc, 0x30, 0xb2, 0xd1, 0xa3, 0x3e, 0x7f, 0x85, 0xa9, + 0xd2, 0x9a, 0xd9, 0xb6, 0x7c, 0x31, 0x20, 0x0f, 0x03, 0x97, 0xde, 0x6f, 0x53, 0xcf, 0xf7, 0x74, + 0x8d, 0xe8, 0xb0, 0x62, 0x53, 0x5a, 0xf5, 0x02, 0x97, 0x3e, 0x68, 0xd0, 0x43, 0x3d, 0xc7, 0xda, + 0xe4, 0x7f, 0xb3, 0xff, 0x60, 0x7c, 0xaa, 0x01, 0xe1, 0x00, 0x1d, 0x12, 0xca, 0x11, 0xe7, 0x67, + 0x07, 0xb6, 0xea, 0x6c, 0x60, 0xf1, 0xd3, 0x9a, 0x4e, 0x35, 0x3b, 0x64, 0x1b, 0x40, 0x32, 0xe5, + 0x4e, 0xad, 0xa6, 0x6b, 0xe4, 0x06, 0x5c, 0xcd, 0xd0, 0xab, 0xae, 0xd3, 0xd2, 0x73, 0x5b, 0xb9, + 0x45, 0x8d, 0x5c, 0x9f, 0x2a, 0x3c, 0xa0, 0xb4, 0xa5, 0xe7, 0xd9, 0x14, 0x65, 0x0a, 0xe4, 0x02, + 0xe4, 0xd5, 0x0b, 0xc6, 0x6f, 0x34, 0xd8, 0xe0, 0xdd, 0x94, 0xab, 0x39, 0xee, 0xea, 0x36, 0x6c, + 0x0a, 0x2c, 0xa1, 0x59, 0x1d, 0xbd, 0x06, 0x7a, 0xaa, 0x94, 0x77, 0xf3, 0x55, 0x58, 0x4f, 0x51, + 0xb1, 0x1f, 0x39, 0xb6, 0x57, 0x53, 0xe4, 0x3d, 0xea, 0xf9, 0x01, 0xad, 0xd5, 0x1c, 0xd7, 0xe7, + 0x1d, 0xc9, 0x1b, 0x65, 0x58, 0xaf, 0x84, 0xc3, 0x31, 0xd3, 0x41, 0xa2, 0x51, 0xb7, 0x1f, 0x61, + 0x17, 0x56, 0x61, 0x89, 0x7e, 0xe4, 0x53, 0xdb, 0x6b, 0x38, 0xb6, 0xbe, 0x60, 0x6c, 0x67, 0x78, + 0xe4, 0xae, 0xf1, 0xbc, 0xba, 0xbe, 0x60, 0x74, 0x60, 0x55, 0x3a, 0xa6, 0xf2, 0x55, 0xb1, 0x03, + 0x5b, 0x72, 0xad, 0xe1, 0xfe, 0xcd, 0x7e, 0xc2, 0x26, 0x5c, 0x9b, 0x2e, 0xa7, 0xbe, 0xae, 0xb1, + 0x59, 0xc8, 0x94, 0x30, 0x7a, 0xce, 0xf8, 0x09, 0xac, 0xa4, 0x30, 0x8f, 0xaf, 0xc3, 0x55, 0xf5, + 0x77, 0x2b, 0x8c, 0x8e, 0xbb, 0xd1, 0x89, 0xbe, 0x90, 0x2d, 0x70, 0x27, 0x51, 0xc4, 0x0a, 0x70, + 0xc5, 0xab, 0x05, 0x7e, 0x38, 0x3c, 0xeb, 0x46, 0x9d, 0x71, 0x78, 0xac, 0xe7, 0x8c, 0xbb, 0xb0, + 0x9a, 0x02, 0x65, 0x61, 0x9f, 0x66, 0x39, 0xe2, 0x40, 0x68, 0xd2, 0x6a, 0xa3, 0xdd, 0xd4, 0x8b, + 0x6c, 0xad, 0xd7, 0x1b, 0xfb, 0x75, 0x1d, 0x8c, 0x3f, 0x68, 0x4c, 0x24, 0x45, 0xfc, 0xc4, 0x66, + 0xcd, 0x94, 0x83, 0xc1, 0x26, 0x82, 0xe3, 0x37, 0x51, 0xcf, 0xe3, 0x2f, 0x86, 0xdb, 0xb0, 0x29, + 0x7e, 0x04, 0xa6, 0x5d, 0x0d, 0xea, 0xa6, 0x5b, 0x3d, 0x34, 0x5d, 0x36, 0x3b, 0x0f, 0xf5, 0x1c, + 0x2e, 0x39, 0x85, 0x12, 0xf8, 0x4e, 0xbb, 0x52, 0xd7, 0xf3, 0x6c, 0x86, 0x53, 0xf4, 0x56, 0xc3, + 0xd6, 0x0b, 0xb8, 0x80, 0xa7, 0xb8, 0xb1, 0x59, 0x56, 0x5e, 0x34, 0xba, 0xa0, 0x67, 0x43, 0x31, + 0xa6, 0x9e, 0x6e, 0xdd, 0xb6, 0x6d, 0xf3, 0x1d, 0xba, 0x06, 0xcb, 0x8e, 0x5f, 0xa7, 0xae, 0x40, + 0xd8, 0x42, 0x48, 0xad, 0xb6, 0x6d, 0xb6, 0xfd, 0xba, 0xe3, 0x36, 0x1e, 0xe1, 0x56, 0xdd, 0x84, + 0x6b, 0x9e, 0x65, 0x56, 0x0e, 0x02, 0xdb, 0xf1, 0x83, 0x86, 0x1d, 0x54, 0xea, 0xa6, 0x6d, 0x53, + 0x4b, 0x07, 0xe3, 0x6f, 0x1a, 0xdc, 0x38, 0xe7, 0x81, 0x87, 0xbc, 0x01, 0x77, 0xea, 0xd4, 0xac, + 0x5a, 0xd4, 0xf3, 0x02, 0xd6, 0x24, 0xb5, 0x7d, 0xf1, 0x8a, 0x3a, 0x73, 0x39, 0xdc, 0x81, 0x6f, + 0x9d, 0xcf, 0x9e, 0x1c, 0x2c, 0xdf, 0x86, 0xd7, 0xcf, 0x67, 0x15, 0x07, 0x4d, 0x8e, 0x18, 0x70, + 0xfb, 0x7c, 0xce, 0xf8, 0x80, 0xca, 0x1b, 0xbf, 0xd3, 0x60, 0x63, 0xb6, 0x3e, 0xc6, 0xfa, 0xd6, + 0xb0, 0x3d, 0xdf, 0xb4, 0xac, 0xa0, 0x65, 0xba, 0x66, 0x33, 0xa0, 0xb6, 0xeb, 0x58, 0xd6, 0xac, + 0x8d, 0xf9, 0x3a, 0xdc, 0x9a, 0xcf, 0xea, 0x55, 0xdc, 0x46, 0x8b, 0xad, 0xf0, 0x32, 0xec, 0xcc, + 0xe7, 0xa2, 0x8d, 0x0a, 0xd5, 0x73, 0x7b, 0x1f, 0x7c, 0xf6, 0xaf, 0x9d, 0x85, 0xcf, 0x9e, 0xef, + 0x68, 0x9f, 0x3f, 0xdf, 0xd1, 0xfe, 0xf9, 0x7c, 0x47, 0x7b, 0xf4, 0x9d, 0x4b, 0xe4, 0xd6, 0x7b, + 0x5c, 0x42, 0xb7, 0x81, 0xef, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x21, 0x75, 0x47, 0x6d, + 0x80, 0x01, 0x00, } func (this *PluginSpecV1) Equal(that interface{}) bool { @@ -42987,6 +42993,13 @@ func (m *AWSMatcher) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.SetupAccessForARN) > 0 { + i -= len(m.SetupAccessForARN) + copy(dAtA[i:], m.SetupAccessForARN) + i = encodeVarintTypes(dAtA, i, uint64(len(m.SetupAccessForARN))) + i-- + dAtA[i] = 0x4a + } if m.KubeAppDiscovery { i-- if m.KubeAppDiscovery { @@ -53286,6 +53299,10 @@ func (m *AWSMatcher) Size() (n int) { if m.KubeAppDiscovery { n += 2 } + l = len(m.SetupAccessForARN) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -115641,6 +115658,38 @@ func (m *AWSMatcher) Unmarshal(dAtA []byte) error { } } m.KubeAppDiscovery = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SetupAccessForARN", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SetupAccessForARN = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/lib/cloud/azure/kubernetes.go b/lib/cloud/azure/kubernetes.go index 9ce91f6f2fd..c9a6ddaf4d7 100644 --- a/lib/cloud/azure/kubernetes.go +++ b/lib/cloud/azure/kubernetes.go @@ -42,6 +42,8 @@ import ( authztypes "k8s.io/client-go/kubernetes/typed/authorization/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + + "github.com/gravitational/teleport/lib/fixtures" ) // AKSAuthMethod defines the authentication method for AKS cluster. @@ -348,7 +350,7 @@ func (c *aksClient) getAzureADCredentials(ctx context.Context, cluster ClusterCr adminCredentialsErr = trace.WrapWithMessage(adminCredentialsErr, `Tried to grant access to %s/%s using aks.ListClusterAdminCredentials`, cluster.ResourceGroup, cluster.ResourceName) // if the creation failed, then the agent will try to run a command to create them. fallthrough - case err != nil: + default: if runCMDErr = c.grantAccessWithCommand(ctx, cluster.ResourceGroup, cluster.ResourceName, cluster.TenantID, groupID); runCMDErr != nil { return nil, time.Time{}, trace.Wrap(err) } @@ -360,7 +362,6 @@ func (c *aksClient) getAzureADCredentials(ctx context.Context, cluster ClusterCr return nil, time.Time{}, trace.WrapWithMessage(trace.NewAggregate(adminCredentialsErr, runCMDErr), `Cannot grant access to %s/%s AKS cluster`, cluster.ResourceGroup, cluster.ResourceName) } - return nil, time.Time{}, trace.NotImplemented("code shouldn't reach") } // getAdminCredentials returns the cluster admin credentials by calling ListClusterAdminCredentials method. @@ -483,7 +484,7 @@ func (c *aksClient) grantAccessWithAdminCredentials(ctx context.Context, adminCf func (c *aksClient) upsertClusterRoleWithAdminCredentials(ctx context.Context, client *kubernetes.Clientset) error { clusterRole := &v1.ClusterRole{} - if err := yaml.Unmarshal([]byte(clusterRoleTemplate), clusterRole); err != nil { + if err := yaml.Unmarshal([]byte(fixtures.KubeClusterRoleTemplate), clusterRole); err != nil { return trace.Wrap(err) } @@ -509,7 +510,7 @@ func (c *aksClient) upsertClusterRoleWithAdminCredentials(ctx context.Context, c func (c *aksClient) upsertClusterRoleBindingWithAdminCredentials(ctx context.Context, client *kubernetes.Clientset, groupID string) error { clusterRoleBinding := &v1.ClusterRoleBinding{} - if err := yaml.Unmarshal([]byte(clusterRoleBindingTemplate), clusterRoleBinding); err != nil { + if err := yaml.Unmarshal([]byte(fixtures.KubeClusterRoleBindingTemplate), clusterRoleBinding); err != nil { return trace.Wrap(err) } @@ -664,50 +665,6 @@ func isAKSClusterRunning(properties *armcontainerservice.ManagedClusterPropertie return false } -const ( - clusterRoleTemplate = ` -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: teleport -rules: -- apiGroups: - - "" - resources: - - users - - groups - - serviceaccounts - verbs: - - impersonate -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - "authorization.k8s.io" - resources: - - selfsubjectaccessreviews - - selfsubjectrulesreviews - verbs: - - create -` - clusterRoleBindingTemplate = ` -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: teleport -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: teleport -subjects: -- kind: Group - name: group_name - apiGroup: rbac.authorization.k8s.io` -) - // kubectlApplyString generates a kubectl apply command to create the ClusterRole // and ClusterRoleBinding. // cat <. + */ + +package fixtures + +// KubeClusterRoleTemplate is a template for a Kubernetes ClusterRole +// that Teleport uses to access Kubernetes resources. +const KubeClusterRoleTemplate = ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: teleport +rules: +- apiGroups: + - "" + resources: + - users + - groups + - serviceaccounts + verbs: + - impersonate +- apiGroups: + - "" + resources: + - pods + verbs: + - get +- apiGroups: + - "authorization.k8s.io" + resources: + - selfsubjectaccessreviews + - selfsubjectrulesreviews + verbs: + - create +` + +// KubeClusterRoleBindingTemplate is a template for a Kubernetes ClusterRoleBinding +// that Teleport uses to access Kubernetes resources. +const KubeClusterRoleBindingTemplate = ` +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: teleport +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: teleport +subjects: +- kind: Group + name: group_name + apiGroup: rbac.authorization.k8s.io` diff --git a/lib/kube/proxy/cluster_details.go b/lib/kube/proxy/cluster_details.go index 0cec2f0e948..bc0c1435696 100644 --- a/lib/kube/proxy/cluster_details.go +++ b/lib/kube/proxy/cluster_details.go @@ -27,8 +27,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/eks" - "github.com/aws/aws-sdk-go/service/sts" - "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/sirupsen/logrus" @@ -42,6 +40,7 @@ import ( "github.com/gravitational/teleport/lib/cloud" "github.com/gravitational/teleport/lib/cloud/azure" "github.com/gravitational/teleport/lib/cloud/gcp" + kubeutils "github.com/gravitational/teleport/lib/kube/utils" "github.com/gravitational/teleport/lib/labels" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/services" @@ -343,7 +342,7 @@ func getAWSClientRestConfig(cloudClients cloud.Clients, clock clockwork.Clock, r return nil, time.Time{}, trace.Wrap(err) } - token, exp, err := genAWSToken(stsClient, cluster.GetAWSConfig().Name, clock) + token, exp, err := kubeutils.GenAWSEKSToken(stsClient, cluster.GetAWSConfig().Name, clock) if err != nil { return nil, time.Time{}, trace.Wrap(err) } @@ -358,39 +357,6 @@ func getAWSClientRestConfig(cloudClients cloud.Clients, clock clockwork.Clock, r } } -// genAWSToken creates an AWS token to access EKS clusters. -// Logic from https://github.com/aws/aws-cli/blob/6c0d168f0b44136fc6175c57c090d4b115437ad1/awscli/customizations/eks/get_token.py#L211-L229 -func genAWSToken(stsClient stsiface.STSAPI, clusterID string, clock clockwork.Clock) (string, time.Time, error) { - const ( - // The sts GetCallerIdentity request is valid for 15 minutes regardless of this parameters value after it has been - // signed. - requestPresignParam = 60 - // The actual token expiration (presigned STS urls are valid for 15 minutes after timestamp in x-amz-date). - presignedURLExpiration = 15 * time.Minute - v1Prefix = "k8s-aws-v1." - clusterIDHeader = "x-k8s-aws-id" - ) - - // generate an sts:GetCallerIdentity request and add our custom cluster ID header - request, _ := stsClient.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{}) - request.HTTPRequest.Header.Add(clusterIDHeader, clusterID) - - // Sign the request. The expires parameter (sets the x-amz-expires header) is - // currently ignored by STS, and the token expires 15 minutes after the x-amz-date - // timestamp regardless. We set it to 60 seconds for backwards compatibility (the - // parameter is a required argument to Presign(), and authenticators 0.3.0 and older are expecting a value between - // 0 and 60 on the server side). - // https://github.com/aws/aws-sdk-go/issues/2167 - presignedURLString, err := request.Presign(requestPresignParam) - if err != nil { - return "", time.Time{}, trace.Wrap(err) - } - - // Set token expiration to 1 minute before the presigned URL expires for some cushion - tokenExpiration := clock.Now().Add(presignedURLExpiration - 1*time.Minute) - return v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedURLString)), tokenExpiration, nil -} - // getStaticCredentialsFromKubeconfig loads a kubeconfig from the cluster and returns the access credentials for the cluster. // If the config defines multiple contexts, it will pick one (the order is not guaranteed). func getStaticCredentialsFromKubeconfig(ctx context.Context, component KubeServiceType, cluster types.KubeCluster, log *logrus.Entry, checker servicecfg.ImpersonationPermissionsChecker) (*staticKubeCreds, error) { diff --git a/lib/kube/utils/eks_token_signed.go b/lib/kube/utils/eks_token_signed.go new file mode 100644 index 00000000000..4431cf93dad --- /dev/null +++ b/lib/kube/utils/eks_token_signed.go @@ -0,0 +1,62 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package utils + +import ( + "encoding/base64" + "time" + + "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" +) + +// GenAWSEKSToken creates an AWS token to access EKS clusters. +// Logic from https://github.com/aws/aws-cli/blob/6c0d168f0b44136fc6175c57c090d4b115437ad1/awscli/customizations/eks/get_token.py#L211-L229 +func GenAWSEKSToken(stsClient stsiface.STSAPI, clusterID string, clock clockwork.Clock) (string, time.Time, error) { + const ( + // The sts GetCallerIdentity request is valid for 15 minutes regardless of this parameters value after it has been + // signed. + requestPresignParam = 60 + // The actual token expiration (presigned STS urls are valid for 15 minutes after timestamp in x-amz-date). + presignedURLExpiration = 15 * time.Minute + v1Prefix = "k8s-aws-v1." + clusterIDHeader = "x-k8s-aws-id" + ) + + // generate an sts:GetCallerIdentity request and add our custom cluster ID header + request, _ := stsClient.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{}) + request.HTTPRequest.Header.Add(clusterIDHeader, clusterID) + + // Sign the request. The expires parameter (sets the x-amz-expires header) is + // currently ignored by STS, and the token expires 15 minutes after the x-amz-date + // timestamp regardless. We set it to 60 seconds for backwards compatibility (the + // parameter is a required argument to Presign(), and authenticators 0.3.0 and older are expecting a value between + // 0 and 60 on the server side). + // https://github.com/aws/aws-sdk-go/issues/2167 + presignedURLString, err := request.Presign(requestPresignParam) + if err != nil { + return "", time.Time{}, trace.Wrap(err) + } + + // Set token expiration to 1 minute before the presigned URL expires for some cushion + tokenExpiration := clock.Now().Add(presignedURLExpiration - 1*time.Minute) + return v1Prefix + base64.RawURLEncoding.EncodeToString([]byte(presignedURLString)), tokenExpiration, nil +} diff --git a/lib/srv/discovery/discovery_test.go b/lib/srv/discovery/discovery_test.go index f77fe50e555..05b5ccda26d 100644 --- a/lib/srv/discovery/discovery_test.go +++ b/lib/srv/discovery/discovery_test.go @@ -1354,7 +1354,7 @@ func TestDiscoveryServer_New(t *testing.T) { discServer, err := New( ctx, &Config{ - CloudClients: nil, + CloudClients: tt.cloudClients, ClusterFeatures: func() proto.Features { return proto.Features{} }, AccessPoint: newFakeAccessPoint(), Matchers: tt.matchers, diff --git a/lib/srv/discovery/fetchers/eks.go b/lib/srv/discovery/fetchers/eks.go index c0330f01002..ad277008e0a 100644 --- a/lib/srv/discovery/fetchers/eks.go +++ b/lib/srv/discovery/fetchers/eks.go @@ -20,19 +20,38 @@ package fetchers import ( "context" + "encoding/base64" "fmt" + "path" + "slices" + "strings" "sync" + "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/eks/eksiface" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" + rbacv1 "k8s.io/api/rbac/v1" + kubeerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/cloud" + awslib "github.com/gravitational/teleport/lib/cloud/aws" + "github.com/gravitational/teleport/lib/fixtures" + kubeutils "github.com/gravitational/teleport/lib/kube/utils" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/srv/discovery/common" ) @@ -44,20 +63,24 @@ const ( type eksFetcher struct { EKSFetcherConfig - mu sync.Mutex - client eksiface.EKSAPI + mu sync.Mutex + client eksiface.EKSAPI + stsClient stsiface.STSAPI + callerIdentity string } -// EKSClientGetter is an interface for getting an EKS client. -type EKSClientGetter interface { +// ClientGetter is an interface for getting an EKS client and an STS client. +type ClientGetter interface { // GetAWSEKSClient returns AWS EKS client for the specified region. GetAWSEKSClient(ctx context.Context, region string, opts ...cloud.AWSOptionsFn) (eksiface.EKSAPI, error) + // GetAWSSTSClient returns AWS STS client for the specified region. + GetAWSSTSClient(ctx context.Context, region string, opts ...cloud.AWSOptionsFn) (stsiface.STSAPI, error) } // EKSFetcherConfig configures the EKS fetcher. type EKSFetcherConfig struct { - // EKSClientGetter retrieves an EKS client. - EKSClientGetter EKSClientGetter + // ClientGetter retrieves an EKS client and an STS client. + ClientGetter ClientGetter // AssumeRole provides a role ARN and ExternalID to assume an AWS role // when fetching clusters. AssumeRole types.AssumeRole @@ -74,12 +97,16 @@ type EKSFetcherConfig struct { FilterLabels types.Labels // Log is the logger. Log logrus.FieldLogger + // SetupAccessForARN is the ARN to setup access for. + SetupAccessForARN string + // Clock is the clock. + Clock clockwork.Clock } // CheckAndSetDefaults validates and sets the defaults values. func (c *EKSFetcherConfig) CheckAndSetDefaults() error { - if c.EKSClientGetter == nil { - return trace.BadParameter("missing EKSClientGetter field") + if c.ClientGetter == nil { + return trace.BadParameter("missing ClientGetter field") } if len(c.Region) == 0 { return trace.BadParameter("missing Region field") @@ -92,6 +119,11 @@ func (c *EKSFetcherConfig) CheckAndSetDefaults() error { if c.Log == nil { c.Log = logrus.WithField(teleport.ComponentKey, "fetcher:eks") } + + if c.Clock == nil { + c.Clock = clockwork.NewRealClock() + } + return nil } @@ -110,13 +142,14 @@ func MakeEKSFetchersFromAWSMatchers(log logrus.FieldLogger, clients cloud.AWSCli case types.AWSMatcherEKS: fetcher, err := NewEKSFetcher( EKSFetcherConfig{ - EKSClientGetter: clients, - AssumeRole: matcherAssumeRole, - Region: region, - Integration: matcher.Integration, - KubeAppDiscovery: matcher.KubeAppDiscovery, - FilterLabels: matcher.Tags, - Log: log, + ClientGetter: clients, + AssumeRole: matcherAssumeRole, + Region: region, + Integration: matcher.Integration, + KubeAppDiscovery: matcher.KubeAppDiscovery, + FilterLabels: matcher.Tags, + Log: log, + SetupAccessForARN: matcher.SetupAccessForARN, }, ) if err != nil { @@ -137,7 +170,22 @@ func NewEKSFetcher(cfg EKSFetcherConfig) (common.Fetcher, error) { return nil, trace.Wrap(err) } - return &eksFetcher{EKSFetcherConfig: cfg}, nil + fetcher := &eksFetcher{EKSFetcherConfig: cfg} + + if err := fetcher.setCallerIdentity(context.Background()); err != nil { + cfg.Log.WithError(err).Warn("Failed to set caller identity.") + } + + // If the fetcher SetupAccessForARN isn't set, use the caller identity. + // This is useful to setup access for the caller identity itself + // without having to specify the ARN. + // If the current caller identity doesn't have access to setup access entries, + // the fetcher will log a warning and skip the setup access process. + if fetcher.SetupAccessForARN == "" { + fetcher.SetupAccessForARN = fetcher.callerIdentity + } + + return fetcher, nil } func (a *eksFetcher) getClient(ctx context.Context) (eksiface.EKSAPI, error) { @@ -148,14 +196,10 @@ func (a *eksFetcher) getClient(ctx context.Context) (eksiface.EKSAPI, error) { return a.client, nil } - client, err := a.EKSClientGetter.GetAWSEKSClient( + client, err := a.ClientGetter.GetAWSEKSClient( ctx, a.Region, - cloud.WithAssumeRole( - a.AssumeRole.RoleARN, - a.AssumeRole.ExternalID, - ), - cloud.WithCredentialsMaybeIntegration(a.Integration), + a.getAWSOpts()..., ) if err != nil { return nil, trace.Wrap(err) @@ -319,5 +363,365 @@ func (a *eksFetcher) getMatchingKubeCluster(ctx context.Context, clusterName str return nil, trace.CompareFailed("EKS cluster %q labels does not match the selector: %s", clusterName, reason) } - return cluster, nil + // If no access configuration is required, return the cluster. + if a.SetupAccessForARN == "" || rsp.Cluster.AccessConfig == nil { + return cluster, nil + } + + // If the fetcher should setup access for the specified ARN, first check if the cluster authentication mode + // is set to either [eks.AuthenticationModeApi] or [eks.AuthenticationModeApiAndConfigMap]. + // If the authentication mode is set to [eks.AuthenticationModeConfigMap], the fetcher will ignore the cluster. + switch st := aws.StringValue(rsp.Cluster.AccessConfig.AuthenticationMode); st { + case eks.AuthenticationModeApiAndConfigMap, eks.AuthenticationModeApi: + if err := a.checkOrSetupAccessForARN(ctx, client, rsp.Cluster); err != nil { + return nil, trace.Wrap(err, "unable to setup access for EKS cluster %q", clusterName) + } + return cluster, nil + default: + a.Log.Warnf("EKS cluster %q has unsupported authentication mode %q. Skipping setup access for ARN %q.", + clusterName, st, a.SetupAccessForARN) + return cluster, nil + } +} + +const ( + // teleportKubernetesGroup is the Kubernetes group that exists in the EKS cluster and is used to grant access to the cluster + // for the specified ARN. + teleportKubernetesGroup = "teleport:kube-service:eks" +) + +// checkOrSetupAccessForARN checks if the ARN has access to the cluster and sets up the access if needed. +// The check involves checking if the access entry exists and if the "teleport:kube-agent:eks" is part of the Kubernetes group. +// If the access entry doesn't exist or is misconfigured, the fetcher will temporarily gain admin access and create the role and binding. +// The fetcher will then upsert the access entry with the correct Kubernetes group. +func (a *eksFetcher) checkOrSetupAccessForARN(ctx context.Context, client eksiface.EKSAPI, cluster *eks.Cluster) error { + entry, err := convertAWSError( + client.DescribeAccessEntryWithContext(ctx, + &eks.DescribeAccessEntryInput{ + ClusterName: cluster.Name, + PrincipalArn: aws.String(a.SetupAccessForARN), + }, + ), + ) + + switch { + case trace.IsAccessDenied(err): + // Access denied means that the principal does not have access to setup access entries for the cluster. + a.Log.WithError(err).Warnf("Access denied to setup access for EKS cluster %q. Please ensure you correctly configured the following permissions: %v", + aws.StringValue(cluster.Name), + []string{ + "eks:ListClusters", + "eks:DescribeCluster", + "eks:DescribeAccessEntry", + "eks:CreateAccessEntry", + "eks:DeleteAccessEntry", + "eks:AssociateAccessPolicy", + }) + return nil + case err == nil: + // If the access entry exists and the principal has access to the cluster, check if the teleportKubernetesGroup is part of the Kubernetes group. + if entry.AccessEntry != nil && slices.Contains(aws.StringValueSlice(entry.AccessEntry.KubernetesGroups), teleportKubernetesGroup) { + return nil + } + fallthrough + case trace.IsNotFound(err): + // If the access entry does not exist or the teleportKubernetesGroup is not part of the Kubernetes group, temporarily gain admin access and create the role and binding. + // This temporary access is granted to the identity that the Discovery service fetcher is running as (callerIdentity). If a role is assumed, the callerIdentity is the assumed role. + if err := a.temporarilyGainAdminAccessAndCreateRole(ctx, client, cluster); trace.IsAccessDenied(err) { + // Access denied means that the principal does not have access to setup access entries for the cluster. + a.Log.WithError(err).Warnf("Access denied to setup access for EKS cluster %q. Please ensure you correctly configured the following permissions: %v", + aws.StringValue(cluster.Name), + []string{ + "eks:ListClusters", + "eks:DescribeCluster", + "eks:DescribeAccessEntry", + "eks:CreateAccessEntry", + "eks:DeleteAccessEntry", + "eks:AssociateAccessPolicy", + }) + return nil + } else if err != nil { + return trace.Wrap(err, "unable to setup access for EKS cluster %q", aws.StringValue(cluster.Name)) + } + + // upsert the access entry with the correct Kubernetes group for the final + err = a.upsertAccessEntry(ctx, client, cluster) + if trace.IsAccessDenied(err) { + // Access denied means that the principal does not have access to setup access entries for the cluster. + a.Log.WithError(err).Warnf("Access denied to setup access for EKS cluster %q. Please ensure you correctly configured the following permissions: %v", + aws.StringValue(cluster.Name), + []string{ + "eks:ListClusters", + "eks:DescribeCluster", + "eks:DescribeAccessEntry", + "eks:CreateAccessEntry", + "eks:DeleteAccessEntry", + "eks:AssociateAccessPolicy", + }) + return nil + } + return trace.Wrap(err, "unable to setup access for EKS cluster %q", aws.StringValue(cluster.Name)) + default: + return trace.Wrap(err) + } + +} + +// temporarilyGainAdminAccessAndCreateRole temporarily gains admin access to the EKS cluster by associating the EKS Cluster Admin Policy +// to the callerIdentity. The fetcher will then create the role and binding for the teleportKubernetesGroup in the EKS cluster. +func (a *eksFetcher) temporarilyGainAdminAccessAndCreateRole(ctx context.Context, client eksiface.EKSAPI, cluster *eks.Cluster) error { + const ( + // https://docs.aws.amazon.com/eks/latest/userguide/access-policies.html + // We use cluster admin policy to create namespace and cluster role. + eksClusterAdminPolicy = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy" + ) + // Setup access for the ARN + rsp, err := convertAWSError( + client.CreateAccessEntryWithContext(ctx, + &eks.CreateAccessEntryInput{ + ClusterName: cluster.Name, + PrincipalArn: aws.String(a.callerIdentity), + }, + ), + ) + if err != nil && !trace.IsAlreadyExists(err) { + return trace.Wrap(err) + } + + // rsp is not nil when the access entry was created and needs to be deleted after the role and binding are created. + if rsp != nil { + defer func() { + _, err := convertAWSError( + client.DeleteAccessEntryWithContext( + ctx, + &eks.DeleteAccessEntryInput{ + ClusterName: cluster.Name, + PrincipalArn: aws.String(a.callerIdentity), + }), + ) + if err != nil { + a.Log.WithError(err).Warnf("Failed to delete access entry for EKS cluster %q", aws.StringValue(cluster.Name)) + } + }() + + } + + _, err = convertAWSError( + client.AssociateAccessPolicyWithContext(ctx, &eks.AssociateAccessPolicyInput{ + AccessScope: &eks.AccessScope{ + Namespaces: nil, + Type: aws.String(eks.AccessScopeTypeCluster), + }, + ClusterName: cluster.Name, + PolicyArn: aws.String(eksClusterAdminPolicy), + PrincipalArn: aws.String(a.callerIdentity), + }), + ) + if err != nil && !trace.IsAlreadyExists(err) { + return trace.Wrap(err, "unable to associate EKS Access Policy to cluster %q", aws.StringValue(cluster.Name)) + } + + timeout := a.Clock.NewTimer(60 * time.Second) + defer timeout.Stop() +forLoop: + for { + + // EKS Access Entries are eventually consistent, so we need to wait for the access to be granted. + // AWS API recommends to wait for 5 seconds before checking the access. + err = a.upsertRoleAndBinding(ctx, cluster) + if err == nil || !kubeerrors.IsForbidden(err) && !kubeerrors.IsUnauthorized(err) { + break + } + select { + case <-timeout.Chan(): + break forLoop + case <-a.Clock.After(5 * time.Second): + + } + + } + return trace.Wrap(err, "unable to upsert role and binding for cluster %q", aws.StringValue(cluster.Name)) +} + +// upsertRoleAndBinding upserts the ClusterRole and ClusterRoleBinding for the teleportKubernetesGroup in the EKS cluster. +func (a *eksFetcher) upsertRoleAndBinding(ctx context.Context, cluster *eks.Cluster) error { + client, err := a.createKubeClient(cluster) + if err != nil { + return trace.Wrap(err, "unable to create Kubernetes client for cluster %q", aws.StringValue(cluster.Name)) + } + ctx, cancel := context.WithTimeout(ctx, 20*time.Second) + defer cancel() + if err := a.upsertClusterRoleWithAdminCredentials(ctx, client); err != nil { + return trace.Wrap(err, "unable to upsert ClusterRole for group %q", teleportKubernetesGroup) + } + + if err := a.upsertClusterRoleBindingWithAdminCredentials(ctx, client, teleportKubernetesGroup); err != nil { + return trace.Wrap(err, "unable to upsert ClusterRoleBinding for group %q", teleportKubernetesGroup) + } + + return nil +} + +func (a *eksFetcher) createKubeClient(cluster *eks.Cluster) (*kubernetes.Clientset, error) { + token, _, err := kubeutils.GenAWSEKSToken(a.stsClient, aws.StringValue(cluster.Name), a.Clock) + if err != nil { + return nil, trace.Wrap(err, "unable to generate EKS token for cluster %q", aws.StringValue(cluster.Name)) + } + + ca, err := base64.StdEncoding.DecodeString(aws.StringValue(cluster.CertificateAuthority.Data)) + if err != nil { + return nil, trace.Wrap(err, "unable to decode EKS cluster %q certificate authority", aws.StringValue(cluster.Name)) + } + + apiEndpoint := aws.StringValue(cluster.Endpoint) + if len(apiEndpoint) == 0 { + return nil, trace.BadParameter("invalid api endpoint for cluster %q", aws.StringValue(cluster.Name)) + } + + client, err := kubernetes.NewForConfig( + &rest.Config{ + Host: apiEndpoint, + BearerToken: token, + TLSClientConfig: rest.TLSClientConfig{ + CAData: ca, + }, + }, + ) + return client, trace.Wrap(err, "unable to create Kubernetes client for cluster %q", aws.StringValue(cluster.Name)) +} + +// upsertClusterRoleWithAdminCredentials tries to upsert the ClusterRole using admin credentials. +func (a *eksFetcher) upsertClusterRoleWithAdminCredentials(ctx context.Context, client *kubernetes.Clientset) error { + clusterRole := &rbacv1.ClusterRole{} + + if err := yaml.Unmarshal([]byte(fixtures.KubeClusterRoleTemplate), clusterRole); err != nil { + return trace.Wrap(err) + } + + _, err := client.RbacV1().ClusterRoles().Create(ctx, clusterRole, metav1.CreateOptions{}) + if err == nil { + return nil + } + + if kubeerrors.IsAlreadyExists(err) { + _, err := client.RbacV1().ClusterRoles().Update(ctx, clusterRole, metav1.UpdateOptions{}) + return trace.Wrap(err) + } + + return trace.Wrap(err) +} + +// upsertClusterRoleBindingWithAdminCredentials tries to upsert the ClusterRoleBinding using admin credentials +// and maps it into the principal group. +func (a *eksFetcher) upsertClusterRoleBindingWithAdminCredentials(ctx context.Context, client *kubernetes.Clientset, groupID string) error { + clusterRoleBinding := &rbacv1.ClusterRoleBinding{} + + if err := yaml.Unmarshal([]byte(fixtures.KubeClusterRoleBindingTemplate), clusterRoleBinding); err != nil { + return trace.Wrap(err) + } + + if len(clusterRoleBinding.Subjects) == 0 { + return trace.BadParameter("Subjects field were not correctly unmarshaled") + } + + clusterRoleBinding.Subjects[0].Name = groupID + + _, err := client.RbacV1().ClusterRoleBindings().Create(ctx, clusterRoleBinding, metav1.CreateOptions{}) + if err == nil { + return nil + } + + if kubeerrors.IsAlreadyExists(err) { + _, err := client.RbacV1().ClusterRoleBindings().Update(ctx, clusterRoleBinding, metav1.UpdateOptions{}) + return trace.Wrap(err) + } + + return trace.Wrap(err) +} + +// upsertAccessEntry upserts the access entry for the specified ARN with the teleportKubernetesGroup. +func (a *eksFetcher) upsertAccessEntry(ctx context.Context, client eksiface.EKSAPI, cluster *eks.Cluster) error { + _, err := convertAWSError( + client.CreateAccessEntryWithContext(ctx, + &eks.CreateAccessEntryInput{ + ClusterName: cluster.Name, + PrincipalArn: aws.String(a.SetupAccessForARN), + KubernetesGroups: aws.StringSlice([]string{teleportKubernetesGroup}), + }, + )) + if err == nil || !trace.IsAlreadyExists(err) { + return trace.Wrap(err) + } + + _, err = convertAWSError( + client.UpdateAccessEntryWithContext(ctx, + &eks.UpdateAccessEntryInput{ + ClusterName: cluster.Name, + PrincipalArn: aws.String(a.SetupAccessForARN), + KubernetesGroups: aws.StringSlice([]string{teleportKubernetesGroup}), + }, + )) + + return trace.Wrap(err) +} + +func (a *eksFetcher) setCallerIdentity(ctx context.Context) error { + if a.AssumeRole.RoleARN != "" { + a.callerIdentity = a.AssumeRole.RoleARN + return nil + } + var err error + a.stsClient, err = a.ClientGetter.GetAWSSTSClient( + ctx, + a.Region, + a.getAWSOpts()..., + ) + if err != nil { + return trace.Wrap(err) + } + identity, err := a.stsClient.GetCallerIdentityWithContext(ctx, &sts.GetCallerIdentityInput{}) + if err != nil { + return trace.Wrap(err) + } + a.callerIdentity = convertAssumedRoleToIAMRole(aws.StringValue(identity.Arn)) + return nil +} + +func (a *eksFetcher) getAWSOpts() []cloud.AWSOptionsFn { + return []cloud.AWSOptionsFn{ + cloud.WithAssumeRole( + a.AssumeRole.RoleARN, + a.AssumeRole.ExternalID, + ), + cloud.WithCredentialsMaybeIntegration(a.Integration), + } +} + +func convertAWSError[T any](rsp T, err error) (T, error) { + err = awslib.ConvertRequestFailureError(err) + return rsp, trace.Wrap(err) +} + +// convertAssumedRoleToIAMRole converts the assumed role ARN to an IAM role ARN. +// The assumed role ARN is in the format "arn:aws:sts::account-id:assumed-role/role-name/role-session-name". +// The IAM role ARN is in the format "arn:aws:iam::account-id:role/role-name". +func convertAssumedRoleToIAMRole(callerIdentity string) string { + const ( + assumeRolePrefix = "assumed-role/" + roleResource = "role" + ) + a, err := arn.Parse(callerIdentity) + if err != nil { + return callerIdentity + } + if !strings.HasPrefix(a.Resource, assumeRolePrefix) { + return callerIdentity + } + a.Service = iam.ServiceName + split := strings.Split(a.Resource, "/") + if len(split) <= 2 { + return callerIdentity + } + a.Resource = path.Join(roleResource, split[1]) + return a.String() } diff --git a/lib/srv/discovery/fetchers/eks_test.go b/lib/srv/discovery/fetchers/eks_test.go index faf76ba2ac7..84309922bbd 100644 --- a/lib/srv/discovery/fetchers/eks_test.go +++ b/lib/srv/discovery/fetchers/eks_test.go @@ -27,6 +27,8 @@ import ( "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/eks/eksiface" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" @@ -101,10 +103,10 @@ func TestEKSFetcher(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := EKSFetcherConfig{ - EKSClientGetter: &mockEKSClientGetter{}, - FilterLabels: tt.args.filterLabels, - Region: tt.args.region, - Log: logrus.New(), + ClientGetter: &mockEKSClientGetter{}, + FilterLabels: tt.args.filterLabels, + Region: tt.args.region, + Log: logrus.New(), } fetcher, err := NewEKSFetcher(cfg) require.NoError(t, err) @@ -131,6 +133,21 @@ func (e *mockEKSClientGetter) GetAWSEKSClient(ctx context.Context, region string return newPopulatedEKSMock(), nil } +func (e *mockEKSClientGetter) GetAWSSTSClient(ctx context.Context, region string, opts ...cloud.AWSOptionsFn) (stsiface.STSAPI, error) { + return &mockSTSAPI{}, nil +} + +type mockSTSAPI struct { + stsiface.STSAPI + arn string +} + +func (a *mockSTSAPI) GetCallerIdentityWithContext(aws.Context, *sts.GetCallerIdentityInput, ...request.Option) (*sts.GetCallerIdentityOutput, error) { + return &sts.GetCallerIdentityOutput{ + Arn: aws.String(a.arn), + }, nil +} + type mockEKSAPI struct { eksiface.EKSAPI clusters []*eks.Cluster diff --git a/lib/srv/discovery/kube_integration_watcher_test.go b/lib/srv/discovery/kube_integration_watcher_test.go index 341bab56293..5090373691c 100644 --- a/lib/srv/discovery/kube_integration_watcher_test.go +++ b/lib/srv/discovery/kube_integration_watcher_test.go @@ -126,22 +126,22 @@ func TestGetAgentVersion(t *testing.T) { func TestServer_getKubeFetchers(t *testing.T) { eks1, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - EKSClientGetter: &cloud.TestCloudClients{}, - FilterLabels: types.Labels{"l1": []string{"v1"}}, - Region: "region1", + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + FilterLabels: types.Labels{"l1": []string{"v1"}}, + Region: "region1", }) require.NoError(t, err) eks2, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - EKSClientGetter: &cloud.TestCloudClients{}, - FilterLabels: types.Labels{"l1": []string{"v1"}}, - Region: "region1", - Integration: "aws1"}) + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + FilterLabels: types.Labels{"l1": []string{"v1"}}, + Region: "region1", + Integration: "aws1"}) require.NoError(t, err) eks3, err := fetchers.NewEKSFetcher(fetchers.EKSFetcherConfig{ - EKSClientGetter: &cloud.TestCloudClients{}, - FilterLabels: types.Labels{"l1": []string{"v1"}}, - Region: "region1", - Integration: "aws1"}) + ClientGetter: &cloud.TestCloudClients{STS: &mocks.STSMock{}}, + FilterLabels: types.Labels{"l1": []string{"v1"}}, + Region: "region1", + Integration: "aws1"}) require.NoError(t, err) aks1, err := fetchers.NewAKSFetcher(fetchers.AKSFetcherConfig{