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 <tiago.silva@goteleport.com>

* handle review comments

* do not exit on failed GetCallerIdentity call

---------

Signed-off-by: Tiago Silva <tiago.silva@goteleport.com>
This commit is contained in:
Tiago Silva 2024-05-28 13:13:47 +01:00 committed by GitHub
parent e49af3a22b
commit 06c735490b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 819 additions and 281 deletions

View file

@ -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

View file

@ -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.

View file

@ -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}}
}

View file

@ -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:])

View file

@ -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 <<EOF | kubectl apply -f -
@ -766,5 +723,5 @@ func kubectlApplyString(group string) string {
%s
---
%s
EOF`, clusterRoleTemplate, strings.ReplaceAll(clusterRoleBindingTemplate, "group_name", group))
EOF`, fixtures.KubeClusterRoleTemplate, strings.ReplaceAll(fixtures.KubeClusterRoleBindingTemplate, "group_name", group))
}

View file

@ -1611,14 +1611,15 @@ func applyDiscoveryConfig(fc *FileConfig, cfg *servicecfg.Config) error {
}
serviceMatcher := types.AWSMatcher{
Types: matcher.Types,
Regions: matcher.Regions,
AssumeRole: assumeRole,
Tags: matcher.Tags,
Params: installParams,
SSM: &types.AWSSSM{DocumentName: matcher.SSM.DocumentName},
Integration: matcher.Integration,
KubeAppDiscovery: matcher.KubeAppDiscovery,
Types: matcher.Types,
Regions: matcher.Regions,
AssumeRole: assumeRole,
Tags: matcher.Tags,
Params: installParams,
SSM: &types.AWSSSM{DocumentName: matcher.SSM.DocumentName},
Integration: matcher.Integration,
KubeAppDiscovery: matcher.KubeAppDiscovery,
SetupAccessForARN: matcher.SetupAccessForARN,
}
if err := serviceMatcher.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)

View file

@ -1698,6 +1698,8 @@ type AWSMatcher struct {
// 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 `yaml:"kube_app_discovery"`
// SetupAccessForARN is the role that the discovery service should create EKS Access Entries for.
SetupAccessForARN string `yaml:"setup_access_for_arn"`
}
// InstallParams sets join method to use on discovered nodes

66
lib/fixtures/kube.go Normal file
View file

@ -0,0 +1,66 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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`

View file

@ -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) {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View file

@ -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,

View file

@ -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()
}

View file

@ -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

View file

@ -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{